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