TAF / TSF: animaciones truecolor de TA: Kingdoms
Fuente: coreprime/kbot @ 0ab97bf · Editar en GitHub
TAF guarda las animaciones de sprites truecolor de Total Annihilation: Kingdoms: efectos de hechizos, explosiones, bolas de fuego, anillos mágicos, sprites de agua y los fondos animados de los menús. Reutiliza la disposición del contenedor GAF de Total Annihilation, pero cambia los píxeles de 8 bits indexados en la paleta por ARGB de 16 bits, así que un TAF lleva su propio color y un canal alfa de verdad: no necesita ninguna paleta externa. TSF es la forma de texto legible de la misma animación: un script delimitado por llaves que el cargador de la interfaz lee directamente.
Tip
Pruébalo tú mismo.
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 la referencia de kbot taf / kbot tsf
de la CLI para ver todas las opciones.
Desde 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
De un vistazo
┌─ 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 es una sola secuencia de animación: un array de fotogramas, cada uno un rectángulo de ancho × alto de píxeles truecolor de 16 bits en little-endian, con un punto de origen y una duración en ticks. A nivel binario no hay compresión ni capas: cada fotograma es un mapa de bits plano sin comprimir.
Note
TAF es el contenedor GAF con otros datos de píxeles. Si has leído
GAF, la cabecera, la tabla de entradas, la cabecera de
secuencia y la lista de fotogramas te resultarán conocidas byte a byte.
Solo cambian la interpretación de FrameInfo y la codificación de los
píxeles. Los dos conviven bajo la extensión .taf: TA: Kingdoms
distribuye archivos .taf con paleta (léelos como un
GAF) y archivos .taf truecolor (esta página). kbot los
distingue por el byte de formato de cada fotograma: consulta Cómo
distinguirlos.
Cabecera (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, la misma palabra que lleva un GAF de TA. Compruébala antes de fiarte de cualquier desplazamiento. |
SequenceCount |
Siempre 1. Le sigue inmediatamente un desplazamiento absoluto uint32 que apunta a la SequenceHeader. Un archivo con varias secuencias no es un TAF truecolor. |
Unknown1 |
Siempre cero; escribe cero al generar el archivo. |
Secuencia (40 bytes)
typedef struct {
uint16 FrameCount;
uint16 Unknown1; // Always 1
uint32 Unknown2; // Always 0
char Name[32]; // NUL-padded, case-preserved
} TAFSequence;
Le siguen FrameCount × FrameListItem:
typedef struct {
uint32 PtrFrameInfo; // → FrameInfo
uint32 Duration; // Game ticks (30 ticks = 1 second)
} FrameListItem;
Warning
El campo de nombre de 32 bytes puede llevar basura después del terminador. Varios TAF de la versión comercial dejan bytes de búfer sin inicializar después del NUL. kbot conserva los 32 bytes exactos para que el viaje de ida y vuelta análisis → serialización sea idéntico byte a byte, pero el nombre real es solo el texto hasta el primer NUL.
Fotograma (información de 24 bytes + datos)
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;
Aquí es donde TAF se separa de GAF. En el FrameInfo de un GAF, el
desplazamiento 0x09 es la marca Compressed y los desplazamientos
0x0A–0x0B son un LayerCount de 16 bits. Un TAF truecolor
reutiliza esos mismos bytes con este significado:
| Desplazamiento | Significado en GAF | Significado en TAF |
|---|---|---|
0x08 |
TransparencyIndex |
Transparency: sin uso, siempre 0 |
0x09 |
Compressed (0/1) |
Format: 4 (ARGB4444) o 5 (ARGB1555) |
0x0A |
byte bajo de LayerCount |
LayerCount: siempre 0 |
0x0B |
byte alto de LayerCount |
Flag: 0x00 o 0xFF, finalidad desconocida |
OriginX / OriginY son el punto de anclaje que se usa al volcar el
fotograma; suele ser el centro del efecto, así que una explosión de 50×48
tiene el origen en 25,24. kbot conserva el byte Flag tal cual para que
el viaje de ida y vuelta sea exacto byte a byte, aunque su significado no
está confirmado en el conjunto de recursos de la versión comercial.
Datos de píxeles
PtrPixelData apunta exactamente a Width × Height valores uint16 en
little-endian: 2 bytes por píxel, de arriba abajo y de izquierda a
derecha, sin compresión y sin cabeceras de fila. Los 16 bits se decodifican
según el Format del 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 se amplía a 8 bits replicando los bits
(v<<4 | v), así que 0xF → 0xFF y 0x0 → 0x00. La expansión es
exactamente invertible, y eso es lo que permite a kbot hacer el viaje de
ida y vuelta de los píxeles sin pérdidas.
ARGB1555 (Format == 5)
bit 15 ──────────────────────────── bit 0
┌─┬───────┬───────┬───────┐
│A│ R │ G │ B │ 1 + 5 + 5 + 5
└─┴───────┴───────┴───────┘
15 14-10 9-5 4-0
El único bit alfa es todo o nada (0 → totalmente transparente, 1 →
totalmente opaco); cada canal de color de 5 bits se amplía por replicación
(v<<3 | v>>2). Usa ARGB1555 para sprites de bordes duros que solo
necesitan un recorte de 1 bit, y ARGB4444 cuando necesites 16 niveles de
alfa suave (humo, resplandores, fundidos).
Important
TAF no necesita paleta. A diferencia de GAF, que solo guarda índices
y depende de palettes/palette.pal o de un PCX acompañante de TA:K, cada
píxel de un TAF ya contiene su propio color y su alfa. Renderizar es una
expansión directa de 16 a 32 bits, sin ninguna tabla de consulta de por
medio.
Cómo distinguir un TAF con paleta de uno truecolor
Las dos clases de .taf comparten la cabecera y la palabra de versión de
GAF, así que no puedes distinguirlas por los 12 primeros bytes. Lo que las
diferencia es el byte Format/Compressed del fotograma, en el
desplazamiento 0x09 de FrameInfo:
| Byte en 0x09 | El archivo es | Se lee con |
|---|---|---|
0 o 1 |
Animación con paleta al estilo GAF (un valor 0/1 significa sin comprimir/comprimido) | El cargador de GAF + una paleta |
4 o 5 |
Animación truecolor (ARGB4444 / ARGB1555) | Esta página (formats/tsf) |
kbot taf comprueba ese byte y rechaza los archivos con paleta con un
error claro; en ese caso, recurre a kbot gaf. La mayoría de los TAF
truecolor de TA:K siguen además una convención de nombres, un sufijo
_4444 o _1555 (manabomb_1555.taf, bluefire_4444.taf), pero lo que
manda es el byte de formato, no el nombre.
TSF: la forma de texto
TSF (TAF Source Format) es la misma animación expresada como un script
al estilo INI delimitado por llaves. El cargador de la interfaz de TA:
Kingdoms lee TSF directamente para los fondos de los menús, y es el
formato de intercambio que genera kbot cuando descompilas un TAF: una
sección por animación, un Frame anidado por fotograma y una Layer por
fotograma que hace referencia a un archivo de imagen externo.
/* anims/titlescreen.tsf — abridged */
[JPGTest]
{
Looping = 0;
[Frame0]
{
Delay = 0;
[Layer0]
{
AnchorX = 0;
AnchorY = 0;
Filename = TitleScreen.jpg;
}
}
[Frame1] { /* … */ }
}
| Token | Se corresponde con | Notas |
|---|---|---|
[Name] { … } |
La secuencia de animación | Una por documento; Name pasa a ser el nombre de la secuencia del TAF. |
Looping |
(orientativo) | Pista para quien crea el archivo; el TAF binario no tiene campo de bucle. |
[FrameN] |
Un Frame |
Delay es la duración en ticks de juego. |
Format |
Frame.Format |
ARGB4444 (por defecto si se omite) o ARGB1555. |
[LayerN] |
La imagen del fotograma | Filename hace referencia a un PNG/JPG externo; AnchorX/Y pasan a ser el origen del fotograma. |
Un TAF compilado aplana la única capa de cada fotograma en un mapa de bits
truecolor. kbot espera exactamente una capa por fotograma (la composición
de varias capas es una comodidad para crear interfaces que el compilador
resuelve al compilar); kbot tsf lint señala todo lo que no vaya a
compilar limpiamente.
Tip
Inspecciona un TSF sin tocar las imágenes.
kbot tsf info anims/titlescreen.tsf # animation, frames, layer filenames
kbot tsf lint anims/titlescreen.tsf # shape check; non-zero exit on errors
lint solo comprueba la estructura; usa kbot taf compile para poner a
prueba todo el proceso de carga de imágenes.
Ejemplo desarrollado: 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
Diez fotogramas de 50×48, cada uno anclado en su centro (25,24) y
mostrado durante 2 ticks: una estrella explosiva de ⅔ de segundo. El
archivo completo ocupa 48 376 bytes: 50 × 48 × 2 = 4,800 bytes de
píxeles por fotograma × 10, más la cabecera, la cabecera de secuencia, la
lista de fotogramas y los diez registros de información de 24 bytes.
Flujos de trabajo con la CLI
El árbol kbot taf cubre la inspección, el renderizado, la conversión y
el ciclo de compilación TAF↔TSF; kbot tsf cubre la forma de texto. Cada
comando resuelve su entrada a través del contexto de kbot
activo o de un montaje --vfs explícito, exactamente igual que kbot gaf
y kbot studio, así que los nombres de archivo sin ruta como
manabomb_1555.taf se buscan en todos los archivos comprimidos
montados.
Inspeccionar y 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 por defecto APNG, que conserva el canal alfa completo;
--format gif cuantiza a una tabla de 255 colores más una posición
transparente (un recorte de 1 bit), para vistas previas y clientes de
chat.
Viaje de ida y vuelta: 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 escribe un TSF y un PNG por fotograma; compile los vuelve a
leer y genera un TAF binario. Para cualquier TAF que kbot haya analizado,
el viaje de ida y vuelta es idéntico byte a byte: kbot taf roundtrip y
kbot taf diff lo verifican.
Importar desde formatos comunes
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 aplana un GIF animado (respetando sus modos de eliminación y
los retardos de cada fotograma) en un TAF truecolor; from-sheet corta
en fotogramas una tira de sprites PNG/JPG con cuadrícula fija. Los dos te
dejan elegir el formato de píxel de destino.
Validar
kbot taf lint anims/manabomb_1555.taf # structural findings; non-zero exit on errors
Herramientas MCP
Las mismas funciones se ofrecen a través del servidor MCP para que un agente pueda inspeccionar y mostrar animaciones directamente:
| Herramienta | Finalidad |
|---|---|
taf_info |
JSON completo por fotograma: nombre, tamaños, orígenes, formatos, duraciones. |
taf_list |
Resumen en JSON de una línea. |
taf_render |
Renderiza un fotograma y lo devuelve en línea como PNG (y opcionalmente lo guarda). |
taf_sheet |
Renderiza todos los fotogramas en una hoja de contactos que devuelve en línea como PNG. |
taf_export |
Escribe en disco un GIF animado o un APNG. |
taf_lint |
Diagnóstico estructural en JSON. |
tsf_info |
Resume un documento TSF (animación, fotogramas, nombres de archivo de las capas). |
tsf_lint |
Comprueba la forma de un documento TSF. |
taf_render y taf_sheet devuelven un bloque de contenido de imagen, así
que un modelo puede previsualizar un efecto de hechizo sin tener que hacer
otra ida y vuelta para leer un archivo. Las rutas se resuelven contra la
carpeta game-data configurada, igual que en cualquier otra herramienta
MCP de kbot.
Escollos
Warning
.taf tiene dos significados. Un .taf puede ser un GAF con paleta
o una animación truecolor. Comprueba siempre el byte de formato del
fotograma (0x09), no la extensión ni el nombre del archivo: consulta
Cómo distinguirlos.
- Exactamente una secuencia. Un TAF truecolor siempre contiene una
sola animación; un
SequenceCountdistinto de1significa que estás ante otro tipo de archivo (probablemente con paleta). - Nunca hay compresión. Cada fotograma es un mapa de bits plano de
Width×Heighta 16 bits. No hay una vía RLE como la de GAF: los efectos de llamas grandes (composite02flame) llegan a unos 600 KB precisamente porque no se empaqueta nada. - El campo de nombre puede contener basura. Los bytes que siguen al NUL final están sin inicializar en varios archivos de la versión comercial; conserva los 32 bytes para un viaje de ida y vuelta exacto, pero lee solo hasta el NUL.
- El byte de flags
0x0Bno tiene explicación. Se ha visto como0x00o0xFF. kbot lo conserva tal cual; no lo normalices o el viaje de ida y vuelta dejará de coincidir. - La exportación a GIF pierde información. GIF solo admite un recorte de 1 bit y 255 colores: el alfa suave de ARGB4444 se convierte en bordes duros. Usa APNG para conservar el canal alfa real.
- Un
Delay/Durationde 0 es válido. Como en GAF, un fotograma de cero ticks lo controlan los eventos (el ejemplo de menúJPGTestusaDelay = 0para un fondo estático). No lo «corrijas» con un valor por defecto.
Tamaños habituales
| Recurso | Rango observado en los TAF de TA:K |
|---|---|
Efecto pequeño (bluefire_4444, 12 fotogramas de unos 32 px) |
~30–60 KB |
Explosión (manabomb_1555, 10 × 50×48) |
~48 KB |
Bola de fuego / hechizo (fireballa_1555) |
~50–120 KB |
Llama compuesta grande (composite02flame) |
~600 KB (el truecolor sin comprimir se acumula) |
| Datos de píxeles por fotograma | Width × Height × 2 bytes, exactamente |
Véase también
- GAF: el contenedor con paleta sobre el que se construye TAF;
léelo primero y úsalo para los archivos
.tafcon paleta. - PCX: cómo obtienen sus colores los recursos con paleta de TA:K (el TAF truecolor no los necesita).
- Interfaz de TA:K: el sistema de menús cuyo cargador lee TSF directamente para los fondos animados.
- Formatos de TA y TA:K: qué comparten los dos juegos y en qué se diferencian.
- Glosario: origin, tick, sub-frame.