すべての形式のページ ↓
グラフィックとメディア

TAF / TSF — TA: Kingdomsのトゥルーカラーアニメーション

出典:coreprime/kbot @ 0ab97bf · GitHubで編集

TAFは、Total Annihilation: Kingdomsのトゥルーカラーのスプライトアニメーションを格納する形式です。呪文のエフェクト、爆発、火の玉、魔法の輪、水のスプライト、アニメーションするメニューの背景などに使われます。Total AnnihilationのGAFコンテナの配置を流用していますが、8ビットのパレットインデックスのピクセルを16ビットのARGBに置き換えているため、TAFは自分自身の色と本物のアルファチャンネルを持ち、外部のパレットを必要としません。TSFは同じアニメーションを人が読めるテキストで表したもので、GUIのローダーが直接読む、波かっこで区切られたスクリプトです。

fireballa_1555.tafのFireballAシーケンス    アルファで縁が柔らかくなったBlueFireの1フレーム

Tip

自分で試す

kbot taf list   anims/manabomb_1555.taf                                 # sequence + frame count
kbot taf export anims/fireballa_1555.taf --format gif -o fireball.gif    # animated preview
kbot taf sheet  anims/manabomb_1555.taf --cols 5 -o manabomb-sheet.png   # contact sheet
kbot taf decompile anims/manabomb_1555.taf --target ./manabomb           # → TSF + PNG layers

すべてのフラグについては、CLIのkbot taf / kbot tsfリファレンスを参照してください。

Goから使う場合はformats/tsfを使います。

import "github.com/coreprime/kbot/formats/tsf"

data, _ := os.ReadFile("anims/manabomb_1555.taf")
taf, _ := tsf.ParseTAF(data)
fmt.Println(taf.Name, len(taf.Frames))
img, _ := taf.FrameImage(0)   // *image.NRGBA, full alpha preserved

概要

┌─ Header (12B) ─┐
│ ver  1  pad    │   ver = 0x00010100, exactly one sequence
└────────┬───────┘
         ▼
┌─ Entry pointer ─┐  1 × uint32 absolute offset → SequenceHeader
└───┬─────────────┘
    ▼
┌─ SequenceHeader (40B) ─┐
│ frameCount, name[32]   │
└─────────┬──────────────┘
          ▼
┌─ FrameListItem (8B) × frameCount ─┐  ptr + tick duration
└───┬───────────────────────────────┘
    ▼
┌─ FrameInfo (24B) ─┐
│ size, origin,     │
│ format(4/5),      │   ← the GAF "compressed" byte, repurposed
│ layers=0, flag,   │
│ ptrPixelData      │
└─────┬─────────────┘
      ▼
┌─ Pixel data: Width×Height × uint16 ─┐  raw ARGB4444 or ARGB1555
└──────────────────────────────────────┘

TAFは1つのアニメーションシーケンスで、フレームの配列です。各フレームは、リトルエンディアンの16ビットのトゥルーカラーピクセルからなる幅×高さの長方形で、原点とティック単位の表示時間を持ちます。バイナリのレベルでは圧縮もレイヤーもなく、どのフレームも平坦な非圧縮のビットマップです。

Note

TAFは、ピクセルのペイロードが異なるGAFコンテナです。GAFを読んだことがあれば、ヘッダー、エントリテーブル、シーケンスヘッダー、フレームリストはバイト単位でおなじみのはずです。変わるのはFrameInfoの解釈とピクセルの符号化だけです。2つは.taf拡張子のもとで共存しています。TA: Kingdomsはパレット付きの.tafファイル(GAFとして読みます)と、トゥルーカラーの.tafファイル(このページ)の両方を出荷しています。kbotは各フレームのフォーマットバイトで両者を見分けます。2つの見分け方を参照してください。


ヘッダー(12バイト)

typedef struct {
    uint32 Version;        // 0x00010100 — identical to the GAF version word
    uint32 SequenceCount;  // Always 1 for a truecolor TAF
    uint32 Unknown1;       // Always 0
} TAFHeader;
フィールド 説明
Version 0x00010100。TAのGAFが持つのと同じワードです。オフセットを信用する前に検証してください。
SequenceCount 常に1です。直後に、SequenceHeaderを指すuint32の絶対オフセットが1つ続きます。複数のシーケンスを持つファイルは、トゥルーカラーのTAFではありません。
Unknown1 常に0です。書き込み時も0を出力します。

シーケンス(40バイト)

typedef struct {
    uint16 FrameCount;
    uint16 Unknown1;       // Always 1
    uint32 Unknown2;       // Always 0
    char   Name[32];       // NUL-padded, case-preserved
} TAFSequence;

この後にFrameCount × FrameListItemが続きます。

typedef struct {
    uint32 PtrFrameInfo;   // → FrameInfo
    uint32 Duration;       // Game ticks (30 ticks = 1 second)
} FrameListItem;

Warning

32バイトの名前フィールドには、終端の後にゴミが入っていることがあります。製品版のTAFのいくつかは、NULの後に初期化されていないバッファーのバイトを残しています。kbotは32バイトをそのまま保持するので、解析 → シリアライズのラウンドトリップはバイト単位で同一になりますが、本当の名前は最初のNULまでの文字列だけです。


フレーム(24バイトの情報とペイロード)

typedef struct {
    uint16 Width;
    uint16 Height;
    int16  OriginX;        // Render anchor, may be negative
    int16  OriginY;
    uint8  Transparency;   // 0x08: always 0 (alpha lives in the pixels)
    uint8  Format;         // 0x09: 4 = ARGB4444, 5 = ARGB1555
    uint8  LayerCount;     // 0x0A: always 0 (no sub-frames)
    uint8  Flag;           // 0x0B: 0x00 or 0xFF, meaning unconfirmed
    uint32 Unknown2;       // 0x0C: always 0
    uint32 PtrPixelData;   // 0x10: → raw pixel bytes
    uint32 Unknown3;       // 0x14: always 0
} FrameInfo;

ここでTAFはGAFから分かれます。GAFのFrameInfoでは、Compressedフラグがオフセット0x09にあり、オフセット0x0A〜0x0Bには16ビットのLayerCountフィールドがあります。トゥルーカラーのTAFは、同じバイトを次の意味に使い回しています。

オフセット GAFでの意味 TAFでの意味
0x08 TransparencyIndex Transparency。未使用で、常に0
0x09 Compressed(0/1) Format。4(ARGB4444)または5(ARGB1555)
0x0A LayerCountの下位バイト LayerCount。常に0
0x0B LayerCountの上位バイト Flag。0x00または0xFFで、用途は不明

OriginX / OriginYはフレームを転送(ブリット)するときに使うアンカーで、ふつうはエフェクトの中心です。そのため、50×48の爆発の原点は25,24になります。製品版のアセット全体を通じて意味が確認できていないものの、kbotはバイト単位で正確なラウンドトリップのためにFlagバイトをそのまま保持します。

ピクセルデータ

PtrPixelDataは、ちょうどWidth × Height個のリトルエンディアンのuint16値を指します。1ピクセルあたり2バイトで、上から下、左から右の順に並び、圧縮も行ヘッダーもありません。16ビットは、フレームのFormatに従って復号します。

ARGB4444(Format == 4)

 bit 15 ──────────────────────────── bit 0
┌─────┬─────┬─────┬─────┐
│  A  │  R  │  G  │  B  │   4 bits each
└─────┴─────┴─────┴─────┘
  15-12  11-8   7-4   3-0

4ビットの各チャンネルはビットの複製(v<<4 | v)で8ビットに広げられるので、0xF → 0xFF、0x0 → 0x00となります。この展開は厳密に逆変換できるため、kbotはピクセルを損失なくラウンドトリップできます。

ARGB1555(Format == 5)

 bit 15 ──────────────────────────── bit 0
┌─┬───────┬───────┬───────┐
│A│   R   │   G   │   B   │   1 + 5 + 5 + 5
└─┴───────┴───────┴───────┘
 15  14-10   9-5    4-0

1ビットのアルファは全か無かです(0 → 完全に透明、1 → 完全に不透明)。5ビットの各色チャンネルは、複製によって広げられます(v<<3 | v>>2)。1ビットの切り抜きで足りる縁のくっきりしたスプライトにはARGB1555を、16段階の柔らかなアルファ(煙、光、フェード)が必要なときはARGB4444を使います。

Important

TAFにはパレットが要りません。インデックスしか格納せず、palettes/palette.palやTA:KのサイドカーPCXに頼るGAFと違って、TAFのピクセルはどれも、すでに自分自身の色とアルファを持っています。描画は16ビットから32ビットへの直接の展開で、ルックアップテーブルは関わりません。


パレット付きとトゥルーカラーのTAFの見分け方

どちらの.tafもGAFのヘッダーとバージョンワードを共有しているので、先頭の12バイトからは区別できません。見分ける手がかりは、FrameInfoのオフセット0x09にあるフレームのFormat/Compressedバイトです。

0x09のバイト ファイルの種類 読み方
0または1 パレット付きのGAF風アニメーション(0/1の値は非圧縮/圧縮を意味します) GAFのローダーとパレット
4または5 トゥルーカラーのアニメーション(ARGB4444 / ARGB1555) このページ(formats/tsf)

kbot tafはこのバイトを検証し、パレット付きのファイルはわかりやすいエラーで拒否します。その場合はkbot gafを使ってください。TA:KのトゥルーカラーのTAFのほとんどは、_4444または_1555という接尾辞(manabomb_1555.taf、bluefire_4444.taf)を付けるファイル名の慣習にも従っていますが、決め手になるのは名前ではなくフォーマットバイトです。


TSF — テキスト形式

TSF(TAF Source Format)は、同じアニメーションを波かっこで区切ったINI風のスクリプトで表したものです。TA: KingdomsのGUIローダーは、メニューの背景用にTSFを直接読みます。また、TAFを逆コンパイルしたときにkbotが出力する交換形式でもあります。アニメーション1つにつき1セクション、フレーム1つにつき入れ子のFrameが1つ、そしてフレームごとに外部の画像ファイルを参照するLayerが1つです。

/* anims/titlescreen.tsf — abridged */
[JPGTest]
{
    Looping = 0;
    [Frame0]
    {
        Delay = 0;
        [Layer0]
        {
            AnchorX = 0;
            AnchorY = 0;
            Filename = TitleScreen.jpg;
        }
    }
    [Frame1] { /* … */ }
}
トークン 対応先 説明
[Name] { … } アニメーションのシーケンス 1文書に1つ。NameがTAFのシーケンス名になります。
Looping (参考情報) 制作時のヒント。バイナリのTAFにはループのフィールドがありません。
[FrameN] Frame Delayは、ゲームのティック単位の表示時間です。
Format Frame.Format ARGB4444(省略時の既定)またはARGB1555。
[LayerN] フレームの画像 Filenameは外部のPNG/JPGを参照し、AnchorX/Yがフレームの原点になります。

コンパイルされたTAFでは、各フレームの1つのレイヤーが1枚のトゥルーカラーのビットマップに平坦化されます。kbotはフレームあたりちょうど1つのレイヤーを想定しており(複数レイヤーの合成はGUI制作上の便宜で、コンパイラーがビルド時に解決します)、きれいにコンパイルできないものはkbot tsf lintが指摘します。

Tip

画像に触れずにTSFを調べる

kbot tsf info anims/titlescreen.tsf   # animation, frames, layer filenames
kbot tsf lint anims/titlescreen.tsf   # shape check; non-zero exit on errors

lintは構造だけを検証します。画像の読み込み処理全体を試すには、kbot taf compileを使ってください。


実例 — manabomb_1555.taf

manabombの10フレームすべてをグリッドに並べたもの

$ kbot taf info anims/manabomb_1555.taf
TAF: anims/manabomb_1555.taf
Sequence:  "manabomb"
Frames:    10
Duration:  20 ticks (0.67s)

#  Size   Origin  Format    Duration  Flag
─  ────   ──────  ──────    ────────  ────
0  50x48  25,24   ARGB1555  2         0x00
1  50x48  25,24   ARGB1555  2         0x00
...
9  50x48  25,24   ARGB1555  2         0x00

50×48のフレームが10枚あり、どれも中心(25,24)をアンカーとして2ティックずつ表示されます。⅔秒の爆発の星です。ファイル全体は48,376バイトで、フレームあたり50 × 48 × 2 = 4,800バイトのピクセルが10枚分、それにヘッダー、シーケンスヘッダー、フレームリスト、24バイトの情報レコード10個が加わります。


CLIでの作業

kbot tafのサブコマンド群は、調査、描画、変換、TAF↔TSFのコンパイルの往復を扱い、kbot tsfはテキスト形式を扱います。どのコマンドも、kbot gafやkbot studioとまったく同じように、有効なkbotコンテキストか明示的な--vfsのマウントを通して入力を解決します。そのため、manabomb_1555.tafのようにファイル名だけを指定しても、マウントされたすべてのアーカイブから探されます。

調査と描画

kbot taf info   anims/bluefire_4444.taf            # per-frame table
kbot taf list   anims/bluefire_4444.taf            # one-line summary
kbot taf render anims/bluefire_4444.taf --frame 4 -o frame4.png
kbot taf sheet  anims/bluefire_4444.taf --cols 6 --bg "#202830" -o sheet.png
kbot taf export anims/bluefire_4444.taf --format apng -o bluefire.png   # or --format gif

exportの既定はAPNGで、アルファチャンネルを完全に保ちます。--format gifは、プレビューやチャットクライアント向けに、255色のテーブルと1つの透過スロット(1ビットの切り抜き)に減色します。

逆コンパイル ↔ コンパイルのラウンドトリップ

kbot taf decompile anims/manabomb_1555.taf --target ./manabomb
#   → manabomb/manabomb.tsf  +  manabomb_0.png … manabomb_9.png

kbot taf compile ./manabomb/manabomb.tsf --images ./manabomb -o rebuilt.taf
kbot taf diff anims/manabomb_1555.taf rebuilt.taf      # exits 0 — identical

decompileはTSFと、フレームごとに1枚のPNGを書き出します。compileはそれらを読み戻してバイナリのTAFにします。kbotが解析したどのTAFでも、ラウンドトリップはバイト単位で同一です。kbot taf roundtripとkbot taf diffのどちらでも確かめられます。

一般的な形式からの取り込み

kbot taf from-gif   boom.gif   --format argb4444 --name Boom        -o boom.taf
kbot taf from-sheet strip.png  --frame-width 64 --frame-height 64 \
                               --count 8 --delay 3 --format argb1555 -o strip.taf

from-gifはアニメーションGIFを(その破棄モードとフレームごとの遅延を守りながら)平坦化して、トゥルーカラーのTAFにします。from-sheetは、固定グリッドのPNG/JPGのスプライトストリップをフレームに切り分けます。どちらも出力するピクセル形式を選べます。

検証

kbot taf lint anims/manabomb_1555.taf   # structural findings; non-zero exit on errors

MCPツール

同じ機能はMCPサーバーからも公開されているので、エージェントはアニメーションを直接調べ、表示することができます。

ツール 用途
taf_info フレームごとの完全なJSON。名前、サイズ、原点、形式、表示時間。
taf_list 1行のJSONの要約。
taf_render 1フレームを描画し、PNGとしてインラインで返す(保存も可能)。
taf_sheet すべてのフレームをコンタクトシートに描画し、PNGとしてインラインで返す。
taf_export アニメーションGIFまたはAPNGをディスクに書き出す。
taf_lint 構造の診断をJSONで返す。
tsf_info TSF文書を要約する(アニメーション、フレーム、レイヤーのファイル名)。
tsf_lint TSF文書の形をチェックする。

taf_renderとtaf_sheetは画像のコンテンツブロックを返すので、モデルはファイルを別途読み込むやり取りなしに、呪文のエフェクトをプレビューできます。パスは、ほかのすべてのkbot MCPツールと同じように、設定されたgame-dataフォルダーを基準に解決されます。


注意点

Warning

.tafという拡張子には2つの意味があります。.tafはパレット付きのGAFのこともあれば、トゥルーカラーのアニメーションのこともあります。拡張子やファイル名ではなく、必ずフレームのフォーマットバイト(0x09)を確認してください。2つの見分け方を参照してください。

  • シーケンスはちょうど1つ。トゥルーカラーのTAFは常に1つのアニメーションを持ちます。SequenceCountが1以外なら、それは別の(おそらくパレット付きの)ファイルです。
  • 圧縮は一切なし。どのフレームも、平坦なWidth×Heightの16ビットビットマップです。GAFのようなRLEの経路はありません。大きな炎のエフェクト(composite02flame)が約600 KBにもなるのは、まさに何も詰められていないからです。
  • 名前フィールドにはゴミが入りうる。製品版のいくつかのファイルでは、終端のNULの後のバイトが初期化されていません。正確なラウンドトリップのためには32バイトすべてを保持し、読むのはNULまでにしてください。
  • 0x0Bのフラグバイトは未解明。0x00か0xFFが観測されています。kbotはこれをそのまま保持します。正規化するとラウンドトリップが一致しなくなるので、しないでください。
  • GIFでの書き出しは非可逆。GIFは1ビットの切り抜きと255色しか持てないので、ARGB4444の柔らかなアルファはくっきりした縁になります。本物のアルファチャンネルを保つには、APNGを使ってください。
  • Delay / Durationが0でも正当。GAFと同じく、0ティックのフレームはイベント駆動です(メニューのJPGTestの例では、静的な背景にDelay = 0を使っています)。既定値に「修正」しないでください。

典型的なサイズ

アセット TA:KのTAFで観測された範囲
小さなエフェクト(bluefire_4444、12 × 32程度のフレーム) 約30〜60 KB
爆発(manabomb_1555、10 × 50×48) 約48 KB
火の玉や呪文(fireballa_1555) 約50〜120 KB
大きな合成の炎(composite02flame) 約600 KB(非圧縮のトゥルーカラーはかさみます)
フレームごとのピクセルデータ ちょうどWidth × Height × 2バイト

関連項目

  • GAF — TAFの土台となるパレット付きのコンテナ。先に読んでください。パレット付きの.tafファイルにはこちらを使います。
  • PCX — パレット付きのTA:Kのアセットが色を得る方法(トゥルーカラーのTAFには不要です)。
  • TA:KのGUI — アニメーションする背景のために、ローダーがTSFを直接読み込むメニューシステム。
  • TAとTA:Kの形式の比較 — 2つのゲームが共有するものと、違うところ。
  • 用語集 — 原点、ティック、サブフレーム。