Усі сторінки форматів ↓
Графіка й медіа

TAF / TSF — повнокольорові анімації TA: Kingdoms

Джерело: coreprime/kbot @ 0ab97bf · Редагувати на GitHub

TAF зберігає повнокольорові анімації спрайтів для Total Annihilation: Kingdoms: ефекти заклинань, вибухи, вогняні кулі, магічні кільця, спрайти води й анімовані фони меню. Він повторно використовує структуру контейнера GAF із Total Annihilation, але замінює 8-бітні пікселі з індексами палітри на 16-бітний ARGB, тож TAF містить власні кольори й справжній альфа-канал — зовнішня палітра не потрібна. TSF — це зрозуміла людині текстова форма тієї самої анімації: скрипт, розмежований фігурними дужками, який завантажувач інтерфейсу читає безпосередньо.

Послідовність FireballA з fireballa_1555.taf    один кадр BlueFire з м'якими напівпрозорими краями

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

Усі прапорці описано в довіднику CLI 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 — це одна анімаційна послідовність: масив кадрів, кожен із яких є прямокутником width × height з 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 — це точка прив'язки, яка використовується під час виведення (blit) кадру; зазвичай це центр ефекту, тож вибух 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 для спрайтів із чіткими краями, яким досить 1-бітної маски вирізання, а 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

усі десять кадрів manabomb, розкладені сіткою

$ 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-байтових інформаційних записів.


Робота в CLI

Дерево команд 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 кольорів плюс один прозорий слот (1-бітна маска вирізання) — для попереднього перегляду й чат-клієнтів.

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 (з урахуванням його режимів disposal і затримок окремих кадрів) у повнокольоровий 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 вміщує лише 1-бітну маску вирізання й 255 кольорів — м'яка прозорість ARGB4444 перетворюється на чіткі краї. Щоб зберегти справжній альфа-канал, використовуйте APNG.
  • Delay / Duration, що дорівнює 0, — допустимі. Як і в GAF, кадр із нулем тіків керується подіями (приклад меню JPGTest використовує Delay = 0 для статичного фону). Не «виправляйте» його на значення за замовчуванням.

Типові розміри

Ресурс Діапазон, що трапляється в TAF із TA:K
Невеликий ефект (bluefire_4444, 12 кадрів приблизно 32 px) ~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.