TAF / TSF — Animazioni truecolor di TA: Kingdoms
TAF memorizza animazioni di sprite truecolor per Total Annihilation: Kingdoms: effetti degli incantesimi, esplosioni, palle di fuoco, anelli magici, sprite dell'acqua e gli sfondi animati dei menu. Riusa la struttura del contenitore GAF di Total Annihilation, ma sostituisce i pixel a 8 bit indicizzati sulla palette con pixel ARGB a 16 bit: un TAF porta con sé i propri colori e un vero canale alfa, senza bisogno di una palette esterna. TSF è la forma testuale leggibile della stessa animazione: uno script delimitato da graffe che il caricatore dell'interfaccia legge direttamente.
Tip
Provalo tu stesso.
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
Consulta il riferimento CLI di kbot taf / kbot tsf
per tutte le opzioni.
Da Go. Usa 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
In breve
┌─ 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 è una singola sequenza di animazione: un array di frame, ciascuno un rettangolo di larghezza × altezza di pixel truecolor a 16 bit little-endian, con un punto di origine e una durata in tick. A livello binario non ci sono né compressione né livelli: ogni frame è una bitmap piatta e non compressa.
Note
TAF è il contenitore GAF con un payload di pixel diverso. Se hai letto
GAF, l'intestazione, la tabella delle voci, l'intestazione della
sequenza e la lista dei frame ti saranno familiari byte per byte. Cambiano
solo l'interpretazione di FrameInfo e la codifica dei pixel. I due
convivono sotto l'estensione .taf: TA: Kingdoms distribuisce file .taf
con palette (da leggere come un GAF) e file .taf
truecolor (questa pagina). kbot li distingue dal byte del formato di
ciascun frame: vedi Distinguere i due
tipi.
Intestazione (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;
| Campo | Note |
|---|---|
Version |
0x00010100, la stessa parola di un GAF di TA. Verificala prima di fidarti di qualsiasi offset. |
SequenceCount |
Sempre 1. È seguito subito da un uint32 di offset assoluto che punta al SequenceHeader. Un file con più sequenze non è un TAF truecolor. |
Unknown1 |
Sempre zero; scrivi zero in scrittura. |
Sequenza (40 byte)
typedef struct {
uint16 FrameCount;
uint16 Unknown1; // Always 1
uint32 Unknown2; // Always 0
char Name[32]; // NUL-padded, case-preserved
} TAFSequence;
Seguita da FrameCount × FrameListItem:
typedef struct {
uint32 PtrFrameInfo; // → FrameInfo
uint32 Duration; // Game ticks (30 ticks = 1 second)
} FrameListItem;
Warning
Il campo del nome da 32 byte può contenere spazzatura dopo il terminatore. Diversi TAF originali lasciano byte di buffer non inizializzati dopo il NUL: kbot conserva esattamente i 32 byte, così che un round-trip analisi → serializzazione sia identico byte per byte, ma il vero nome è solo il testo fino al primo NUL.
Frame (info di 24 byte + payload)
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;
È qui che TAF si discosta da GAF. In un FrameInfo di un GAF, l'offset
0x09 è il flag Compressed e gli offset 0x0A–0x0B sono un
LayerCount a 16 bit. Un TAF truecolor riusa quegli stessi byte con
questo significato:
| Offset | Significato in GAF | Significato in TAF |
|---|---|---|
0x08 |
TransparencyIndex |
Transparency: inutilizzato, sempre 0 |
0x09 |
Compressed (0/1) |
Format: 4 (ARGB4444) o 5 (ARGB1555) |
0x0A |
byte basso di LayerCount |
LayerCount: sempre 0 |
0x0B |
byte alto di LayerCount |
Flag: 0x00 o 0xFF, scopo sconosciuto |
OriginX / OriginY sono il punto di ancoraggio usato quando si copia il
frame sullo schermo (blit): di solito il centro dell'effetto, quindi
un'esplosione 50×48 ha origine 25,24. kbot conserva il byte Flag tale e
quale per avere round-trip esatti byte per byte, anche se il suo significato
non è confermato sull'insieme degli asset originali.
Dati dei pixel
PtrPixelData punta esattamente a Width × Height valori uint16
little-endian: 2 byte per pixel, dall'alto in basso, da sinistra a destra,
senza compressione e senza intestazioni di riga. I 16 bit si decodificano in
base al Format del frame:
ARGB4444 (Format == 4)
bit 15 ──────────────────────────── bit 0
┌─────┬─────┬─────┬─────┐
│ A │ R │ G │ B │ 4 bits each
└─────┴─────┴─────┴─────┘
15-12 11-8 7-4 3-0
Ogni canale a 4 bit viene esteso a 8 bit per replicazione dei bit
(v<<4 | v), quindi 0xF → 0xFF e 0x0 → 0x00. L'espansione è
esattamente invertibile, ed è questo che permette a kbot di fare il
round-trip dei pixel senza perdite.
ARGB1555 (Format == 5)
bit 15 ──────────────────────────── bit 0
┌─┬───────┬───────┬───────┐
│A│ R │ G │ B │ 1 + 5 + 5 + 5
└─┴───────┴───────┴───────┘
15 14-10 9-5 4-0
L'unico bit alfa è tutto o niente (0 → completamente trasparente, 1 →
completamente opaco); ogni canale di colore a 5 bit viene esteso per
replicazione (v<<3 | v>>2). Usa ARGB1555 per gli sprite dai bordi netti
che hanno bisogno solo di un ritaglio a 1 bit, e ARGB4444 quando servono 16
livelli di alfa sfumato (fumo, bagliori, dissolvenze).
Important
TAF non ha bisogno di una palette. A differenza di GAF, che memorizza
solo indici e dipende da palettes/palette.pal o da un PCX di
accompagnamento di TA:K, ogni pixel TAF contiene già il proprio colore e il
proprio alfa. Il rendering è un'espansione diretta da 16 a 32 bit, senza
alcuna tabella di ricerca.
Distinguere i TAF con palette da quelli truecolor
Entrambi i tipi di .taf condividono l'intestazione e la parola di versione
del GAF, quindi non si possono distinguere dai primi 12 byte. Il
discriminante è il byte Format/Compressed del frame, all'offset 0x09
di FrameInfo:
| Byte a 0x09 | Il file è | Da leggere con |
|---|---|---|
0 o 1 |
Animazione con palette in stile GAF (un valore 0/1 significa non compresso/compresso) | caricatore GAF + una palette |
4 o 5 |
Animazione truecolor (ARGB4444 / ARGB1555) | questa pagina (formats/tsf) |
kbot taf controlla il byte e rifiuta un file con palette con un errore
chiaro; in quel caso usa kbot gaf. La maggior parte dei TAF truecolor di
TA:K segue anche una convenzione sui nomi dei file, un suffisso _4444 o
_1555 (manabomb_1555.taf, bluefire_4444.taf), ma a fare fede è il byte
del formato, non il nome.
TSF — la forma testuale
TSF (TAF Source Format) è la stessa animazione espressa come uno script in
stile INI delimitato da graffe. Il caricatore dell'interfaccia di TA:
Kingdoms legge i TSF direttamente per gli sfondi dei menu, ed è il
formato di interscambio che kbot produce quando decompili un TAF: una sezione
per animazione, un Frame annidato per ogni frame e un Layer per frame che
rimanda a un file di immagine esterno.
/* anims/titlescreen.tsf — abridged */
[JPGTest]
{
Looping = 0;
[Frame0]
{
Delay = 0;
[Layer0]
{
AnchorX = 0;
AnchorY = 0;
Filename = TitleScreen.jpg;
}
}
[Frame1] { /* … */ }
}
| Token | Corrisponde a | Note |
|---|---|---|
[Name] { … } |
La sequenza di animazione | Una per documento; Name diventa il nome della sequenza del TAF. |
Looping |
(indicativo) | Suggerimento per chi crea l'animazione; il TAF binario non ha un campo per il loop. |
[FrameN] |
Un Frame |
Delay è la durata in tick di gioco. |
Format |
Frame.Format |
ARGB4444 (predefinito se omesso) o ARGB1555. |
[LayerN] |
L'immagine del frame | Filename rimanda a un PNG/JPG esterno; AnchorX/Y diventano l'origine del frame. |
Un TAF compilato appiattisce l'unico livello di ogni frame in una sola bitmap
truecolor. kbot si aspetta esattamente un livello per frame (la composizione
di più livelli è una comodità per chi crea interfacce, che il compilatore
risolve in fase di build): kbot tsf lint segnala tutto ciò che non si
compilerebbe in modo pulito.
Tip
Esamina un TSF senza toccare le immagini.
kbot tsf info anims/titlescreen.tsf # animation, frames, layer filenames
kbot tsf lint anims/titlescreen.tsf # shape check; non-zero exit on errors
lint controlla solo la struttura: usa kbot taf compile per mettere alla
prova l'intera pipeline di caricamento delle immagini.
Esempio svolto — 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
Dieci frame 50×48, ciascuno ancorato al proprio centro (25,24) e mostrato
per 2 tick: una stella esplosiva di ⅔ di secondo. L'intero file è di 48.376
byte: 50 × 48 × 2 = 4,800 byte di pixel per frame × 10, più l'intestazione,
l'intestazione della sequenza, la lista dei frame e dieci record di info da
24 byte.
Flussi di lavoro dalla CLI
L'albero kbot taf copre l'ispezione, il rendering, la conversione e il
ciclo di compilazione TAF↔TSF; kbot tsf copre la forma testuale. Ogni
comando risolve il proprio input tramite il contesto di
kbot attivo o un
montaggio esplicito con --vfs, esattamente come kbot gaf e kbot studio,
così i nomi di file semplici come manabomb_1555.taf vengono risolti in
tutti gli archivi montati.
Esaminare e disegnare
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
Per impostazione predefinita export produce APNG, che conserva l'intero
canale alfa; --format gif quantizza su una tabella di 255 colori più uno
slot trasparente (un ritaglio a 1 bit), per anteprime e client di chat.
Round-trip decompilazione ↔ compilazione
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 scrive un TSF più un PNG per frame; compile li rilegge in un
TAF binario. Per qualsiasi TAF che kbot ha analizzato, il round-trip è
identico byte per byte: lo verificano sia kbot taf roundtrip sia
kbot taf diff.
Importare da formati comuni
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 appiattisce una GIF animata (rispettandone le modalità di
eliminazione dei frame, il disposal, e i ritardi per frame) in un TAF
truecolor; from-sheet taglia in frame una striscia di sprite PNG/JPG a
griglia fissa. Entrambi ti lasciano scegliere il formato dei pixel di
destinazione.
Validare
kbot taf lint anims/manabomb_1555.taf # structural findings; non-zero exit on errors
Strumenti MCP
Le stesse funzioni sono esposte tramite il server MCP, così un agente può esaminare e mostrare direttamente le animazioni:
| Strumento | Scopo |
|---|---|
taf_info |
JSON completo per frame: nome, dimensioni, origini, formati, durate. |
taf_list |
Riepilogo JSON di una riga. |
taf_render |
Disegna un frame e lo restituisce inline come PNG (facoltativamente lo salva anche). |
taf_sheet |
Disegna tutti i frame in un foglio provini, restituito inline come PNG. |
taf_export |
Scrive su disco una GIF animata o un APNG. |
taf_lint |
Diagnostica strutturale in JSON. |
tsf_info |
Riassume un documento TSF (animazione, frame, nomi dei file dei livelli). |
tsf_lint |
Controlla la forma di un documento TSF. |
taf_render e taf_sheet restituiscono un blocco di contenuto immagine,
così un modello può vedere l'anteprima di un effetto magico senza un
round-trip separato per leggere un file. I percorsi vengono risolti rispetto
alla cartella game-data configurata, come per ogni altro strumento MCP di
kbot.
Insidie
Warning
.taf ha più significati. Un .taf può essere un GAF con palette o
un'animazione truecolor. Controlla sempre il byte del formato del frame
(0x09), non l'estensione o il nome del file: vedi Distinguere i due
tipi.
- Esattamente una sequenza. Un TAF truecolor contiene sempre una sola
animazione; un
SequenceCountdiverso da1significa che stai guardando un altro tipo di file (probabilmente con palette). - Mai compressione. Ogni frame è una bitmap piatta
Width×Heighta 16 bit. Non c'è un percorso RLE come nel GAF: i grandi effetti di fiamma (composite02flame) arrivano a ~600 KB proprio perché nulla è compresso. - Il campo del nome può contenere spazzatura. In diversi file originali i byte dopo il NUL finale non sono inizializzati; conserva tutti i 32 byte per un round-trip esatto, ma leggi solo fino al NUL.
- Il byte di flag
0x0Bnon ha spiegazione. È stato osservato come0x00o0xFF. kbot lo conserva tale e quale; non normalizzarlo, o i round-trip smettono di coincidere. - L'esportazione in GIF perde informazioni. Una GIF può contenere solo un ritaglio a 1 bit e 255 colori: l'alfa sfumato di ARGB4444 diventa a bordi netti. Usa APNG per conservare il vero canale alfa.
- Un
Delay/Durationpari a 0 è valido. Come nel GAF, un frame da zero tick è guidato dagli eventi (l'esempio di menuJPGTestusaDelay = 0per uno sfondo statico). Non "correggerlo" con un valore predefinito.
Dimensioni tipiche
| Elemento | Intervallo osservato nei TAF di TA:K |
|---|---|
Effetto piccolo (bluefire_4444, 12 frame da circa 32) |
~30–60 KB |
Esplosione (manabomb_1555, 10 × 50×48) |
~48 KB |
Palla di fuoco / incantesimo (fireballa_1555) |
~50–120 KB |
Grande fiamma composita (composite02flame) |
~600 KB (il truecolor non compresso pesa) |
| Dati dei pixel per frame | Esattamente Width × Height × 2 byte |
Vedi anche
- GAF: il contenitore con palette su cui è costruito TAF; leggilo
per primo, e usalo per i file
.tafcon palette. - PCX: come gli asset con palette di TA:K ottengono i loro colori (il TAF truecolor non ne ha bisogno).
- Interfaccia TA:K: il sistema dei menu, il cui caricatore usa direttamente i TSF per gli sfondi animati.
- Formati TA e TA:K: che cosa condividono i due giochi e dove divergono.
- Glossario: origine (origin), tick, sotto-frame (sub-frame).