TAF / TSF: animações truecolor do TA: Kingdoms
Fonte: coreprime/kbot @ 0ab97bf · Editar no GitHub
O TAF guarda 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. Ele reaproveita a estrutura do contêiner GAF do Total Annihilation, mas troca os pixels de 8 bits indexados por paleta por ARGB de 16 bits. Assim, um TAF traz as suas próprias cores e um canal alfa de verdade, sem precisar de paleta externa. O TSF é a forma em texto legível da mesma animação: um script delimitado por chaves que o carregador da interface lê diretamente.
Tip
Experimente você mesmo.
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 do kbot taf / kbot tsf
da linha de comando.
Em 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 array de quadros, cada um um retângulo de largura × altura de pixels truecolor de 16 bits em little-endian, com um ponto de origem e uma duração em ticks. No nível binário não há compressão nem camadas: todo quadro é um bitmap simples e sem compressão.
Note
O TAF é o contêiner GAF com outro tipo de dados de pixels. Se você já
leu a página do GAF, o cabeçalho, a tabela de entrada, o
cabeçalho da sequência e a lista de quadros são conhecidos byte a byte.
Só a interpretação do FrameInfo e a codificação dos pixels mudam. Os
dois convivem sob a extensão .taf: o TA: Kingdoms traz arquivos .taf
com paleta (leia-os como um GAF) e arquivos .taf
truecolor (esta página). O kbot os distingue pelo byte de formato de
cada quadro. Veja Como 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 | Observações |
|---|---|
Version |
0x00010100, o mesmo valor que um GAF do TA traz. Valide-o antes de confiar em qualquer offset. |
SequenceCount |
Sempre 1. Logo em seguida vem um offset absoluto uint32 que aponta para o SequenceHeader. Um arquivo com várias sequências não é um TAF truecolor. |
Unknown1 |
Sempre zero. Grave zero. |
Sequência (40 bytes)
typedef struct {
uint16 FrameCount;
uint16 Unknown1; // Always 1
uint32 Unknown2; // Always 0
char Name[32]; // NUL-padded, case-preserved
} TAFSequence;
Em seguida vêm 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 TAFs 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 de leitura → serialização seja idêntica byte a byte, mas só o texto até o primeiro NUL é o nome de verdade.
Quadro (informações de 24 bytes + dados)
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 offset 0x09
é a flag Compressed e os offsets 0x0A–0x0B são um
LayerCount de 16 bits. Um TAF truecolor reaproveita esses mesmos
bytes com este significado:
| Offset | 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 baixo de LayerCount |
LayerCount: sempre 0 |
0x0B |
byte alto de LayerCount |
Flag: 0x00 ou 0xFF, finalidade desconhecida |
OriginX / OriginY são a âncora usada ao copiar o quadro, em geral o
centro do efeito: uma explosão de 50×48 tem origem 25,24. O kbot
mantém o byte Flag tal como está, para idas e voltas exatas byte a
byte, embora o seu significado não esteja confirmado no conjunto de
recursos da versão comercial.
Dados de pixels
PtrPixelData aponta para exatamente Width × Height valores uint16 em
little-endian: 2 bytes por pixel, de cima para baixo, da esquerda para a
direita, sem compressão e sem cabeçalhos de linha. Os 16 bits são
decodificados de acordo com o Format do quadro:
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 é ampliado para 8 bits por replicação de bits
(v<<4 | v), de modo que 0xF → 0xFF e 0x0 → 0x00. A expansão é
exatamente inversível, e é isso que permite ao kbot fazer a ida e volta dos
pixels 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 único bit de alfa é tudo ou nada (0 → totalmente transparente, 1 →
totalmente opaco). Cada canal de cor de 5 bits é ampliado por replicação
(v<<3 | v>>2). Use ARGB1555 em sprites de bordas duras que só precisam
de um recorte de 1 bit, e ARGB4444 quando precisar de 16 níveis de alfa
suave (fumaça, brilhos, esmaecimentos).
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 acompanhante do
TA:K, cada pixel 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 nenhuma
tabela de consulta.
Como distinguir o TAF com paleta do TAF truecolor
Os dois tipos de .taf compartilham o cabeçalho e o valor de versão do
GAF, por isso não dá para distingui-los pelos primeiros 12 bytes. O que os
diferencia é o byte Format/Compressed do quadro, no offset 0x09 do
FrameInfo:
| Byte em 0x09 | O arquivo é | Ler com |
|---|---|---|
0 ou 1 |
Animação com paleta no estilo GAF (o valor 0/1 significa sem compressão/com compressão) | O leitor de GAF + uma paleta |
4 ou 5 |
Animação truecolor (ARGB4444 / ARGB1555) | esta página (formats/tsf) |
kbot taf valida esse byte e recusa um arquivo com paleta com um erro
claro. Nesse caso, use kbot gaf. A maioria dos TAFs truecolor do TA:K
também segue uma convenção de nome de arquivo, 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 em texto
O TSF (TAF Source Format) é a mesma animação expressa como um script no
estilo INI, delimitado por chaves. 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 você descompila um TAF: uma seção
por animação, um Frame aninhado por quadro e um Layer por quadro que
referencia um arquivo de imagem externo.
/* anims/titlescreen.tsf — abridged */
[JPGTest]
{
Looping = 0;
[Frame0]
{
Delay = 0;
[Layer0]
{
AnchorX = 0;
AnchorY = 0;
Filename = TitleScreen.jpg;
}
}
[Frame1] { /* … */ }
}
| Elemento | Corresponde a | Observações |
|---|---|---|
[Name] { … } |
A sequência de animação | Uma por documento. Name vira o nome da sequência do TAF. |
Looping |
(indicativo) | Dica de criação. O TAF binário não tem campo de loop. |
[FrameN] |
Um Frame |
Delay é a duração em ticks do jogo. |
Format |
Frame.Format |
ARGB4444 (padrão se omitido) ou ARGB1555. |
[LayerN] |
A imagem do quadro | Filename referencia um PNG/JPG externo. AnchorX/Y viram a origem do quadro. |
Um TAF compilado achata a única camada de cada quadro num bitmap
truecolor. O kbot espera exatamente uma camada por quadro (compor várias
camadas é uma comodidade da criação de interfaces que o compilador resolve
na hora de montar), e kbot tsf lint aponta tudo o que não compila
direito.
Tip
Inspecione um TSF sem mexer 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 valida só a estrutura. Use kbot taf compile para exercitar 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 quadros de 50×48, cada um ancorado no centro (25,24) e mantido por 2
ticks: uma estrela de explosão de ⅔ de segundo. O arquivo inteiro tem
48.376 bytes: 50 × 48 × 2 = 4,800 bytes de pixels por quadro × 10, mais
o cabeçalho, o cabeçalho da sequência, a lista de quadros e os dez
registros de informações de 24 bytes.
Fluxos de trabalho na linha de comando
A árvore kbot taf cobre inspeção, renderização, conversão e o ciclo de
compilação TAF↔TSF. kbot tsf cobre a forma em texto. Todo comando
resolve a sua entrada pelo contexto do kbot
ativo ou por uma montagem --vfs explícita, exatamente como kbot gaf e
kbot studio, de modo que nomes de arquivo simples como
manabomb_1555.taf são procurados em todos os arquivos compactados
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
Por padrão, export gera APNG, que mantém o canal alfa completo.
--format gif reduz as cores a uma tabela de 255 cores mais uma posição
transparente (um recorte de 1 bit), para prévias e clientes de chat.
Descompilar ↔ compilar: ida e volta
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 grava um TSF e um PNG por quadro. compile os lê de volta
para um TAF binário. Para qualquer TAF que o kbot leu, a ida e volta é
idêntica byte a byte: kbot taf roundtrip e kbot taf diff verificam
isso.
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 (respeitando os seus modos de descarte e
os atrasos de cada quadro) num TAF truecolor. from-sheet fatia uma tira
de sprites PNG/JPG em grade fixa em quadros. Os dois permitem escolher o
formato de pixel de destino.
Validar
kbot taf lint anims/manabomb_1555.taf # structural findings; non-zero exit on errors
Ferramentas MCP
As mesmas funções estão disponíveis pelo servidor MCP, para que um agente possa inspecionar e mostrar animações diretamente:
| Ferramenta | Finalidade |
|---|---|
taf_info |
JSON completo por quadro: nome, tamanhos, origens, formatos, durações. |
taf_list |
Resumo em JSON de uma linha. |
taf_render |
Renderiza um quadro e o devolve embutido como PNG (e, opcionalmente, também o salva). |
taf_sheet |
Renderiza todos os quadros numa folha de contatos, devolvida embutida como PNG. |
taf_export |
Grava um GIF animado ou um APNG no disco. |
taf_lint |
Diagnóstico estrutural em JSON. |
tsf_info |
Resume um documento TSF (animação, quadros, nomes dos arquivos das camadas). |
tsf_lint |
Verifica a estrutura de um documento TSF. |
taf_render e taf_sheet devolvem um bloco de conteúdo de imagem, de
modo que um modelo pode ver a prévia de um efeito de feitiço sem uma ida e
volta extra para ler um arquivo. Os caminhos são resolvidos na pasta
game-data configurada, como em todas as outras ferramentas MCP do kbot.
Armadilhas
Warning
.taf tem dois sentidos. Um .taf pode ser um GAF com paleta ou
uma animação truecolor. Confira sempre o byte de formato do quadro
(0x09), e não a extensão ou o nome do arquivo. Veja Como distinguir os dois.
- Exatamente uma sequência. Um TAF truecolor sempre contém uma única
animação. Um
SequenceCountdiferente de1significa que você está olhando para outro tipo de arquivo (provavelmente com paleta). - Nunca há compressão. Todo quadro é um bitmap simples de
Width×Heightem 16 bits. Não existe um caminho RLE como o do GAF: os grandes efeitos de chamas (composite02flame) chegam a ~600 KB justamente porque nada é compactado. - O campo de nome pode conter lixo. Os bytes depois do NUL final não são inicializados em vários arquivos da versão comercial. Preserve os 32 bytes para uma ida e volta exata, mas leia só até o NUL.
- O byte de flag em
0x0Bnão tem explicação. Foi observado como0x00ou0xFF. O kbot o mantém tal como está. Não o normalize, senão as idas e voltas deixam de bater. - A exportação para GIF tem perdas. O GIF só comporta um recorte de 1 bit e 255 cores: o alfa suave do ARGB4444 vira bordas duras. Use APNG para preservar o canal alfa de verdade.
Delay/Durationigual a 0 é válido. Como no GAF, um quadro de zero ticks é controlado por eventos (o exemplo de menuJPGTestusaDelay = 0para um fundo estático). Não o "corrija" para um valor padrão.
Tamanhos típicos
| Recurso | Faixa observada nos TAFs do TA:K |
|---|---|
Efeito pequeno (bluefire_4444, quadros de cerca de 12 × 32) |
~30–60 KB |
Explosão (manabomb_1555, 10 × 50×48) |
~48 KB |
Bola de fogo / feitiço (fireballa_1555) |
~50–120 KB |
Chama composta grande (composite02flame) |
~600 KB (truecolor sem compressão pesa) |
| Dados de pixels por quadro | Exatamente Width × Height × 2 bytes |
Veja também
- GAF: o contêiner com paleta sobre o qual o TAF é construído.
Leia-o primeiro, e use-o para os arquivos
.tafcom paleta. - PCX: como os recursos com paleta do TA:K fornecem as suas cores (o TAF truecolor não precisa de nenhuma).
- Interface do TA:K: o sistema de menus cujo carregador lê TSF diretamente para os fundos animados.
- Formatos do TA e do TA:K: o que os dois jogos compartilham e onde se diferenciam.
- Glossário: origin (origem), tick, sub-frame (subquadro).