Tutte le pagine dei formati ↓
Grafica e media

TAF / TSF — Animazioni truecolor di TA: Kingdoms

Fonte: coreprime/kbot @ 0ab97bf · Modifica su GitHub

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.

sequenza FireballA da fireballa_1555.taf    singolo frame di BlueFire con bordi alfa sfumati

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

tutti e dieci i frame di manabomb disposti in una griglia

$ 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 SequenceCount diverso da 1 significa che stai guardando un altro tipo di file (probabilmente con palette).
  • Mai compressione. Ogni frame è una bitmap piatta Width×Height a 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 0x0B non ha spiegazione. È stato osservato come 0x00 o 0xFF. 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 / Duration pari a 0 è valido. Come nel GAF, un frame da zero tick è guidato dagli eventi (l'esempio di menu JPGTest usa Delay = 0 per 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 .taf con 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).