Todas as páginas de formatos ↓
Gráficos e mídia

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.

sequência FireballA de fireballa_1555.taf    um quadro de BlueFire com bordas alfa suaves

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

os dez quadros de manabomb dispostos numa grade

$ 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 SequenceCount diferente de 1 significa que você está olhando para outro tipo de arquivo (provavelmente com paleta).
  • Nunca há compressão. Todo quadro é um bitmap simples de Width×Height em 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 0x0B não tem explicação. Foi observado como 0x00 ou 0xFF. 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 / Duration igual a 0 é válido. Como no GAF, um quadro de zero ticks é controlado por eventos (o exemplo de menu JPGTest usa Delay = 0 para 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 .taf com 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).