TAF / TSF — полноцветные анимации TA: Kingdoms
Источник: coreprime/kbot @ 0ab97bf · Редактировать на GitHub
TAF хранит полноцветные анимации спрайтов для Total Annihilation: Kingdoms: эффекты заклинаний, взрывы, огненные шары, магические кольца, спрайты воды и анимированные фоны меню. Он использует структуру контейнера GAF из Total Annihilation, но вместо 8-битных пикселей с индексами палитры хранит 16-битный ARGB, так что TAF несёт собственный цвет и настоящий альфа-канал — внешняя палитра не нужна. TSF — читаемая текстовая форма той же анимации: скрипт с фигурными скобками, который загрузчик интерфейса читает напрямую.
Tip
Попробуйте сами.
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
Все флаги описаны в справочнике по kbot taf / kbot tsf
для командной строки.
Из Go. Используйте пакет 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
Кратко
┌─ 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
└──────────────────────────────────────┘
TAF — это одна последовательность анимации, массив кадров, каждый из которых — прямоугольник ширина × высота из 16-битных полноцветных пикселей в little-endian с точкой привязки (origin) и длительностью в тиках. На двоичном уровне нет ни сжатия, ни слоёв: каждый кадр — плоский растр без сжатия.
Note
TAF — это контейнер GAF с другими пиксельными данными. Если вы
читали про GAF, заголовок, таблица точек входа, заголовок
последовательности и список кадров будут знакомы до байта. Меняются
только толкование FrameInfo и кодирование пикселей. Оба варианта
живут под расширением .taf: TA: Kingdoms поставляет и палитровые
файлы .taf (читайте их как GAF), и полноцветные файлы
.taf (эта страница). kbot различает их по байту формата в каждом
кадре — см. Как их различить.
Заголовок (12 байт)
typedef struct {
uint32 Version; // 0x00010100 — identical to the GAF version word
uint32 SequenceCount; // Always 1 for a truecolor TAF
uint32 Unknown1; // Always 0
} TAFHeader;
| Поле | Примечания |
|---|---|
Version |
0x00010100, то же слово, что и в GAF из TA. Проверяйте его, прежде чем доверять любому смещению. |
SequenceCount |
Всегда 1. Сразу за ним идёт одно абсолютное смещение uint32, указывающее на SequenceHeader. Файл с несколькими последовательностями — не полноцветный TAF. |
Unknown1 |
Всегда ноль; при записи выводите ноль. |
Последовательность (40 байт)
typedef struct {
uint16 FrameCount;
uint16 Unknown1; // Always 1
uint32 Unknown2; // Always 0
char Name[32]; // NUL-padded, case-preserved
} TAFSequence;
Дальше следуют FrameCount × FrameListItem:
typedef struct {
uint32 PtrFrameInfo; // → FrameInfo
uint32 Duration; // Game ticks (30 ticks = 1 second)
} FrameListItem;
Warning
В 32-байтовом поле имени после терминатора может быть мусор. В нескольких оригинальных TAF после NUL остались неинициализированные байты буфера. kbot сохраняет все 32 байта в точности, чтобы round-trip «разбор → сериализация» был побайтно идентичным, но настоящее имя — это только текст до первого NUL.
Кадр (24 байта описания + полезная нагрузка)
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;
Именно здесь TAF расходится с GAF. В FrameInfo из GAF по смещению
0x09 находится флаг Compressed, а смещения 0x0A–0x0B — это
16-битный LayerCount. Полноцветный TAF использует те же байты
иначе:
| Смещение | Значение в GAF | Значение в TAF |
|---|---|---|
0x08 |
TransparencyIndex |
Transparency — не используется, всегда 0 |
0x09 |
Compressed (0/1) |
Format — 4 (ARGB4444) или 5 (ARGB1555) |
0x0A |
младший байт LayerCount |
LayerCount — всегда 0 |
0x0B |
старший байт LayerCount |
Flag — 0x00 или 0xFF, назначение неизвестно |
OriginX / OriginY — точка привязки при выводе кадра; обычно это
центр эффекта, так что у взрыва 50×48 точка привязки 25,24. kbot
сохраняет байт Flag как есть ради побайтно точного round-trip, хотя его
значение по всему набору оригинальных ресурсов не подтверждено.
Пиксельные данные
PtrPixelData указывает ровно на Width × Height значений uint16 в
little-endian — 2 байта на пиксель, сверху вниз, слева направо, без
сжатия и без заголовков строк. 16 бит декодируются в зависимости от
Format кадра:
ARGB4444 (Format == 4)
bit 15 ──────────────────────────── bit 0
┌─────┬─────┬─────┬─────┐
│ A │ R │ G │ B │ 4 bits each
└─────┴─────┴─────┴─────┘
15-12 11-8 7-4 3-0
Каждый 4-битный канал расширяется до 8 бит повторением битов
(v<<4 | v), так что 0xF → 0xFF, а 0x0 → 0x00. Это расширение
точно обратимо, и именно поэтому kbot может выполнять round-trip пикселей
без потерь.
ARGB1555 (Format == 5)
bit 15 ──────────────────────────── bit 0
┌─┬───────┬───────┬───────┐
│A│ R │ G │ B │ 1 + 5 + 5 + 5
└─┴───────┴───────┴───────┘
15 14-10 9-5 4-0
Единственный бит альфы работает по принципу «всё или ничего» (0 —
полностью прозрачный, 1 — полностью непрозрачный); каждый 5-битный
цветовой канал расширяется повторением (v<<3 | v>>2). Используйте
ARGB1555 для спрайтов с резкими краями, которым достаточно однобитной
маски, и ARGB4444, когда нужны 16 уровней мягкой альфы (дым, свечение,
затухание).
Important
TAF не нужна палитра. В отличие от GAF, который хранит только
индексы и зависит от palettes/palette.pal или сопутствующего PCX в
TA:K, каждый пиксель TAF уже содержит свой цвет и альфу. Отрисовка —
прямое расширение 16 бит → 32 бита без всякой таблицы поиска.
Как отличить палитровый TAF от полноцветного
У обоих видов .taf общий заголовок и слово версии GAF, поэтому по
первым 12 байтам их не различить. Различает их байт Format/Compressed
кадра по смещению 0x09 в FrameInfo:
| Байт по смещению 0x09 | Файл — это | Чем читать |
|---|---|---|
0 или 1 |
Палитровая анимация в стиле GAF (значение 0/1 означает без сжатия / со сжатием) | Загрузчик GAF + палитра |
4 или 5 |
Полноцветная анимация (ARGB4444 / ARGB1555) | эта страница (formats/tsf) |
kbot taf проверяет этот байт и отказывается открывать палитровый файл,
выдавая понятную ошибку; в этом случае берите kbot gaf. Большинство
полноцветных TAF в TA:K также следуют соглашению об именах — суффикс
_4444 или _1555 (manabomb_1555.taf, bluefire_4444.taf), — но
решает байт формата, а не имя.
TSF — текстовая форма
TSF (TAF Source Format) — та же анимация, записанная в виде скрипта в
духе INI с фигурными скобками. Загрузчик интерфейса TA: Kingdoms читает
TSF напрямую для фонов меню, и именно в этом формате обмена kbot
выдаёт результат декомпиляции TAF: одна секция на анимацию, вложенный
Frame на каждый кадр и один Layer на кадр со ссылкой на внешний файл
изображения.
/* anims/titlescreen.tsf — abridged */
[JPGTest]
{
Looping = 0;
[Frame0]
{
Delay = 0;
[Layer0]
{
AnchorX = 0;
AnchorY = 0;
Filename = TitleScreen.jpg;
}
}
[Frame1] { /* … */ }
}
| Токен | Соответствует | Примечания |
|---|---|---|
[Name] { … } |
Последовательности анимации | Одна на документ; Name становится именем последовательности TAF. |
Looping |
(справочно) | Подсказка для автора; в двоичном TAF поля зацикливания нет. |
[FrameN] |
Frame |
Delay — длительность в игровых тиках. |
Format |
Frame.Format |
ARGB4444 (по умолчанию, если не указан) или ARGB1555. |
[LayerN] |
Изображению кадра | Filename ссылается на внешний PNG/JPG; AnchorX/Y становятся точкой привязки кадра. |
Скомпилированный TAF сводит единственный слой каждого кадра в один
полноцветный растр. kbot ожидает ровно один слой на кадр (наложение
нескольких слоёв — удобство при создании интерфейса, которое компилятор
разрешает во время сборки); kbot tsf lint отмечает всё, что не скомпилируется чисто.
Tip
Просмотр TSF, не трогая изображения.
kbot tsf info anims/titlescreen.tsf # animation, frames, layer filenames
kbot tsf lint anims/titlescreen.tsf # shape check; non-zero exit on errors
lint проверяет только структуру — чтобы прогнать весь конвейер
загрузки изображений, используйте kbot taf compile.
Разбор примера — 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
Десять кадров 50×48, каждый привязан к своему центру (25,24) и
показывается 2 тика — звезда взрыва длиной ⅔ секунды. Весь файл занимает
48 376 байт: 50 × 48 × 2 = 4,800 байт пикселей на кадр × 10, плюс
заголовок, заголовок последовательности, список кадров и десять
24-байтовых записей описания кадров.
Работа в командной строке
Дерево команд kbot taf охватывает просмотр, отрисовку, преобразование
и цикл компиляции TAF↔TSF; kbot tsf работает с текстовой формой.
Каждая команда находит входной файл через активный
контекст kbot
или явно смонтированный --vfs, точно так же, как kbot gaf и
kbot studio, поэтому голые имена файлов вроде manabomb_1555.taf ищутся
во всех смонтированных архивах.
Просмотр и отрисовка
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 выдаёт APNG, который сохраняет полный
альфа-канал; --format gif квантует цвета до таблицы из 255 цветов плюс один прозрачный слот
(однобитная маска) — для превью и чат-клиентов.
Round-trip: декомпиляция ↔ компиляция
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 записывает TSF и по одному PNG на кадр; compile читает их
обратно в двоичный TAF. Для любого TAF, который kbot разобрал, round-trip
побайтно идентичен — это проверяют и kbot taf roundtrip, и
kbot taf diff.
Импорт из распространённых форматов
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 сводит анимированный GIF (с учётом его режимов смены кадров и
задержек каждого кадра) в полноцветный TAF; from-sheet нарезает на
кадры ленту спрайтов PNG/JPG с фиксированной сеткой. Обе команды
позволяют выбрать целевой формат пикселей.
Проверка
kbot taf lint anims/manabomb_1555.taf # structural findings; non-zero exit on errors
Инструменты MCP
Те же возможности доступны через MCP-сервер, так что агент может изучать анимации и показывать их напрямую:
| Инструмент | Назначение |
|---|---|
taf_info |
Полный JSON по кадрам: имя, размеры, точки привязки, форматы, длительности. |
taf_list |
Краткая сводка в JSON. |
taf_render |
Отрисовать один кадр и вернуть его прямо в ответе как PNG (по желанию также сохранить). |
taf_sheet |
Отрисовать все кадры на лист кадров (contact sheet) и вернуть его прямо в ответе как PNG. |
taf_export |
Записать на диск анимированный GIF или APNG. |
taf_lint |
Диагностика структуры в JSON. |
tsf_info |
Сводка по документу TSF (анимация, кадры, имена файлов слоёв). |
tsf_lint |
Проверка структуры документа TSF. |
taf_render и taf_sheet возвращают блок с изображением, поэтому модель
может посмотреть эффект заклинания без отдельного чтения файла. Пути
разрешаются относительно настроенной папки game-data так же, как в
любом другом MCP-инструменте kbot.
Подводные камни
Warning
У .taf два значения. .taf может быть палитровым GAF или
полноцветной анимацией. Всегда проверяйте байт формата кадра (0x09), а
не расширение или имя файла — см. Как их различить.
- Ровно одна последовательность. Полноцветный TAF всегда содержит одну
анимацию;
SequenceCount, отличный от1, означает, что перед вами другой (скорее всего, палитровый) файл. - Никакого сжатия. Каждый кадр — плоский 16-битный растр
Width×Height. Пути через RLE, как в GAF, нет — большие эффекты пламени (composite02flame) доходят до ~600 КБ именно потому, что ничего не упаковано. - В поле имени может быть мусор. Байты после завершающего NUL в нескольких оригинальных файлах не инициализированы; для точного round-trip сохраняйте все 32 байта, но читайте только до NUL.
- Назначение байта флага
0x0Bне выяснено. Встречаются0x00и0xFF. kbot сохраняет его как есть; не нормализуйте его, иначе round-trip перестанет совпадать. - Экспорт в GIF идёт с потерями. GIF вмещает только однобитную маску и 255 цветов — мягкая альфа ARGB4444 превращается в резкие края. Чтобы сохранить настоящий альфа-канал, используйте APNG.
Delay/Duration, равные 0, допустимы. Как и в GAF, кадр с нулём тиков управляется событиями (в примере менюJPGTestдля статичного фона используетсяDelay = 0). Не «исправляйте» это на значение по умолчанию.
Типичные размеры
| Ресурс | Диапазон в TAF из TA:K |
|---|---|
Небольшой эффект (bluefire_4444, 12 кадров размером около 32) |
~30–60 КБ |
Взрыв (manabomb_1555, 10 × 50×48) |
~48 КБ |
Огненный шар / заклинание (fireballa_1555) |
~50–120 КБ |
Большое составное пламя (composite02flame) |
~600 КБ (полноцветные данные без сжатия быстро набирают объём) |
| Пиксельные данные кадра | Ровно Width × Height × 2 байт |
См. также
- GAF — палитровый контейнер, на котором построен TAF;
прочитайте его первым и используйте для палитровых файлов
.taf. - PCX — откуда палитровые ресурсы TA:K берут цвета (полноцветному TAF они не нужны).
- Интерфейс TA:K — система меню, загрузчик которой читает TSF напрямую для анимированных фонов.
- Форматы TA и TA:K — что у двух игр общего и в чём они расходятся.
- Глоссарий — origin, tick, sub-frame.