TA: Kingdomsのマップ — .tnt + .ota + .crt + .kmp
TA: Kingdomsのマップは、1つか2つのメタデータファイルを伴った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 | マップの地形データ(TA:K固有のIDVersionワードを持つ、TNTの形をしたバイナリ) |
.ota |
55 | マップのメタデータ。TAの.otaと同じ形のTDF形式で、TAK固有のフィールドがある |
.txt |
55 | マップの自由記述の説明文(数語。ロビーに表示される) |
.crt |
33 | シナリオのサイドカー。事前配置のユニット、プレイヤーごとのルールエンジン、名前付きのトリガー領域 |
.tdf |
6 | マップごとのゲームプレイ調整ファイル(キャンペーンの調整、AIへの指示) |
典型的なマルチプレイ用のマップは.tnt、.ota、.txtの3つ組で出荷され、.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の16進ダンプです。
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という値です(TAのTNTは0x00002000を使います)。コンテナは今でもヘッダーと、ポインターで位置を示すセクションの並びですが、TA:Kはタイルモザイクの形式ではまったくありません。TAがタイルインデックスのグリッドと32×32のタイル画像の集合を格納するのに対し、TA:Kは地形を、外部のJPGテクスチャを参照するテクスチャマッピングのグリッドとして格納し、それとは別にハイトマップと地形物の配置グリッドを持ちます。以下はすべて、出荷された55のマップで検証済みです。
Width/Heightはピクセル数ではなく、16pxのDataUnitの数です。athri cayのような「15 × 15」のマップはWidth = Height = 480のDataUnitを格納しており、一辺は480 × 16 = 7680pxになります。ハイトマップと地形物グリッドはDataUnitごとにサンプリングされます。地形はそれより粗い32pxのGraphic Unitグリッドでサンプリングされ、これは各軸でDataUnitのグリッドのちょうど半分です(guW = Width/2)。- ヘッダーは連続する12個の
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ごとに高さ1バイト |
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ごとに1バイトで、カラーのビットマップではなく、グレースケールの標高データです。地形はテクスチャマッピングによって再構成されます。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のボイドバイトを使います。
Note
現在TA:KのTNTでできること。kbot tnt describeは、ヘッダー、王国、ハイトマップと地形の寸法、地形物の内訳を報告します。kbot tnt preview --vfs <root>はフル解像度のテクスチャマッピングされた地形を描画し、その上に各地形物のGAFスプライトをホットスポットに合わせて合成します。ゲームが描くとおりの、完全なマップです。kbot tnt imageは、それ単体で完結するグレースケールのハイトマップを描画します(外部のテクスチャは不要で、--featuresを付けるとカテゴリ別に色分けしたマーカーを重ねます)。kbot tnt minimapは、埋め込まれた126 × 126のプレビューを書き出します。
ミニマップのパレット — 王国ごとに1つ
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] |
スキーマのサブセクションを1つのブロックに対応させたもの(TA:Kはマップごとにスキーマを1つしか出荷していません)。 |
それ以外(StartPos1..N、aiprofile=など)は、TAとまったく同じように働きます。kbot mountのOTAビューアーは、変更なしで両方を扱えます。
.kmp — キャンペーンミッションのHPI v2アーカイブ
.kmpは、名前を変えたHPI v2アーカイブです。adamantine gate.kmpの先頭の16進ダンプです。
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とキャンペーンの目標を動かすプレイヤーごとのルールエンジン、そしてそれらのルールが参照する名前付きの長方形のトリガー領域です。マルチプレイ用のマップには空のスタブが付いています(ユニットなし、空のプレイヤースロットが9つ、トリガーなしで56バイト)。キャンペーンや特別なマップでは全セクションが埋まっており、大きさは最大で約120 KBになります。
ファイル全体がリトルエンディアンで、エンジンのメモリ上の構造体をそのまま写したものです。そのため、ユニットのレコードには、初期化されていない埋め草のフィールドがいくつか含まれます。
ヘッダー
| オフセット | 型 | フィールド | 説明 |
|---|---|---|---|
0x00 |
uint32 |
Signature | 0x3F800000。IEEE-754の1.0fによるマーカー |
0x04 |
uint32 |
Unknown1 | 出荷されたマップでは、1つを除いてすべて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(NUL終端) |
+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バイトの引数スロット5つからなります。uint32のアクション数。その後の各アクションは、uint32のオペコードと64バイトの引数スロット5つからなります。
トリガー
末尾にあるuint32のトリガー数の後に、その数だけトリガーのレコードが続きます。各レコードは、256バイトのNUL終端の名前と、マップのマス単位で表した4つのuint32の境界(Left、Top、Right、Bottom)からなります。名前は人が読める文字列です(The Hill、Player 1)。
ツール
kbot crt describe <map.crt>は、ユニットの種類の内訳、ルールの数、トリガー領域を表示します。配置の全一覧を見るには--verboseを付けます。MCPのcrt_describeツールは、同じデータをJSONで返します。
1つのファイル(cairbray coast landing.crt)だけは、手で編集されてずれたヘッダーを持っており、この配置に合いません。パーサーはこれを誤って復号するのではなく、わかりやすいエラーで拒否します。
.txt — 自由記述の説明文
ロビーで使われる1行の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が必要です。変更したマップも含まれます。これがないと、エンジンはハッシュが許可リストにないものをすべて拒否します。どのMODでも、インストール手順にこのことを書いておいてください。
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へまとめる処理です。