TA: Kingdoms 맵 — .tnt + .ota + .crt + .kmp
TA: Kingdoms 맵은 메타데이터 파일 한두 개를 곁들인 TNT의 변형(IDVersion 워드가 다름)이며, 여기에 싱글플레이 미션용 HPI v2 캠페인 아카이브 형식이 더해집니다. 예전의 "BTM/높이 맵 BMP" 연구 노트가 짐작했던 것보다 TA의 맵 모델에 훨씬 가깝습니다 — 실제 GoG TA:K 설치본을 살펴보면
.btm이나 부속 BMP 파일은 전혀 없습니다.kbot의 기존 HPI v2 리더는
.kmp아카이브를 따로 신경 쓸 필요 없이 그대로 처리합니다. kbot의 TNT 리더는 TA:K 버전 워드를 인식하고 헤더, DataUnit 높이 맵, 텍스처 매핑 그리드(지형 이름 + U/V 오프셋 테이블), 지형지물 배치 그리드 + 이름 테이블, 내장 미니맵을 디코딩합니다 — 외부 지형 JPG와 지형지물 GAF 스프라이트를 합성해 완전한 맵을 원래 해상도로 렌더링하기에 충분합니다. 이 페이지는 정식 출시된 설치본에서 확인할 수 있는 내용과 아직 남아 있는 공백을 정리합니다.
Important
상태 업데이트(이 페이지의 처음 "연구 노트" 버전과 비교해). 이 페이지가
예전에 다루던 파일 유형(.btm, 부속 BMP, 진영별 팔레트 PCX)은 정식 출시된
GoG TA: Kingdoms 설치본에 실제로 존재하지 않습니다. 실제 파일 유형은 아래에
정리되어 있습니다. 다른 오픈 소스 TA:K 프로젝트와 비교해 보려면
iiCompleteDestruction을
참고하세요. 일부 TA:K 전용 구조에 대해서는 그쪽 파서 구현이 kbot보다 더
완전합니다.
TA:K 맵을 이루는 파일
GoG 설치본의 maps 디렉터리 전체에서 센 결과:
| 확장자 | 개수 | 용도 |
|---|---|---|
.kmp |
181 | 캠페인 미션의 맵 + 스크립트 + 에셋을 묶은 HPI v2 아카이브 |
.tnt |
55 | 맵 지형 데이터(TNT 모양의 바이너리, TA:K 전용 IDVersion 워드) |
.ota |
55 | 맵 메타데이터 — TDF 형식, TA의 .ota와 같은 모양에 TAK 전용 필드가 추가됨 |
.txt |
55 | 자유 텍스트 맵 설명(몇 단어, 로비에 표시됨) |
.crt |
33 | 시나리오 부속 파일: 미리 배치된 유닛, 플레이어별 규칙 엔진, 이름 붙은 트리거 영역 |
.tdf |
6 | 맵별 게임플레이 조정 파일(캠페인 조정, AI 유도) |
일반적인 멀티플레이 맵은 .tnt + .ota + .txt 세 파일로 배포되며, 때로는
.crt 부속 파일이 붙습니다. 캠페인 미션은 같은 파일 묶음에 미션 스크립트를
더해 .kmp 아카이브로 묶여 있습니다.
Tip
아무것도 풀지 않고 TA:K 맵 묶음의 목록 보기:
# The HPI v2 reader handles .kmp transparently
kbot hpi list /path/to/tak/maps/adamantine\ gate.kmp -v
kbot hpi info /path/to/tak/maps/adamantine\ gate.kmp
.tnt — TA:K 지형(TNT v2)
TA:K는 TA의 TNT 구조를 다시 쓰지만 IDVersion 워드를 올립니다.
abnar's terrace.tnt의 헥스 덤프:
00000000: 00 40 00 00 20 01 00 00 e0 00 00 00 3a 00 00 00 .@.. .......:...
00000010: 34 00 00 00 34 fc 00 00 34 f4 02 00 16 00 00 00 4...4...4.......
첫 uint32는 **0x00004000**입니다 — 0x00002000을 쓰는 TA의 TNT와 비교해
보세요. 컨테이너는 여전히 헤더 뒤에 포인터로 가리키는 섹션들이 오는 구조지만,
TA:K는 타일 모자이크 형식이 전혀 아닙니다. TA가 타일 인덱스 그리드 + 32×32
타일 그래픽 팔레트를 저장하는 자리에, TA:K는 외부 JPG 텍스처를 참조하는
텍스처 매핑 그리드로 지형을 저장하고, 별도의 높이 맵과 지형지물 배치
그리드를 둡니다. 다음 내용은 모두 정식 출시된 55개 맵에서 확인했습니다.
Width/Height는 픽셀이 아니라 16px _DataUnit_의 개수입니다.athri cay처럼 "15 × 15"인 맵은Width = Height = 480DataUnit을 저장합니다 → 한 변이480 × 16 = 7680px입니다. 높이 맵과 지형지물 그리드는 DataUnit마다 하나씩 표본을 두고, 지형은 그보다 거친 32px Graphic Unit 그리드로 표본을 둡니다. 이 그리드는 각 축에서 DataUnit 그리드의 정확히 절반입니다(guW = Width/2).- 헤더는 오프셋과 개수를 담은
uint32열두 개가 연속으로 놓인 것이며, TA의 필드 이름은 더 이상 내용을 설명하지 못합니다. 아래 표는 TA:K에서의 의미를 보여 줍니다. - 섹션 포인터는 절대 오프셋이므로, 각 섹션은 자기 포인터 + 계산된 길이로 읽습니다.
| 오프셋 | TA:K 필드 | 섹션 크기 | 비고 |
|---|---|---|---|
0x00 |
Version |
— | 0x4000(TA는 0x2000을 씀) |
0x04 |
Width |
— | DataUnit 단위. ×16 → 픽셀, ÷2 → Graphic Unit |
0x08 |
Height |
— | DataUnit 단위 |
0x0C |
SeaLevel |
— | 물로 간주하는 높이 기준값 |
0x10 |
HeightMapOffset |
Width × Height바이트 |
DataUnit마다 높이 바이트 하나 |
0x14 |
AttributesOffset |
Width × Height × 2 |
지형지물 그리드: DataUnit마다 uint16 인덱스(≥0xFF00 = 없음) |
0x18 |
FeatureNamesOffset |
Count × 132 |
지형지물 이름 테이블(4바이트 인덱스 + 128바이트 이름) |
0x1C |
FeatureCount |
— | 지형지물 종류의 수 |
0x20 |
TerrainNamesOffset |
guW × guH × 4 |
GU별 텍스처 이름 → terrain/%08x.jpg |
0x24 |
UMappingOffset |
guW × guH바이트 |
GU별 텍스처 안의 열 오프셋(×32px) |
0x28 |
VMappingOffset |
guW × guH바이트 |
GU별 텍스처 안의 행 오프셋(×32px) |
0x2C |
MiniMapOffset |
8 + 126×126 |
uint32 w, uint32 h, 그 뒤 인덱스 |
높이 맵(0x10)은 DataUnit마다 한 바이트입니다 — 색상 비트맵이 아니라
회색조 고도 필드입니다. 지형은 텍스처 매핑으로 재구성합니다: 각 Graphic
Unit에 대해 지형 이름 테이블(0x20)이 텍스처 파일(terrain/<name>.jpg, 이름은
소문자 8자리 16진수로 표기)을 알려 주고, U/V 테이블(0x24/0x28)이 그 JPG의
(u×32, v×32)에서 복사할 32×32 서브타일을 알려 줍니다. 지형지물
그리드(0x14)는 배치 레이어입니다: uint16의 Width × Height 배열로, 각
칸은 이름 테이블을 가리키는 지형지물 인덱스이거나 "여기에는 지형지물 없음"을
뜻하는 높은 감시값(≥0xFF00)입니다. 배치의 DataUnit 칸 (x, y)는 지형 픽셀
(x×16, y×16)에 대응합니다. 이름 테이블(0x18)은 TA의 TileAnim 테이블과
같은 132바이트 항목 구조를 씁니다.
미니맵 블록은 정식 출시된 파일 전체에서 항상 126 × 126이며, 정사각형이
아닌 맵의 바깥쪽을 채울 때 TA와 같은 0x64 공백(void) 바이트를 씁니다.
Note
현재 TA:K TNT에서 되는 것. kbot tnt describe는 헤더, 왕국, 높이 맵/지형
크기, 지형지물 분류별 개수를 보여 줍니다. kbot tnt preview --vfs <root>는
원래 해상도의 텍스처 매핑 지형을 렌더링하고, 그 위에 모든 지형지물의 GAF
스프라이트를 핫스팟에 맞춰 합성합니다 — 게임이 그리는 그대로의 완전한
맵입니다. kbot tnt image는 자체적으로 완결된 회색조 높이 맵을
렌더링합니다(외부 텍스처가 필요 없음, --features는 분류별 색상 표시를 겹쳐
그림). kbot tnt minimap은 내장된 126 × 126 미리 보기를 내보냅니다.
미니맵 팔레트 — 왕국마다 하나
TA:K는 미니맵을 하나의 전역 팔레트로 칠하지 않습니다. 각 맵은 126 × 126
미리 보기를 그 맵이 속한 왕국의 텍스처 팔레트에 맞춰 구워 둡니다 — 같은
이름의 .ota에 있는 kingdom= 필드(aramon / taros / veruna / zhon /
creon)입니다. 같은 인덱스를 엉뚱한 왕국의 팔레트로(또는 TA의 PALETTE.PAL로)
렌더링하면 얼룩덜룩한 잡음이 나오고, 올바른 팔레트로 렌더링하면 자연스러운
물, 해변, 지형이 나옵니다.
kbot tnt minimap은 같은 이름의 .ota에서 왕국을 자동으로 알아냅니다.
.ota가 없을 때(예: 낱개 .tnt)는 --kingdom <name>을 직접 지정하세요.
kbot tnt minimap "athri cay.tnt" -t mini.png # auto: veruna
kbot tnt minimap loose.tnt --kingdom aramon -t mini.png
.ota — 맵 메타데이터(TDF, 대부분 익숙함)
TA:K의 .ota는 TA의 것과 같은 TDF 모양의 형식이며, TAK 전용 필드가 몇 개
있습니다. abnar's terrace.ota 전체:
[GlobalHeader]
{
Copyright=Copyright 1998 Cavedog Entertainment. All rights reserved.;
missionname=Abnar's Terrace;
missiondescription=9 x 7 4 Player 32MB;
kingdom=veruna; // ← TA:K-specific: the side the map "belongs" to
numplayers=4;
size=9 x 7; // ← TA:K-specific: free-text size for the lobby
memory=32 MB; // ← TA:K-specific: ~RAM requirement for the lobby
hasscenario=0;
[Map Data]
{
Type=Network 1;
aiprofile=DEFAULT;
[specials]
{
[special0] { specialwhat=StartPos1; XPos=37; ZPos=190; }
[special1] { specialwhat=StartPos2; XPos=215; ZPos=38; }
…
}
}
}
| 필드 | TA의 대응 항목 | 비고 |
|---|---|---|
kingdom= |
없음 | 진영 소속(aramon / taros / veruna / zhon / creon). 로비가 이 값으로 필터링하며, .tnt에 구워 둔 미리 보기를 칠하는 지형/미니맵 팔레트도 이 값으로 고릅니다. |
size= |
없음 | 로비에 표시할, 사람이 읽을 수 있는 맵 크기. |
memory= |
없음 | 권장 RAM. 표시용일 뿐입니다. |
hasscenario= |
없음 | 캠페인 미션은 1, 스커미시 맵은 0. |
[Map Data] |
[Schema N] |
스키마 하위 섹션을 블록 하나로 대응시키는 맵(TA:K는 맵마다 스키마를 하나씩만 배포함). |
그 밖의 모든 것(StartPos1..N, aiprofile= 등)은 TA와 똑같이 동작합니다 —
kbot mount의 OTA 뷰어는 두 게임의 파일을 고치지 않고 모두 처리합니다.
.kmp — 캠페인 미션용 HPI v2 아카이브
.kmp는 확장자를 바꾼 HPI v2 아카이브입니다.
adamantine gate.kmp의 앞부분 헥스:
00000000: 48 41 50 49 00 00 02 00 57 32 02 00 77 00 00 00 HAPI....W2..w...
00000010: 15 32 02 00 42 00 00 00 20 00 00 00 00 00 00 00 .2..B... .......
00000020: 53 51 53 48 … SQSH…
v2 헤더 모양 그대로입니다(HAPI 표식 → 0x00020000 버전 → 24바이트 v2 헤더 →
SQSH 청크). kbot hpi list, info, extract가 모두 바로 동작합니다. 각
.kmp에는 보통 다음이 들어 있습니다.
- 미션의
.tnt와.ota - 컷신 / 브리핑용 GAF 몇 개
- 미션 스크립트 COB 파일 — TA:K는 미션 스크립트에 별도의 스크립트 언어를
쓰지 않고 COB 형식(
VersionSignature == 6)을 다시 씁니다. 정식판 TAK 설치본 전체에 있는 3,885개의MISSION_COMMAND(0x10073000) 옵코드 사용 지점이 여기에 있습니다. 바이너리 형식과, 미션 명령이 인덱스로 가리키는 COB별 사운드 이름 문자열 풀은 cob.md의 TA: Kingdoms — COB v6 차이점과 부록 C를 참고하세요. - 때로는 미션별 유닛 재정의
.crt — 시나리오 스크립트(유닛, 규칙, 트리거)
.crt는 맵의 스크립트 레이어를 담습니다: 플레이가 시작되기 전에 배치되는
유닛, 스커미시 AI와 캠페인 목표를 움직이는 플레이어별 규칙 엔진, 그리고 그
규칙들이 참조하는 이름 붙은 직사각형 트리거 영역입니다. 멀티플레이 맵은 빈
스텁(유닛 없음, 빈 플레이어 슬롯 아홉 개, 트리거 없음, 56바이트)을 담아
배포되고, 캠페인 맵과 특수 맵은 모든 섹션을 채우며 최대 약 120 KB에
이릅니다.
파일 전체가 리틀 엔디언이며 엔진의 메모리 내 구조체를 그대로 옮긴 것이므로, 유닛 레코드에는 초기화되지 않은 패딩 필드가 여럿 들어 있습니다.
헤더
| 오프셋 | 형식 | 필드 | 비고 |
|---|---|---|---|
0x00 |
uint32 |
Signature | 0x3F800000 — IEEE-754 1.0f 표식 |
0x04 |
uint32 |
Unknown1 | 정식 출시된 맵 하나를 빼고는 모두 0 |
0x08 |
uint32 |
UnitCount | 뒤따르는 배치 유닛 레코드의 수 |
abnar's terrace.crt(빈 스텁)의 시작:
00000000: 00 00 80 3f 00 00 00 00 00 00 00 00 09 00 00 00
1.0f, Unknown1 = 0, UnitCount = 0, 그다음 플레이어 수 9.
유닛 레코드(각 568바이트)
| 오프셋 | 형식 | 필드 |
|---|---|---|
+0 |
char[256] |
UnitType(널 종료) |
+256 |
char[256] |
UniqueName(보통 비어 있음) |
+512 |
uint32 |
X(월드) |
+516 |
uint32 |
Y(높이 축) |
+520 |
uint32 |
Z(월드) |
+524 |
uint32 |
PlayerId(0부터 시작) |
+528 |
uint32 |
HealthPercent(보통 100) |
+532 |
uint32 |
ArmorPercent(보통 100) |
+536 |
uint32 |
WeaponPercent(보통 100) |
+540 |
uint32 |
Angle(보통 180) |
+544 |
uint32 |
Veteran |
+548 |
uint32 |
알 수 없음 |
+552 |
uint32 |
알 수 없음 |
+556 |
uint32 |
FootprintX |
+560 |
uint32 |
FootprintZ |
+564 |
uint32 |
알 수 없음 |
플레이어, 규칙, 조건, 동작
유닛 뒤에는 uint32 플레이어 수(항상 9)가 오고, 그 수만큼 플레이어 블록이
이어집니다. 각 플레이어 블록은 uint32 규칙 수와 그 뒤에 오는 그 수만큼의
규칙입니다. 규칙 하나는 다음과 같습니다.
uint32조건 수, 그 뒤 각 조건은uint32옵코드와 64바이트 인수 슬롯 다섯 개;uint32동작 수, 그 뒤 각 동작은uint32옵코드와 64바이트 인수 슬롯 다섯 개.
트리거
맨 끝의 uint32 트리거 수 뒤에 그 수만큼 트리거 레코드가 이어집니다. 각
레코드는 256바이트 널 종료 이름과, 맵 칸 단위의 uint32 경계값 네 개 —
Left, Top, Right, Bottom — 입니다. 이름은 사람이 읽을 수 있는
형태입니다(The Hill, Player 1).
도구
kbot crt describe <map.crt>는 유닛 종류별 개수, 규칙 수, 트리거 영역을
출력합니다. 전체 배치 목록을 보려면 --verbose를 붙이세요. MCP crt_describe
도구는 같은 데이터를 JSON으로 돌려줍니다.
파일 하나(cairbray coast landing.crt)는 헤더가 밀려 있고 손으로 고친
흔적이 있어 이 구조와 맞지 않습니다. 파서는 이 파일을 잘못 디코딩하는 대신
분명한 오류와 함께 거부합니다.
.txt — 자유 텍스트 설명
로비에서 쓰는 한 줄짜리 ASCII 문자열입니다. 예:
abnar's terrace.txt: 9 x 7 4 Player 32MB(.ota의
missiondescription에 들어 있는 것과 같은 문자열). 제작 도구가 내보내기 과정에서
둘 다 썼을 것으로 보이며, 게임은 .ota의 값을 표시하는 것으로 보입니다.
현재 TA:K 맵을 다루는 방법
실제로 쓸 수 있는 방법을 수고가 적은 것부터 순서대로 소개합니다.
- 자유롭게 살펴보기 —
.kmp파일에 대한kbot hpi list/info/extract는 별다른 설정 없이 동작합니다..ota와.txt는 일반 텍스트입니다. - 맵 설명 보기 —
kbot tnt describe <map.tnt>는 TA:K 헤더, 왕국, 높이 맵/지형 크기, 지형지물 분류별 개수를 보여 줍니다. - 미니맵 렌더링 —
kbot tnt minimap <map.tnt> -t mini.png는 내장된 126 × 126 미리 보기를 트루컬러로 내보내며, 같은 이름의.ota에서 왕국 팔레트를 자동으로 고릅니다(--kingdom으로 바꿀 수 있음, 위의 팔레트 설명 참고). - 텍스처를 입힌 전체 맵 렌더링 —
kbot tnt preview <map.tnt> --vfs <flattened-tak-root> -t map.png는 설치본의terrain/*.jpg로 지형에 텍스처를 입히고 모든 지형지물의 GAF 스프라이트를 합성해, 게임이 렌더링하는 그대로의 맵을 만듭니다(athri cay → 7680 × 7680). 지형지물 스프라이트 팔레트는palettes/<kingdom>_features.pcx에서 읽습니다. - 높이 맵 렌더링 —
kbot tnt image <map.tnt> -t height.png는 외부 에셋이 필요 없습니다. 회색조 DataUnit 높이 맵을 출력합니다(--features는 분류별 색상의 배치 표시를 겹쳐 그림). - 캠페인 아카이브 다시 패키징 —
kbot hpi pack은 현재 v1만 만들므로, 다시 패키징한.kmp는 로드에 실패합니다. kbot이 v2 쓰기를 지원할 때까지는.kmp를 추출해 낱개 파일을 고친 다음, v2를 지원하는 서드파티 패커를 써서 손으로 편집하세요.
Warning
TA: Kingdoms는 고친 맵을 포함해 고친 파일 대부분을 로드하려면
-disablecavedogverification이 필요합니다. 이것이 없으면 엔진은 해시가
허용 목록에 없는 모든 것을 거부합니다. 모든 모드의 설치 안내에 이 내용을
적어 두세요.
현재 kbot이 TA:K 맵에 대해 할 수 있는 것과 없는 것
| 작업 | 상태 |
|---|---|
.kmp(HPI v2) 헤더 + 파일 목록 읽기 |
✅ |
.kmp에서 파일 추출 |
✅ |
.ota 파싱(TDF 형식) |
✅ |
TA:K .tnt 헤더 읽기(kbot tnt describe) |
✅ |
TA:K .tnt 미니맵 내보내기(kbot tnt minimap) |
✅(트루컬러 — .ota에 따른 왕국별 팔레트) |
텍스처를 입힌 TA:K 전체 맵 렌더링(kbot tnt preview --vfs) |
✅(텍스처 매핑 지형 + 지형지물 GAF 스프라이트) |
TA:K 높이 맵 렌더링(kbot tnt image) |
✅(자체 완결 회색조, --features는 표시를 겹쳐 그림) |
TA:K .tnt 쓰기(kbot tnt pack) |
❌ |
.kmp 쓰기(HPI v2 패키징) |
❌ |
❌ 항목에 대한 풀 리퀘스트는 언제든 환영합니다. 높이 맵, 텍스처 매핑 그리드,
지형지물 그리드, 지형지물 테이블이 모두 디코딩되었으므로 지형과 지형지물은 원래
해상도로 렌더링됩니다. 남은 공백은 편집한 TA:K 맵을 왕복 변환하기 위한 쓰기
경로와 v2 .kmp 패키징입니다.