모든 형식 페이지 ↓
그래픽·미디어

TAF / TSF — TA: Kingdoms 트루컬러 애니메이션

출처: coreprime/kbot @ 0ab97bf · GitHub에서 편집

TAF는 Total Annihilation: Kingdoms의 트루컬러 스프라이트 애니메이션을 저장합니다: 마법 효과, 폭발, 화염구, 마법 고리, 물 스프라이트, 움직이는 메뉴 배경 등입니다. Total Annihilation의 GAF 컨테이너 구조를 그대로 쓰지만 8비트 팔레트 인덱스 픽셀을 16비트 ARGB로 바꾸었으므로, TAF는 자기 색상과 진짜 알파 채널을 직접 담습니다 — 외부 팔레트가 필요 없습니다. TSF는 같은 애니메이션을 사람이 읽을 수 있는 텍스트로 나타낸 형식으로, GUI 로더가 직접 읽는 중괄호 구분 스크립트입니다.

fireballa_1555.taf의 FireballA 시퀀스    가장자리 알파가 부드러운 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는 애니메이션 시퀀스 하나입니다 — 프레임의 배열이며, 각 프레임은 리틀 엔디언 16비트 트루컬러 픽셀로 된 너비 × 높이 직사각형에 원점과 틱 단위 지속 시간이 붙은 것입니다. 바이너리 수준에는 압축도 레이어도 없습니다: 모든 프레임은 압축하지 않은 단순한 비트맵입니다.

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, TA GAF에 들어 있는 것과 같은 워드입니다. 어떤 오프셋이든 믿기 전에 먼저 이 값을 확인하세요.
SequenceCount 항상 1. 바로 뒤에 SequenceHeader를 가리키는 uint32 절대 오프셋 하나가 옵니다. 시퀀스가 여러 개인 파일은 트루컬러 TAF가 아닙니다.
Unknown1 항상 0. 쓸 때도 0을 씁니다.

시퀀스(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바이트를 정확히 보존하지만, 진짜 이름은 첫 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와 갈라지는 지점이 여기입니다. GAF의 FrameInfo에서 오프셋 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입니다. Flag 바이트의 의미는 정식판 에셋 전체에서 확인되지 않았지만, kbot은 바이트 단위로 정확한 왕복 변환을 위해 이 값을 그대로 보존합니다.

픽셀 데이터

PtrPixelData는 정확히 Width × Height개의 리틀 엔디언 uint16 값을 가리킵니다 — 픽셀당 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비트 채널은 각각 비트 복제(v<<4 | v)로 8비트로 넓힙니다. 그래서 0xF → 0xFF, 0x0 → 0x00이 됩니다. 이 확장은 정확히 되돌릴 수 있으므로 kbot이 픽셀을 손실 없이 왕복 변환할 수 있습니다.

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)로 넓힙니다. 1비트 잘라내기만 필요한 가장자리가 딱딱한 스프라이트에는 ARGB1555를, 16단계의 부드러운 알파가 필요할 때(연기, 빛 번짐, 페이드)는 ARGB4444를 쓰세요.

Important

TAF에는 팔레트가 필요 없습니다. 인덱스만 저장하고 palettes/palette.pal이나 TA:K 부속 PCX에 의존하는 GAF와 달리, TAF의 모든 픽셀에는 이미 자기 색상과 알파가 들어 있습니다. 렌더링은 조회 테이블 없이 16비트 → 32비트로 바로 확장하는 것뿐입니다.


팔레트 TAF와 트루컬러 TAF 구별하기

두 종류의 .taf는 GAF 헤더와 버전 워드를 함께 쓰므로, 처음 12바이트로는 구별할 수 없습니다. 판별 기준은 FrameInfo 오프셋 0x09에 있는 프레임의 Format/Compressed 바이트입니다.

0x09의 바이트 파일 종류 읽는 방법
0 또는 1 팔레트 GAF 방식 애니메이션(값 0/1은 비압축/압축을 뜻함) GAF 로더 + 팔레트
4 또는 5 트루컬러 애니메이션(ARGB4444 / ARGB1555) 이 페이지(formats/tsf)

kbot taf는 이 바이트를 검사해 팔레트 파일을 분명한 오류와 함께 거부합니다. 그럴 때는 kbot gaf를 쓰세요. TA:K 트루컬러 TAF는 대부분 파일 이름 관례도 따릅니다 — _4444나 _1555 접미사(manabomb_1555.taf, bluefire_4444.taf) — 하지만 판단 기준은 이름이 아니라 형식 바이트입니다.


TSF — 텍스트 형식

TSF(TAF Source Format)는 같은 애니메이션을 중괄호로 구분하는 INI 비슷한 스크립트로 나타낸 것입니다. TA: Kingdoms GUI 로더는 메뉴 배경용으로 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은 프레임마다 레이어가 정확히 하나라고 가정합니다(여러 레이어 합성은 컴파일러가 빌드할 때 처리하는 GUI 제작상의 편의 기능입니다) — 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 gaf, kbot studio와 똑같이 활성 kbot 컨텍스트나 명시적인 --vfs 마운트를 거쳐 입력을 찾으므로, 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비트 잘라내기)를 더해 양자화합니다.

디컴파일 ↔ 컴파일 왕복 변환

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로 만듭니다. kbot이 파싱한 TAF라면 무엇이든 왕복 변환 결과가 바이트 단위로 동일합니다 — 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 모든 프레임을 밀착 인화지 형태로 렌더링해 PNG로 인라인 반환합니다.
taf_export 애니메이션 GIF나 APNG를 디스크에 씁니다.
taf_lint 구조 진단을 JSON으로 반환합니다.
tsf_info TSF 문서를 요약합니다(애니메이션, 프레임, 레이어 파일 이름).
tsf_lint TSF 문서의 모양을 검사합니다.

taf_render와 taf_sheet는 이미지 콘텐츠 블록을 돌려주므로, 모델은 파일을 따로 읽는 왕복 과정 없이 마법 효과를 미리 볼 수 있습니다. 경로는 다른 모든 kbot MCP 도구와 같은 방식으로, 설정된 game-data 폴더를 기준으로 찾습니다.


주의할 점

Warning

.taf는 여러 의미로 쓰입니다. .taf는 팔레트 GAF일 수도 있고 트루컬러 애니메이션일 수도 있습니다. 확장자나 파일 이름이 아니라 항상 프레임 형식 바이트(0x09)를 확인하세요 — 두 가지 구별하기를 참고하세요.

  • 시퀀스는 정확히 하나. 트루컬러 TAF에는 항상 애니메이션이 하나만 들어 있습니다. SequenceCount가 1이 아니면 다른 파일(아마 팔레트 파일)을 보고 있는 것입니다.
  • 압축은 절대 없음. 모든 프레임은 Width×Height 크기의 단순한 16비트 비트맵입니다. GAF 같은 RLE 경로가 없습니다 — 큰 불꽃 효과(composite02flame)가 약 600 KB에 이르는 것도 바로 아무것도 압축하지 않기 때문입니다.
  • 이름 필드에 쓰레기 값이 있을 수 있음. 정식판 파일 몇 개에서는 종료 NUL 뒤의 바이트가 초기화되어 있지 않습니다. 정확한 왕복 변환을 위해 32바이트를 모두 보존하되, 읽을 때는 NUL까지만 읽으세요.
  • 0x0B 플래그 바이트는 설명되지 않았음. 0x00 또는 0xFF로 관찰됩니다. kbot은 그대로 보존합니다. 정규화하면 왕복 변환 결과가 일치하지 않게 됩니다.
  • GIF 내보내기는 손실이 있음. GIF는 1비트 잘라내기와 255색만 담을 수 있으므로, ARGB4444의 부드러운 알파는 딱딱한 가장자리가 됩니다. 진짜 알파 채널을 보존하려면 APNG를 쓰세요.
  • Delay / Duration이 0이어도 유효함. GAF와 마찬가지로 0틱 프레임은 이벤트로 구동됩니다(메뉴의 JPGTest 예제는 정적인 배경에 Delay = 0을 씁니다). 기본값으로 "고치지" 마세요.

일반적인 크기

에셋 TA:K TAF에서 관찰된 범위
작은 효과(bluefire_4444, 대략 12 × 32 프레임) 약 30–60 KB
폭발(manabomb_1555, 10 × 50×48) 약 48 KB
화염구 / 마법(fireballa_1555) 약 50–120 KB
큰 합성 불꽃(composite02flame) 약 600 KB(압축하지 않은 트루컬러가 쌓인 결과)
프레임당 픽셀 데이터 정확히 Width × Height × 2바이트

함께 보기

  • GAF — TAF의 바탕이 되는 팔레트 컨테이너. 먼저 읽고, 팔레트 방식 .taf 파일에는 이것을 쓰세요.
  • PCX — 팔레트를 쓰는 TA:K 에셋이 색상을 얻는 방법(트루컬러 TAF에는 필요 없음).
  • TA:K GUI — 움직이는 배경용으로 로더가 TSF를 직접 읽는 메뉴 시스템.
  • TA와 TA:K 형식 비교 — 두 게임이 공유하는 것과 갈라지는 곳.
  • 용어집 — origin, tick, sub-frame.