マップデータを解析する(4):境界データと面進行データ
前回までの調査で判った「差分データ」「境界パターンデータ」は、どんな順序で、いつ展開されるのか?
今回のレポートではその部分を調査してみます。
ZANACを本当に(つまりソースから)解析する
マップ生成の調査方法として、これまでは『ゲーム画面からデータ格納の仕組みを推測する』という手法を採っていましたが、
このへんで少し、アセンブリソースの解析についても触れたいと思います。
以前のレポートでも述べましたが、ZANACのパターン番号体系はASCIIコードに準拠している為、"ROUND"や"GAME OVER"などのゲーム中のメッセージが
判りやすい形で格納されています(mapview.exe)。
(1)ソース中、文字列データの位置を特定する。
(2)その文字列データを画面表示しているルーチンを調査する。
(3)そのルーチンを呼び出している処理を探し、今度はそれらを調査することで、スコアや自機を格納しているRAMアドレスが判明する
という流れで調査を進めるのが、最も効率が良いのではないでしょうか。
さて、ソースの解析が順調に進み、画面右の「現在のROUND数」の表示処理から、ROUND数が格納されているRAMのアドレスが
判明したとします。
そのアドレスについて処理を行っているルーチンで、
●ある変数A(2バイト長)について、アドレス945Cからのテーブル(9つの値を持つ)のいずれより下か?を判定して、ROUND数に8〜0の値を入れるルーチン
●ROUND数(8〜0)を元に、ある変数A(2バイト長)に、アドレス945Cからのテーブルの値のいずれかを代入するルーチン
という2つのルーチンが見つかりました。
このことから、
・アドレス945Cからのテーブルは、0面から8面までのマップデータの開始アドレス(2バイト長)を示す
・ある変数Aは、現在のマップデータ位置を示すポインタである
ということが判ります。
以下、アドレス945Cからのテーブルを「マップデータテーブル」、ある変数Aを「マップデータポインタ」と呼びます。
また、マップデータポインタで参照されるデータは、「マップデータ」として、ここからはマップデータの構成について調査してみます。
境界データ
マップデータテーブルの、1面のデータ開始位置を示す値(8番目、つまり+14,+15バイトの2バイト)を元に、
ソースの1面の面データと思われる部分を調べてみます。
例によって実際の内容は表記できないのですが、そのアドレスから+20バイトの辺りの2バイトは、どこかのアドレスを示しているような・・・
そうです、ここには差分データの回で調べた、「緑背景のイコン」の差分データの開始アドレスが格納されています。
しかも、2バイトほど隣にも、同じアドレスが見つかります。
これは、1面のマップ冒頭の、イコンが画面左右に1個ずつ、計2個並んでいるデータではないでしょうか?
マップデータを少し読み進めると、「茶色い小島」「茶色背景のイコン」「武器庫」「武器庫」の差分データ開始アドレスも見つかると思います。
とうやら、このデータは1面の差分アドレスの出現順序のデータと見て間違いないようです。
さて、境界パターンデータ(川)についてはどうでしょうか?
データの格納順序としては、この近辺にあるはずなのですが・・・
実は、このデータの中には、境界パターンデータを示すアドレスは含まれていません。
今発見した緑背景イコン×2と、茶色い小島のデータの間が不自然に開いているようですので、その中のアドレスっぽい値を探してみます。
9Exx(xx 9E)というアドレスが2つ見つかるので、さらにそのアドレスが指し示しているデータ近辺を調べてみます。
数バイト先に、まさに前回調べた、境界パターンデータの開始アドレスが含まれていました。
この、境界パターンデータの開始アドレスを含むデータを、「境界データ」と呼ぶことにします。
ZANACのマップにおいて、「緑の地表/水面」など、異なる地表の境界線は、この「境界データ」と、「境界パターンデータ」が組みになって表現されています。
また、前回調査した川のような地形は、これら「境界データ」「境界パターンデータ」の左岸&右岸のペアによって表現されているようです。
以上、マップデータとは少し離れてしまったようなので、ここからはさらにマップデータの調査を進めてみます。
マップデータ形式を調査
これまでの調査で、マップデータには「差分データのアドレス」「境界データのアドレス」が含まれていることがわかりました。
また、これらのデータが現れる順序も、ほぼゲーム中の画面に従っているようです。
もう少しマップデータの調査を進めてみます。
ROUND数の処理の所で出てきた「マップデータポインタ」に着目してみます。
マップデータポインタを操作しているルーチンを、ソースの中から探し出します。それほど多くはありません。
そのうちの一つのルーチンは、「マップデータポインタでマップデータを参照し、解釈する」ルーチンのはずです。
見当をつけてソースを読んでみますが、これがなかなか難しいです・・・
このレポートの中でもたびたび書いていますが、アセンブリソースの解析はどこが難しいのでしょうか。
まず第一に、『そのルーチンが呼び出される直前のレジスタ内容、メモリ内容を判断するのが難しいこと』。しかし、これはC言語など一般のプログラミング言語で言うところの、引数の内容ががよく判らない、というのと同じことでしょう。
第二に、こちらが重要なのですが、『変数名がわからない』こと。メモリ内容は名前ではなく、アドレスで管理されます。
他人の書いたソースファイルを読むとき、変数名から内容のあたりをつけてゆくことができない為、解析する場合、RAMアドレス-自分でつけた変数名の表を作成しながらでないと難しいでしょう
(というか、少なくとも私はそうしているのですが、ひょっとしたら一般的な方法ではないのでしょうか?)。
アセンブリ言語に比べ、VisualBasic、C言語などが「高級言語」と呼ばれる理由が身をもってわかります。しかし、これもアセンブリソースを解析すればこそ体験できる不便さで、まさに解析の醍醐味と言えるのではないでしょうか?
さて、面倒くさいソース解析は私が代わりにやらせていただきました。
あるルーチンの中で、「マップデータポインタから1バイト読み出し、その値の末尾4ビット(0〜0Fh)によって、0〜0Chの12通りに分岐する」という処理がありました。
0〜0Fhだと、本当は15通りなのですが、プログラムには12通りの処理しかなく、残り3通りは存在しません。データが間違いなく12通りしかない!ということで、プログラムの小型化を図っているのでしょう。
この分岐先のルーチンをさらに調べることで、マップデータの構造が明確になってゆきます。
例えば、「差分データ」については、初めの1バイトの末尾4ビットが"5"で始まるデータ列、
「境界データ」については、初めの1バイトの末尾4ビットが"2"で始まるデータ列で参照しているようです。
以下、差分データと境界データが、マップデータ内でどのような形で参照されているのかのフォーマットを示します。
<差分データ参照のデータ列>
0x85 nn <-----"0x85"の末尾は5、nnは差分データ個数を示す
n1 xx LL HH <-----(※n1&0x08=0のとき)
n1 xx yy LL HH <-----(※n1&0x08!=0のとき)
ただし、不明な数値n1、画面左からの表示位置xx、現在y位置からのyの差yy、差分データ格納アドレスHHLL
<境界データ参照のデータ列>
0x82 nn <-----"0x82"の末尾は2、nnは差分データ個数を示す
n1 xx bt LL HH <-----(※n1&0x08!=0のとき)
ただし、不明な数値n1、画面左からの表示位置xx、境界タイプ?bt、境界データ格納アドレスHHLL
実際のソースを見ると判りますが、マップ中の「川」は、2つの境界データ参照が組になって表現されています。
2つの境界データ参照の表示位置xxの差は川幅となります。
ここまできてやっと、ZANACのマップの川の表現方法が判明しました。
面進行データ
なお、マップデータを調べてゆくと、上記のようなデータ列(初めの1バイトの末尾4ビットが0〜0Ch)の合間合間に、2バイトのデータが含まれていることがわかりました。
初めは意味の無いデータなのか?とも思いましたが、トレースデータと読みあわせを行ううち、
・合間合間の2バイトデータは、「初めの1バイトの末尾4ビットが0〜0Chで始まるデータ列」1つにつき、1つ存在
・この合間合間データが同じ「データ列」は、続けて処理されている
ということがわかりました。もしや、と思い、
マップデータ中、1面の初めのイコンを現すデータ列の前の「合間合間データ」
と、
実際のゲーム中にイコンが表示されるのはマップの下端(ゲーム開始直後、スクロールする前の画面下端)からy方向にどれだけ離れているか?(イコンの全マップ中のy座標)
とを比較してみると、見事に一致しました。ちなみに、イコンのy座標は十進数で50(=032h)となります。
このことから、この合間合間データは、いわばデータ列に振られたインデックスで、ZANACマップの描画中、カウンタ(多分、マップのy座標のどこを描画中か?を示す)がインデックスと等しくなったとき、
そのデータ列がマップデータ描画に使用されるのではないか?と推測できます。
ここで、差分データ参照、境界データ参照以外のデータ列にも注目してみましょう。
あるデータ列は面の途中に表示されるボスを、あるデータ列は面データの終了を意味し、次面のデータ開始アドレスを示すようです。
また、これは後付けで判明したのですが、各面の特殊兵器コンテナの内容を出現順に示すデータ列へのポインタ等も存在します。
このことから、これらのデータ列の集合は、単純にマップ描画のみを担っているのではなく、ゲーム全体の進行に用いられているようです。
よって、このデータ列の集合を「面進行データ」と呼ぶことにしました。
以上、大変長くなりましたが、最後にプログラムの紹介です。
seqview.c(seqview.lzh : ファイルサイズ 7,114バイト)
(LZH形式で圧縮された、C言語ソースファイル、実行用バッチファイル、Cygwin上でコンパイルしたexeファイルです。
展開後、生成されたファイル全てをzanac.romと同一のフォルダに移動し、seqview.batを実行してください。
なお、展開後に生成されたseqview.exeは、Cygwinが無い環境では動かないかもしれません。
その場合は、seqview.cをコンパイル後、seqview.batの内容を参考に実行してください。)
プログラムを実行すると、round0.txt〜round8.txtまでの9つのテキストファイルが生成されます。
これらのファイルには、ROMファイルから読み出した、各面の面進行データの内容が記されています。
面進行データの中には、これまで説明していなかった"Set Default Map Pattern"や、"borderline data change"などの文字列があります。
これらのデータ列の意味はどうやって調べたのでしょうか?いえ、真面目に解析したわけではありません。
次回のレポートで説明し、それをもって第一次中間報告のまとめとします。
(今回は期待を持たせたカッコ良い終わり方にしました)
文字ばっかりで長い!疲れつつZANAC解析のトップへ戻る
トップへ戻る