TAF / TSF — Animações truecolor do TA: Kingdoms
Fonte: coreprime/kbot @ 0ab97bf · Editar no GitHub
O TAF guarda as animações de sprites truecolor do Total Annihilation: Kingdoms: efeitos de feitiços, explosões, bolas de fogo, anéis mágicos, sprites de água e os fundos animados dos menus. Reutiliza a disposição do contentor GAF do Total Annihilation, mas troca os píxeis de 8 bits indexados na paleta por ARGB de 16 bits, por isso um TAF traz a sua própria cor e um verdadeiro canal alfa, sem precisar de paleta externa. O TSF é a forma de texto legível da mesma animação: um script delimitado por chavetas que o carregador da interface lê diretamente.
Tip
Experimente por si.
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
Todas as opções estão na referência de kbot taf / kbot tsf
da linha de comandos.
A partir de Go. Use 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
Visão geral
┌─ 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
└──────────────────────────────────────┘
Um TAF é uma única sequência de animação: um vetor de fotogramas, cada um com um retângulo de largura × altura de píxeis truecolor de 16 bits little-endian, um ponto de origem e uma duração em ticks. Não há compressão nem camadas ao nível binário: cada fotograma é um mapa de bits simples, não comprimido.
Note
O TAF é o contentor GAF com outro payload de píxeis. Se já leu a
página do GAF, o cabeçalho, a tabela de entrada, o cabeçalho da
sequência e a lista de fotogramas são-lhe familiares byte a byte. Só
mudam a interpretação de FrameInfo e a codificação dos píxeis. Os dois
coexistem sob a extensão .taf: o TA: Kingdoms inclui ficheiros .taf
com paleta (leia-os como um GAF) e ficheiros .taf
truecolor (esta página). O kbot distingue-os pelo byte de formato de
cada fotograma; veja Distinguir os
dois.
Cabeçalho (12 bytes)
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 | Notas |
|---|---|
Version |
0x00010100, a mesma palavra que um GAF do TA tem. Valide-a antes de confiar em qualquer deslocamento. |
SequenceCount |
Sempre 1. Seguido imediatamente de um deslocamento absoluto uint32 que aponta para o SequenceHeader. Um ficheiro com várias sequências não é um TAF truecolor. |
Unknown1 |
Sempre zero; escreva zero ao gravar. |
Sequência (40 bytes)
typedef struct {
uint16 FrameCount;
uint16 Unknown1; // Always 1
uint32 Unknown2; // Always 0
char Name[32]; // NUL-padded, case-preserved
} TAFSequence;
Seguem-se FrameCount × FrameListItem:
typedef struct {
uint32 PtrFrameInfo; // → FrameInfo
uint32 Duration; // Game ticks (30 ticks = 1 second)
} FrameListItem;
Warning
O campo de nome de 32 bytes pode trazer lixo depois do terminador. Vários TAF da versão comercial deixam bytes de buffer não inicializados depois do NUL; o kbot preserva exatamente os 32 bytes, para que uma ida e volta análise → serialização seja idêntica byte a byte, mas só o texto até ao primeiro NUL é o nome verdadeiro.
Fotograma (24 bytes de informação + 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;
É aqui que o TAF se afasta do GAF. Num FrameInfo de GAF, o deslocamento
0x09 é a flag Compressed e os deslocamentos 0x0A–0x0B são um
LayerCount de 16 bits. Um TAF truecolor reutiliza esses mesmos bytes
com este significado:
| Deslocamento | Significado no GAF | Significado no TAF |
|---|---|---|
0x08 |
TransparencyIndex |
Transparency — não usado, sempre 0 |
0x09 |
Compressed (0/1) |
Format — 4 (ARGB4444) ou 5 (ARGB1555) |
0x0A |
byte inferior de LayerCount |
LayerCount — sempre 0 |
0x0B |
byte superior de LayerCount |
Flag — 0x00 ou 0xFF, finalidade desconhecida |
OriginX / OriginY são a âncora usada ao copiar (blit) o fotograma:
normalmente o centro do efeito, por isso uma explosão de 50×48 tem a
origem em 25,24. O kbot mantém o byte Flag tal e qual, para idas e
voltas exatas ao byte, embora o seu significado não esteja confirmado no
conjunto de recursos da versão comercial.
Dados de píxeis
PtrPixelData aponta para exatamente Width × Height valores uint16
little-endian: 2 bytes por píxel, de cima para baixo, da esquerda para a
direita, sem compressão e sem cabeçalhos de linha. Os 16 bits
descodificam-se de acordo com o Format do fotograma:
ARGB4444 (Format == 4)
bit 15 ──────────────────────────── bit 0
┌─────┬─────┬─────┬─────┐
│ A │ R │ G │ B │ 4 bits each
└─────┴─────┴─────┴─────┘
15-12 11-8 7-4 3-0
Cada canal de 4 bits é alargado a 8 bits por replicação de bits
(v<<4 | v), por isso 0xF → 0xFF e 0x0 → 0x00. A expansão é
exatamente invertível, o que permite ao kbot fazer a ida e volta dos
píxeis sem perdas.
ARGB1555 (Format == 5)
bit 15 ──────────────────────────── bit 0
┌─┬───────┬───────┬───────┐
│A│ R │ G │ B │ 1 + 5 + 5 + 5
└─┴───────┴───────┴───────┘
15 14-10 9-5 4-0
O bit de alfa único é tudo ou nada (0 → totalmente transparente, 1 →
totalmente opaco); cada canal de cor de 5 bits é alargado por replicação
(v<<3 | v>>2). Use ARGB1555 nos sprites de margens nítidas que só
precisam de um recorte de 1 bit, e ARGB4444 quando precisar de 16 níveis
de alfa suave (fumo, brilhos, desvanecimentos).
Important
O TAF não precisa de paleta. Ao contrário do GAF, que só guarda
índices e depende de palettes/palette.pal ou de um PCX auxiliar do
TA:K, cada píxel de um TAF já contém a sua própria cor e o seu alfa. A
renderização é uma expansão direta de 16 bits → 32 bits, sem qualquer
tabela de consulta.
Distinguir os TAF com paleta dos truecolor
Os dois tipos de .taf partilham o cabeçalho e a palavra de versão do
GAF, por isso não é possível distingui-los pelos primeiros 12 bytes. O que
os distingue é o byte Format/Compressed do fotograma, no deslocamento
0x09 de FrameInfo:
| Byte em 0x09 | O ficheiro é | Ler com |
|---|---|---|
0 ou 1 |
Animação com paleta ao estilo GAF (um valor de 0/1 significa não comprimido/comprimido) | O carregador de GAF + uma paleta |
4 ou 5 |
Animação truecolor (ARGB4444 / ARGB1555) | Esta página (formats/tsf) |
kbot taf valida o byte e recusa um ficheiro com paleta com um erro
claro; nesse caso, recorra a kbot gaf. A maioria dos TAF truecolor do
TA:K também segue uma convenção de nomes, com um sufixo _4444 ou _1555
(manabomb_1555.taf, bluefire_4444.taf), mas quem decide é o byte de
formato, não o nome.
TSF — a forma de texto
O TSF (TAF Source Format) é a mesma animação expressa num script ao
estilo INI, delimitado por chavetas. O carregador da interface do TA:
Kingdoms lê o TSF diretamente para os fundos dos menus, e é o formato
de intercâmbio que o kbot gera quando descompila um TAF: uma secção por
animação, um Frame aninhado por fotograma e um Layer por fotograma,
que refere um ficheiro de imagem externo.
/* anims/titlescreen.tsf — abridged */
[JPGTest]
{
Looping = 0;
[Frame0]
{
Delay = 0;
[Layer0]
{
AnchorX = 0;
AnchorY = 0;
Filename = TitleScreen.jpg;
}
}
[Frame1] { /* … */ }
}
| Token | Corresponde a | Notas |
|---|---|---|
[Name] { … } |
A sequência de animação | Uma por documento; Name passa a ser o nome da sequência do TAF. |
Looping |
(indicativo) | Sugestão para o autor; o TAF binário não tem campo de ciclo. |
[FrameN] |
Um Frame |
Delay é a duração em ticks do jogo. |
Format |
Frame.Format |
ARGB4444 (por omissão) ou ARGB1555. |
[LayerN] |
A imagem do fotograma | Filename refere um PNG/JPG externo; AnchorX/Y passam a ser a origem do fotograma. |
Um TAF compilado achata a única camada de cada fotograma num mapa de bits
truecolor. O kbot espera exatamente uma camada por fotograma (a composição
de várias camadas é uma comodidade de criação da interface que o
compilador resolve no momento da construção); kbot tsf lint assinala
tudo o que não compile sem problemas.
Tip
Inspecione um TSF sem tocar nas imagens.
kbot tsf info anims/titlescreen.tsf # animation, frames, layer filenames
kbot tsf lint anims/titlescreen.tsf # shape check; non-zero exit on errors
lint só valida a estrutura; use kbot taf compile para pôr à prova
todo o processo de carregamento das imagens.
Exemplo comentado — 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
Dez fotogramas de 50×48, cada um ancorado no centro (25,24) e mostrado
durante 2 ticks: uma estrela de explosão de ⅔ de segundo. O ficheiro
inteiro tem 48 376 bytes: 50 × 48 × 2 = 4,800 bytes de píxeis por
fotograma × 10, mais o cabeçalho, o cabeçalho da sequência, a lista de
fotogramas e dez registos de informação de 24 bytes.
Fluxos de trabalho na linha de comandos
A árvore kbot taf cobre a inspeção, a renderização, a conversão e o
ciclo de compilação TAF↔TSF; kbot tsf trata da forma de texto. Todos os
comandos resolvem a entrada através do contexto do kbot
ativo ou de uma montagem --vfs explícita, exatamente como kbot gaf e
kbot studio, por isso nomes de ficheiro simples como manabomb_1555.taf
são procurados em todos os arquivos montados.
Inspecionar e renderizar
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 usa APNG por omissão, que mantém o canal alfa completo;
--format gif quantiza para uma tabela de 255 cores mais uma posição
transparente (um recorte de 1 bit), para pré-visualizações e clientes de
chat.
Ida e volta descompilar ↔ compilar
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 escreve um TSF e um PNG por fotograma; compile volta a
lê-los para um TAF binário. Em qualquer TAF que o kbot tenha analisado, a
ida e volta é idêntica byte a byte; tanto kbot taf roundtrip como
kbot taf diff o verificam.
Importar de formatos comuns
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 achata um GIF animado num TAF truecolor e respeita os modos de
descarte e os atrasos de cada fotograma; from-sheet corta em
fotogramas uma tira de sprites PNG/JPG em grelha fixa. Ambos permitem
escolher o formato de píxel de destino.
Validar
kbot taf lint anims/manabomb_1555.taf # structural findings; non-zero exit on errors
Ferramentas MCP
A mesma interface está disponível através do servidor MCP, para que um agente possa inspecionar e mostrar animações diretamente:
| Ferramenta | Finalidade |
|---|---|
taf_info |
JSON completo por fotograma: nome, tamanhos, origens, formatos, durações. |
taf_list |
Resumo JSON de uma linha. |
taf_render |
Renderiza um fotograma e devolve-o embutido como PNG (e, opcionalmente, também o grava). |
taf_sheet |
Renderiza todos os fotogramas numa folha de contactos, devolvida embutida como PNG. |
taf_export |
Escreve um GIF animado ou um APNG no disco. |
taf_lint |
Diagnósticos estruturais em JSON. |
tsf_info |
Resume um documento TSF (animação, fotogramas, nomes dos ficheiros das camadas). |
tsf_lint |
Verifica a forma de um documento TSF. |
taf_render e taf_sheet devolvem um bloco de conteúdo de imagem, para
que um modelo possa pré-visualizar um efeito de feitiço sem uma ida e
volta separada para ler um ficheiro. Os caminhos são resolvidos na pasta
game-data configurada, da mesma forma que em todas as outras
ferramentas MCP do kbot.
Armadilhas
Warning
.taf tem dois significados. Um .taf pode ser um GAF com paleta
ou uma animação truecolor. Verifique sempre o byte de formato do
fotograma (0x09), e não a extensão ou o nome do ficheiro; veja Distinguir os dois.
- Exatamente uma sequência. Um TAF truecolor tem sempre uma única
animação; um
SequenceCountdiferente de1significa que está a olhar para outro tipo de ficheiro (provavelmente com paleta). - Nunca há compressão. Cada fotograma é um mapa de bits simples de
Width×Heightcom 16 bits. Não há um caminho RLE como no GAF: os grandes efeitos de chamas (composite02flame) chegam a ~600 KB precisamente porque nada é comprimido. - O campo do nome pode ter lixo. Os bytes depois do NUL terminador não estão inicializados em vários ficheiros da versão comercial; preserve os 32 bytes para uma ida e volta exata, mas leia só até ao NUL.
- O byte de flag
0x0Bnão tem explicação. Foi observado como0x00ou0xFF. O kbot mantém-no tal e qual; não o normalize, ou as idas e voltas deixam de coincidir. - A exportação para GIF perde informação. O GIF só suporta um recorte de 1 bit e 255 cores: o alfa suave do ARGB4444 passa a margens nítidas. Use APNG para preservar o verdadeiro canal alfa.
- Um
Delay/Durationde 0 é válido. Tal como no GAF, um fotograma de zero ticks é orientado por eventos (o exemplo de menuJPGTestusaDelay = 0para um fundo estático). Não o "corrija" para um valor predefinido.
Tamanhos típicos
| Recurso | Intervalo observado nos TAF do TA:K |
|---|---|
Efeito pequeno (bluefire_4444, 12 fotogramas de mais ou menos 32) |
~30–60 KB |
Explosão (manabomb_1555, 10 × 50×48) |
~48 KB |
Bola de fogo / feitiço (fireballa_1555) |
~50–120 KB |
Grande chama composta (composite02flame) |
~600 KB (o truecolor sem compressão acumula) |
| Dados de píxeis por fotograma | Width × Height × 2 bytes, exatamente |
Ver também
- GAF — o contentor com paleta em que o TAF assenta; leia-o
primeiro e use-o para os ficheiros
.tafcom paleta. - PCX — como os recursos do TA:K com paleta obtêm as suas cores (o TAF truecolor não precisa de nenhuma).
- Interface do TA:K — o sistema de menus cujo carregador usa o TSF diretamente para os fundos animados.
- Formatos do TA e do TA:K — o que os dois jogos partilham e onde divergem.
- Glossário — origem, tick, subfotograma.