TAF / TSF – Truecolor-Animationen von TA: Kingdoms
TAF speichert Sprite-Animationen in Truecolor für Total Annihilation: Kingdoms: Zaubereffekte, Explosionen, Feuerbälle, magische Ringe, Wasser-Sprites und die animierten Menühintergründe. Es verwendet den Containeraufbau von GAF aus Total Annihilation wieder, tauscht aber die palettenindizierten 8-Bit-Pixel gegen 16-Bit-ARGB; ein TAF bringt also seine eigenen Farben und einen echten Alphakanal mit – eine externe Palette ist nicht nötig. TSF ist die für Menschen lesbare Textform derselben Animation: ein durch geschweifte Klammern gegliedertes Skript, das der Oberflächenlader direkt liest.
Tip
Probier es selbst.
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
Alle Flags stehen in der
kbot taf-/kbot tsf-Referenz
der CLI.
Aus Go heraus. Verwende 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
Auf einen Blick
┌─ 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
└──────────────────────────────────────┘
Ein TAF ist eine einzige Animationssequenz – ein Array aus Frames, jeder ein Rechteck aus Breite × Höhe Truecolor-Pixeln zu 16 Bit in Little-Endian, mit einem Ursprungspunkt und einer Dauer in Ticks. Auf Binärebene gibt es weder Kompression noch Ebenen: Jeder Frame ist eine flache, unkomprimierte Bitmap.
Note
TAF ist der GAF-Container mit anderen Pixel-Nutzdaten. Wenn du
GAF gelesen hast, kommen dir Header, Eintragstabelle,
Sequenz-Header und Frame-Liste Byte für Byte bekannt vor. Nur die
Auslegung von FrameInfo und die Pixelkodierung ändern sich. Beide
existieren unter der Endung .taf nebeneinander: TA: Kingdoms liefert
.taf-Dateien mit Palette aus (lies sie als GAF) und
.taf-Dateien in Truecolor (diese Seite). kbot unterscheidet sie am
Format-Byte jedes Frames – siehe Die beiden
unterscheiden.
Header (12 Byte)
typedef struct {
uint32 Version; // 0x00010100 — identical to the GAF version word
uint32 SequenceCount; // Always 1 for a truecolor TAF
uint32 Unknown1; // Always 0
} TAFHeader;
| Feld | Anmerkungen |
|---|---|
Version |
0x00010100, dasselbe Wort wie in einem GAF von TA. Prüfe es, bevor du irgendeinem Offset vertraust. |
SequenceCount |
Immer 1. Unmittelbar danach folgt ein absoluter uint32-Offset, der auf den SequenceHeader zeigt. Eine Datei mit mehreren Sequenzen ist kein Truecolor-TAF. |
Unknown1 |
Immer null; beim Schreiben null ausgeben. |
Sequenz (40 Byte)
typedef struct {
uint16 FrameCount;
uint16 Unknown1; // Always 1
uint32 Unknown2; // Always 0
char Name[32]; // NUL-padded, case-preserved
} TAFSequence;
Darauf folgen FrameCount × FrameListItem:
typedef struct {
uint32 PtrFrameInfo; // → FrameInfo
uint32 Duration; // Game ticks (30 ticks = 1 second)
} FrameListItem;
Warning
Das 32-Byte-Namensfeld kann hinter dem Terminator Datenmüll enthalten. Mehrere TAFs der Verkaufsversion lassen nach dem NUL nicht initialisierte Pufferbytes stehen – kbot bewahrt die genauen 32 Byte, damit ein Round-Trip aus Parsen → Serialisieren byte-identisch ist, aber nur der Text bis zum ersten NUL ist der echte Name.
Frame (24-Byte-Info + Nutzdaten)
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;
Hier weicht TAF von GAF ab. In einer FrameInfo von GAF ist Offset 0x09
das Flag Compressed, und die Offsets 0x0A–0x0B sind ein
16-Bit-LayerCount. Ein Truecolor-TAF verwendet dieselben Bytes mit
folgender Bedeutung:
| Offset | Bedeutung in GAF | Bedeutung in TAF |
|---|---|---|
0x08 |
TransparencyIndex |
Transparency – unbenutzt, immer 0 |
0x09 |
Compressed (0/1) |
Format – 4 (ARGB4444) oder 5 (ARGB1555) |
0x0A |
unteres Byte von LayerCount |
LayerCount – immer 0 |
0x0B |
oberes Byte von LayerCount |
Flag – 0x00 oder 0xFF, Zweck unbekannt |
OriginX / OriginY sind der Ankerpunkt, an dem der Frame geblittet
wird – meist die Mitte des Effekts, eine Explosion von 50×48 hat also den
Ursprung 25,24. kbot übernimmt das Flag-Byte unverändert, damit
Round-Trips byte-genau bleiben, auch wenn seine Bedeutung über den
gesamten Asset-Bestand der Verkaufsversion hinweg nicht bestätigt ist.
Pixeldaten
PtrPixelData zeigt auf genau Width × Height uint16-Werte in
Little-Endian – 2 Byte pro Pixel, von oben nach unten, von links nach
rechts, ohne Kompression und ohne Zeilen-Header. Die 16 Bit werden je nach
Format des Frames dekodiert:
ARGB4444 (Format == 4)
bit 15 ──────────────────────────── bit 0
┌─────┬─────┬─────┬─────┐
│ A │ R │ G │ B │ 4 bits each
└─────┴─────┴─────┴─────┘
15-12 11-8 7-4 3-0
Jeder 4-Bit-Kanal wird durch Bit-Replikation (v<<4 | v) auf 8 Bit
erweitert, also 0xF → 0xFF und 0x0 → 0x00. Die Erweiterung ist
exakt umkehrbar, und genau das erlaubt kbot einen verlustfreien Round-Trip
der Pixel.
ARGB1555 (Format == 5)
bit 15 ──────────────────────────── bit 0
┌─┬───────┬───────┬───────┐
│A│ R │ G │ B │ 1 + 5 + 5 + 5
└─┴───────┴───────┴───────┘
15 14-10 9-5 4-0
Das einzelne Alpha-Bit kennt nur alles oder nichts (0 → vollständig
transparent, 1 → vollständig deckend); jeder 5-Bit-Farbkanal wird durch
Replikation erweitert (v<<3 | v>>2). Nimm ARGB1555 für hart umrandete
Sprites, die nur eine 1-Bit-Maske brauchen, und ARGB4444, wenn du 16 Stufen
weiches Alpha brauchst (Rauch, Leuchten, Überblendungen).
Important
TAF braucht keine Palette. Anders als GAF – das nur Indizes speichert
und auf palettes/palette.pal oder eine PCX-Begleitdatei von TA:K
angewiesen ist – enthält jedes TAF-Pixel bereits seine eigene Farbe und
sein Alpha. Das Rendern ist eine direkte Erweiterung von 16 auf 32 Bit,
ganz ohne Nachschlagetabelle.
TAF mit Palette und TAF in Truecolor unterscheiden
Beide Arten von .taf teilen sich den GAF-Header und das Versionswort,
anhand der ersten 12 Byte kannst du sie also nicht unterscheiden. Das
Unterscheidungsmerkmal ist das Byte Format/Compressed des Frames bei
Offset 0x09 der FrameInfo:
| Byte bei 0x09 | Die Datei ist | Lesen mit |
|---|---|---|
0 oder 1 |
Animation im GAF-Stil mit Palette (ein Wert von 0/1 bedeutet unkomprimiert/komprimiert) | GAF-Lader + eine Palette |
4 oder 5 |
Truecolor-Animation (ARGB4444 / ARGB1555) | diese Seite (formats/tsf) |
kbot taf prüft das Byte und weist eine Datei mit Palette mit einer klaren
Fehlermeldung ab; nimm in diesem Fall kbot gaf. Die meisten
Truecolor-TAFs von TA:K folgen außerdem einer Namenskonvention – einem
Suffix _4444 oder _1555 (manabomb_1555.taf, bluefire_4444.taf) –,
maßgeblich ist aber das Format-Byte, nicht der Name.
TSF – die Textform
TSF (TAF Source Format) ist dieselbe Animation, ausgedrückt als
INI-ähnliches Skript, das durch geschweifte Klammern gegliedert ist. Der
Oberflächenlader von TA: Kingdoms liest TSF für Menühintergründe
direkt, und es ist das Austauschformat, das kbot ausgibt, wenn du ein
TAF dekompilierst: ein Abschnitt pro Animation, ein verschachteltes Frame
pro Frame und ein Layer pro Frame, das auf eine externe Bilddatei
verweist.
/* anims/titlescreen.tsf — abridged */
[JPGTest]
{
Looping = 0;
[Frame0]
{
Delay = 0;
[Layer0]
{
AnchorX = 0;
AnchorY = 0;
Filename = TitleScreen.jpg;
}
}
[Frame1] { /* … */ }
}
| Token | Entspricht | Anmerkungen |
|---|---|---|
[Name] { … } |
der Animationssequenz | Eine pro Dokument; Name wird zum Sequenznamen im TAF. |
Looping |
(nur Hinweis) | Hinweis für das Authoring; das binäre TAF hat kein Feld für Schleifen. |
[FrameN] |
einem Frame |
Delay ist die Dauer in Spiel-Ticks. |
Format |
Frame.Format |
ARGB4444 (Standard, wenn weggelassen) oder ARGB1555. |
[LayerN] |
dem Bild des Frames | Filename verweist auf ein externes PNG/JPG; AnchorX/Y werden zum Ursprung des Frames. |
Ein kompiliertes TAF reduziert die einzige Ebene jedes Frames auf eine
Truecolor-Bitmap. kbot erwartet genau eine Ebene pro Frame (das
Zusammensetzen mehrerer Ebenen ist eine Annehmlichkeit beim Erstellen von
Oberflächen, die der Compiler beim Bauen auflöst) – kbot tsf lint
markiert alles, was sich nicht sauber kompilieren lässt.
Tip
Ein TSF untersuchen, ohne die Bilder anzufassen.
kbot tsf info anims/titlescreen.tsf # animation, frames, layer filenames
kbot tsf lint anims/titlescreen.tsf # shape check; non-zero exit on errors
lint prüft nur die Struktur – mit kbot taf compile durchläufst du die
vollständige Pipeline zum Laden der Bilder.
Beispiel im Detail – manabomb_1555.taf
$ 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
Zehn Frames zu 50×48, jeder in seiner Mitte verankert (25,24) und 2 Ticks
lang angezeigt – ein Explosionsstern von ⅔ Sekunde. Die ganze Datei ist
48.376 Byte groß: 50 × 48 × 2 = 4,800 Pixelbytes pro Frame × 10, dazu
Header, Sequenz-Header, Frame-Liste und zehn Info-Datensätze zu 24 Byte.
Abläufe in der CLI
Der Befehlsbaum kbot taf deckt Untersuchen, Rendern, Konvertieren und den
Kompilierzyklus TAF↔TSF ab; kbot tsf deckt die Textform ab. Jeder Befehl
löst seine Eingabe über den aktiven
kbot-Kontext oder
ein ausdrückliches --vfs-Einhängen auf, genau wie kbot gaf und
kbot studio; bloße Dateinamen wie manabomb_1555.taf werden also in
jedem eingehängten Archiv gesucht.
Untersuchen und rendern
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 erzeugt standardmäßig APNG, das den vollen Alphakanal behält;
--format gif quantisiert für Vorschauen und Chat-Clients auf eine Tabelle
mit 255 Farben plus einen transparenten Platz (eine 1-Bit-Maske).
Round-Trip Dekompilieren ↔ Kompilieren
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 schreibt ein TSF plus ein PNG pro Frame; compile liest sie
wieder in ein binäres TAF ein. Für jedes TAF, das kbot geparst hat, ist der
Round-Trip byte-identisch – kbot taf roundtrip und kbot taf diff prüfen
das beide.
Aus gängigen Formaten importieren
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 reduziert ein animiertes GIF (unter Beachtung seiner
Disposal-Modi und der Verzögerungen pro Frame) zu einem Truecolor-TAF;
from-sheet zerlegt einen Sprite-Streifen als PNG/JPG mit festem Raster in
Frames. Bei beiden kannst du das Pixelformat des Ziels wählen.
Prüfen
kbot taf lint anims/manabomb_1555.taf # structural findings; non-zero exit on errors
MCP-Werkzeuge
Dieselben Funktionen stellt der MCP-Server bereit, damit ein Agent Animationen direkt untersuchen und zeigen kann:
| Werkzeug | Zweck |
|---|---|
taf_info |
Vollständiges JSON pro Frame: Name, Größen, Ursprünge, Formate, Dauern. |
taf_list |
Einzeilige Zusammenfassung als JSON. |
taf_render |
Einen Frame rendern und direkt als PNG zurückgeben (optional auch speichern). |
taf_sheet |
Alle Frames auf einen Kontaktbogen rendern, direkt als PNG zurückgegeben. |
taf_export |
Ein animiertes GIF oder APNG auf die Platte schreiben. |
taf_lint |
Strukturdiagnosen als JSON. |
tsf_info |
Ein TSF-Dokument zusammenfassen (Animation, Frames, Dateinamen der Ebenen). |
tsf_lint |
Die Form eines TSF-Dokuments prüfen. |
taf_render und taf_sheet liefern einen Bild-Inhaltsblock zurück, ein
Modell kann einen Zaubereffekt also ohne zusätzlichen Round-Trip zum Lesen
einer Datei ansehen. Pfade werden im konfigurierten Ordner game-data
aufgelöst, genauso wie bei jedem anderen MCP-Werkzeug von kbot.
Stolperfallen
Warning
.taf ist mehrdeutig. Eine .taf kann ein GAF mit Palette oder
eine Truecolor-Animation sein. Prüfe immer das Format-Byte des Frames
(0x09), nicht die Endung oder den Dateinamen – siehe Die beiden unterscheiden.
- Genau eine Sequenz. Ein Truecolor-TAF enthält immer eine einzige
Animation; ein
SequenceCountungleich1heißt, dass du eine andere (wahrscheinlich palettierte) Datei vor dir hast. - Niemals Kompression. Jeder Frame ist eine flache 16-Bit-Bitmap von
Width×Height. Es gibt keinen RLE-Pfad wie bei GAF – große Flammeneffekte (composite02flame) erreichen ~600 KB, eben weil nichts gepackt ist. - Das Namensfeld kann Datenmüll enthalten. Die Bytes nach dem abschließenden NUL sind in mehreren Dateien der Verkaufsversion nicht initialisiert; bewahre für einen exakten Round-Trip alle 32 Byte, lies aber nur bis zum NUL.
- Das Flag-Byte
0x0Bist ungeklärt. Beobachtet als0x00oder0xFF. kbot übernimmt es unverändert; normalisiere es nicht, sonst stimmen Round-Trips nicht mehr überein. - Der GIF-Export ist verlustbehaftet. GIF kann nur eine 1-Bit-Maske und 255 Farben halten – weiches ARGB4444-Alpha wird zu harten Kanten. Nimm APNG, um den echten Alphakanal zu erhalten.
- Ein
Delay/ eineDurationvon 0 ist zulässig. Wie bei GAF ist ein Frame mit null Ticks ereignisgesteuert (das MenübeispielJPGTestverwendetDelay = 0für einen statischen Hintergrund). „Repariere“ das nicht auf einen Standardwert.
Typische Größen
| Asset | Beobachteter Bereich in TAFs von TA:K |
|---|---|
Kleiner Effekt (bluefire_4444, 12 Frames zu etwa 32) |
~30–60 KB |
Explosion (manabomb_1555, 10 × 50×48) |
~48 KB |
Feuerball / Zauber (fireballa_1555) |
~50–120 KB |
Große zusammengesetzte Flamme (composite02flame) |
~600 KB (unkomprimiertes Truecolor summiert sich) |
| Pixeldaten pro Frame | genau Width × Height × 2 Byte |
Siehe auch
- GAF – der Container mit Palette, auf dem TAF aufbaut; lies ihn
zuerst und verwende ihn für
.taf-Dateien mit Palette. - PCX – wie Assets von TA:K mit Palette ihre Farben bekommen (Truecolor-TAF braucht keine).
- Oberfläche von TA:K – das Menüsystem, dessen Lader TSF für animierte Hintergründe direkt verarbeitet.
- TA und TA:K im Vergleich – was die beiden Spiele teilen und wo sie auseinandergehen.
- Glossar – origin, tick, sub-frame.