TAF / TSF — animations truecolor de TA: Kingdoms
Source : coreprime/kbot @ 0ab97bf · Modifier sur GitHub
Le TAF stocke les animations de sprites truecolor de Total Annihilation: Kingdoms : effets de sorts, explosions, boules de feu, anneaux magiques, sprites d'eau et fonds de menus animés. Il reprend la disposition du conteneur GAF de Total Annihilation, mais remplace les pixels 8 bits indexés dans une palette par de l'ARGB 16 bits : un TAF porte donc ses propres couleurs et un vrai canal alpha, sans palette externe. Le TSF est la forme texte lisible de la même animation : un script délimité par des accolades que le chargeur de l'interface lit directement.
Tip
Essayez vous-même.
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
Consultez la référence de kbot taf / kbot tsf
de la CLI pour toutes les options.
Depuis Go. Utilisez 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
Vue d'ensemble
┌─ 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
└──────────────────────────────────────┘
Un TAF est une séquence d'animation unique — un tableau de frames, chacune étant un rectangle largeur × hauteur de pixels truecolor 16 bits en little-endian, avec un point d'origine et une durée en ticks. Il n'y a ni compression ni calques au niveau binaire : chaque frame est une image plate, non compressée.
Note
Le TAF est le conteneur GAF avec une autre charge utile de pixels. Si
vous avez lu GAF, l'en-tête, la table des entrées, l'en-tête de
séquence et la liste des frames vous seront familiers à l'octet près. Seules
l'interprétation de FrameInfo et l'encodage des pixels changent. Les deux
cohabitent sous l'extension .taf : TA: Kingdoms livre des fichiers .taf
à palette (à lire comme des GAF) et des fichiers .taf
truecolor (cette page). kbot les distingue grâce à l'octet de format de
chaque frame — voir Distinguer les deux
types.
En-tête (12 octets)
typedef struct {
uint32 Version; // 0x00010100 — identical to the GAF version word
uint32 SequenceCount; // Always 1 for a truecolor TAF
uint32 Unknown1; // Always 0
} TAFHeader;
| Champ | Remarques |
|---|---|
Version |
0x00010100, le même mot que dans un GAF de TA. Vérifiez-le avant de vous fier au moindre offset. |
SequenceCount |
Toujours 1. Immédiatement suivi d'un offset absolu uint32 qui pointe sur le SequenceHeader. Un fichier à plusieurs séquences n'est pas un TAF truecolor. |
Unknown1 |
Toujours zéro ; écrire zéro. |
Séquence (40 octets)
typedef struct {
uint16 FrameCount;
uint16 Unknown1; // Always 1
uint32 Unknown2; // Always 0
char Name[32]; // NUL-padded, case-preserved
} TAFSequence;
Suivi de FrameCount × FrameListItem :
typedef struct {
uint32 PtrFrameInfo; // → FrameInfo
uint32 Duration; // Game ticks (30 ticks = 1 second)
} FrameListItem;
Warning
Le champ de nom de 32 octets peut contenir des déchets après le terminateur. Plusieurs TAF d'origine laissent des octets de tampon non initialisés après le NUL : kbot conserve les 32 octets exacts pour qu'un aller-retour lecture → sérialisation soit identique à l'octet près, mais seul le texte jusqu'au premier NUL constitue le vrai nom.
Frame (info de 24 octets + charge utile)
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;
C'est ici que le TAF s'écarte du GAF. Dans un FrameInfo de GAF, l'offset
0x09 est le flag Compressed et les offsets 0x0A–0x0B forment un
LayerCount de 16 bits. Un TAF truecolor réutilise ces mêmes octets avec
le sens suivant :
| Offset | Sens dans le GAF | Sens dans le TAF |
|---|---|---|
0x08 |
TransparencyIndex |
Transparency — inutilisé, toujours 0 |
0x09 |
Compressed (0/1) |
Format — 4 (ARGB4444) ou 5 (ARGB1555) |
0x0A |
octet de poids faible de LayerCount |
LayerCount — toujours 0 |
0x0B |
octet de poids fort de LayerCount |
Flag — 0x00 ou 0xFF, rôle inconnu |
OriginX / OriginY sont le point d'ancrage utilisé pour copier la frame à
l'écran — en général le centre de l'effet : une explosion de 50×48 a donc
pour origine 25,24. kbot conserve l'octet Flag tel quel pour des
allers-retours exacts à l'octet près, bien que son sens ne soit pas confirmé
sur l'ensemble des ressources d'origine.
Données de pixels
PtrPixelData pointe sur exactement Width × Height valeurs uint16 en
little-endian — 2 octets par pixel, de haut en bas et de gauche à droite,
sans compression ni en-têtes de ligne. Les 16 bits se décodent selon le
Format de la frame :
ARGB4444 (Format == 4)
bit 15 ──────────────────────────── bit 0
┌─────┬─────┬─────┬─────┐
│ A │ R │ G │ B │ 4 bits each
└─────┴─────┴─────┴─────┘
15-12 11-8 7-4 3-0
Chaque canal de 4 bits est étendu à 8 bits par réplication des bits
(v<<4 | v) : 0xF → 0xFF et 0x0 → 0x00. L'extension est exactement
inversible, ce qui permet à kbot de faire l'aller-retour des pixels sans
perte.
ARGB1555 (Format == 5)
bit 15 ──────────────────────────── bit 0
┌─┬───────┬───────┬───────┐
│A│ R │ G │ B │ 1 + 5 + 5 + 5
└─┴───────┴───────┴───────┘
15 14-10 9-5 4-0
L'unique bit alpha est en tout ou rien (0 → entièrement transparent, 1 →
entièrement opaque) ; chaque canal de couleur de 5 bits est étendu par
réplication (v<<3 | v>>2). Utilisez l'ARGB1555 pour les sprites aux bords
nets qui n'ont besoin que d'un détourage sur 1 bit, et l'ARGB4444 quand il
vous faut 16 niveaux d'alpha progressif (fumée, halos, fondus).
Important
Le TAF n'a besoin d'aucune palette. Contrairement au GAF — qui ne
stocke que des index et dépend de palettes/palette.pal ou d'un PCX
compagnon de TA:K —, chaque pixel d'un TAF contient déjà sa propre couleur
et son alpha. Le rendu est une simple extension 16 bits → 32 bits, sans
table de correspondance.
Distinguer les TAF à palette des TAF truecolor
Les deux sortes de .taf partagent l'en-tête et le mot de version du GAF :
impossible donc de les distinguer sur les 12 premiers octets. Le critère est
l'octet Format/Compressed de la frame, à l'offset 0x09 du FrameInfo :
| Octet en 0x09 | Le fichier est | À lire avec |
|---|---|---|
0 ou 1 |
une animation à palette de style GAF (une valeur 0/1 signifie non compressé/compressé) | le chargeur GAF + une palette |
4 ou 5 |
une animation truecolor (ARGB4444 / ARGB1555) | cette page (formats/tsf) |
kbot taf vérifie cet octet et refuse un fichier à palette avec une erreur
claire ; dans ce cas, tournez-vous vers kbot gaf. La plupart des TAF
truecolor de TA:K suivent aussi une convention de nommage — un suffixe
_4444 ou _1555 (manabomb_1555.taf, bluefire_4444.taf) —, mais c'est
l'octet de format qui fait foi, pas le nom.
TSF — la forme texte
Le TSF (TAF Source Format) est la même animation exprimée sous forme de
script de style INI, délimité par des accolades. Le chargeur d'interface de
TA: Kingdoms lit le TSF directement pour les fonds de menus, et c'est le
format d'échange que produit kbot quand vous décompilez un TAF : une section
par animation, une sous-section Frame par frame, et un Layer par frame
qui renvoie à un fichier image externe.
/* anims/titlescreen.tsf — abridged */
[JPGTest]
{
Looping = 0;
[Frame0]
{
Delay = 0;
[Layer0]
{
AnchorX = 0;
AnchorY = 0;
Filename = TitleScreen.jpg;
}
}
[Frame1] { /* … */ }
}
| Jeton | Correspond à | Remarques |
|---|---|---|
[Name] { … } |
La séquence d'animation | Une par document ; Name devient le nom de la séquence du TAF. |
Looping |
(indicatif) | Indication pour l'auteur ; le TAF binaire n'a pas de champ de boucle. |
[FrameN] |
Une Frame |
Delay est la durée en ticks de jeu. |
Format |
Frame.Format |
ARGB4444 (par défaut s'il est omis) ou ARGB1555. |
[LayerN] |
L'image de la frame | Filename renvoie à un PNG/JPG externe ; AnchorX/Y deviennent l'origine de la frame. |
Un TAF compilé aplatit l'unique calque de chaque frame en une seule image
truecolor. kbot attend exactement un calque par frame (la composition de
plusieurs calques est une commodité de création d'interface que le
compilateur résout à la construction) ; kbot tsf lint signale tout ce qui ne
se compilera pas proprement.
Tip
Inspecter un TSF sans toucher aux images.
kbot tsf info anims/titlescreen.tsf # animation, frames, layer filenames
kbot tsf lint anims/titlescreen.tsf # shape check; non-zero exit on errors
lint ne vérifie que la structure ; utilisez kbot taf compile pour
exercer toute la chaîne de chargement des images.
Exemple commenté — 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
Dix frames de 50×48, chacune ancrée en son centre (25,24) et affichée
pendant 2 ticks : une étoile d'explosion de ⅔ de seconde. Le fichier entier
fait 48 376 octets : 50 × 48 × 2 = 4,800 octets de pixels par frame × 10,
plus l'en-tête, l'en-tête de séquence, la liste des frames et dix
enregistrements d'info de 24 octets.
Utilisation en ligne de commande
L'arborescence kbot taf couvre l'inspection, le rendu, la conversion et le
cycle de compilation TAF↔TSF ; kbot tsf couvre la forme texte. Chaque
commande résout son entrée via le contexte kbot
actif ou un montage --vfs explicite, exactement comme kbot gaf et
kbot studio : des noms de fichiers nus comme manabomb_1555.taf sont donc
cherchés dans toutes les archives montées.
Inspecter et rendre
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 produit par défaut de l'APNG, qui conserve tout le canal alpha ;
--format gif quantifie vers une table de 255 couleurs plus un emplacement
transparent (un détourage sur 1 bit), pour les aperçus et les clients de
messagerie.
Aller-retour décompilation ↔ compilation
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 écrit un TSF plus un PNG par frame ; compile les relit pour
produire un TAF binaire. Pour tout TAF que kbot a analysé, l'aller-retour est
identique à l'octet près — kbot taf roundtrip et kbot taf diff le
vérifient tous deux.
Importer depuis des formats courants
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 aplatit un GIF animé (en respectant ses modes de disposition et
ses délais par frame) en un TAF truecolor ; from-sheet découpe en frames une
bande de sprites PNG/JPG à grille fixe. Les deux vous laissent choisir le
format de pixel cible.
Valider
kbot taf lint anims/manabomb_1555.taf # structural findings; non-zero exit on errors
Outils MCP
Les mêmes fonctions sont exposées par le serveur MCP, pour qu'un agent puisse inspecter et montrer les animations directement :
| Outil | Rôle |
|---|---|
taf_info |
JSON complet par frame : nom, tailles, origines, formats, durées. |
taf_list |
Résumé JSON d'une ligne. |
taf_render |
Rend une frame et la renvoie en ligne sous forme de PNG (et peut aussi l'enregistrer). |
taf_sheet |
Rend toutes les frames dans une planche contact, renvoyée en ligne sous forme de PNG. |
taf_export |
Écrit un GIF animé ou un APNG sur le disque. |
taf_lint |
Diagnostics structurels en JSON. |
tsf_info |
Résume un document TSF (animation, frames, noms des fichiers de calques). |
tsf_lint |
Vérifie la forme d'un document TSF. |
taf_render et taf_sheet renvoient un bloc de contenu image : un modèle
peut ainsi prévisualiser un effet de sort sans aller-retour supplémentaire de
lecture de fichier. Les chemins sont résolus par rapport au dossier
game-data configuré, comme pour tous les autres outils MCP de kbot.
Pièges
Warning
L'extension .taf est ambiguë. Un .taf peut être un GAF à palette
ou une animation truecolor. Vérifiez toujours l'octet de format de la
frame (0x09), pas l'extension ni le nom de fichier — voir Distinguer les deux types.
- Exactement une séquence. Un TAF truecolor contient toujours une seule
animation ; un
SequenceCountdifférent de1signifie que vous avez affaire à un autre fichier (probablement à palette). - Jamais de compression. Chaque frame est une image plate de
Width×Heighten 16 bits. Il n'existe pas de chemin RLE comme dans le GAF : les grands effets de flammes (composite02flame) atteignent ~600 Ko justement parce que rien n'est compacté. - Le champ de nom peut contenir des déchets. Dans plusieurs fichiers d'origine, les octets qui suivent le NUL final ne sont pas initialisés ; conservez les 32 octets pour un aller-retour exact, mais ne lisez que jusqu'au NUL.
- L'octet de flag
0x0Breste inexpliqué. On l'observe à0x00ou à0xFF. kbot le conserve tel quel ; ne le normalisez pas, sinon les allers-retours ne correspondent plus. - L'export GIF perd de l'information. Le GIF ne peut contenir qu'un détourage sur 1 bit et 255 couleurs : l'alpha progressif de l'ARGB4444 devient des bords nets. Utilisez l'APNG pour conserver le vrai canal alpha.
- Un
Delay/Durationde 0 est valide. Comme dans le GAF, une frame de zéro tick est pilotée par les événements (l'exemple de menuJPGTestutiliseDelay = 0pour un fond statique). Ne le « corrigez » pas en une valeur par défaut.
Tailles typiques
| Ressource | Plage observée dans les TAF de TA:K |
|---|---|
Petit effet (bluefire_4444, 12 × frames de ~32) |
~30–60 Ko |
Explosion (manabomb_1555, 10 × 50×48) |
~48 Ko |
Boule de feu / sort (fireballa_1555) |
~50–120 Ko |
Grande flamme composite (composite02flame) |
~600 Ko (le truecolor non compressé finit par peser lourd) |
| Données de pixels par frame | Width × Height × 2 octets, exactement |
Voir aussi
- GAF — le conteneur à palette sur lequel repose le TAF ; à lire en
premier, et à utiliser pour les fichiers
.tafà palette. - PCX — comment les ressources à palette de TA:K obtiennent leurs couleurs (le TAF truecolor n'en a pas besoin).
- Interface de TA:K — le système de menus dont le chargeur utilise directement le TSF pour les fonds animés.
- Formats TA et TA:K — ce que les deux jeux ont en commun et où ils divergent.
- Glossaire — origine, tick, sous-frame.