マップデータを解析する(5):まとめ
前回のラストで、ZANACの面進行データの場所が判りました。
今回はより掘り下げてレポートし、ZANACのマップ表示の仕組みについてまとめます。


MapDataDecoder
今回のレポートはプログラムの紹介から始めます。

  mapdecode.exe(mapdecode.lzh : ファイルサイズ 73,012バイト)

LZH形式で圧縮された、Windowsの実行ファイルです。プログラムはhspで作成しました。
展開後、生成されたmapdecode.exeをzanac.romと同一のフォルダに移動し、実行してください。


mapdecodeは、ZANACのROMファイルからマップデータを読み込んで表示するプログラムで、ほぼ正確なマップを表示できると思います。
というのは、ゲーム中の全マップと突き合わせての確認は行っていないからです。もし、おかしな部分に気づかれたらぜひ教えてください。

右の「自動スクロールON/OFF」ボタンでスクロールを進めたり止めたりできます。ボス画面では強制的に止まります。
また、「一行上」ボタンで一行づつ画面を更新することもできます。

mapviewと同様、パターンジェネレータの内容もいつでも変更できます。 ただし、パターンジェネレータ変更は自動で行われるので、あまり役に立たない機能です。

また、0〜8の数値を入力してからワープ用ボタンを押せば、好きな面に飛ぶこともできます。 「WarpWait」値は、ワープ時の画面エフェクト用の速さを示し、大きいほどエフェクトがゆっくりになります。 実際のゲーム中のエフェクトと合わせてみるといいかもしれません。マップデータ解読には全く関係ないですが。


面進行データの調査は、このプログラムを作成しながら行ってみました。
プログラムで表示できないデータがあったら、その都度プログラムに新しいデータを解釈する機能を追加するという手法です。
以下、面進行データとZANACのマップデータ展開ルーチンについて、プログラムを作りながら気づいた点についてレポートします。


マップ表示の基本とマップデータの形式
マップデータの展開・表示を行う仕組みはとても単純です。

  (1)面進行データから2バイト読み出す(インデックス=データ列用y座標値の読み出し)。
  (2)前の(1)で読み出したデータと、「現在のマップのy座標」を比較し、一致したらその次のデータ列を読み出し、そのデータにあわせて面進行処理。
    もし一致していなければ、画面を一行スクロールさせ、「現在のマップのy座標」を+1し、(1)に戻る。

前回で少し触れましたが、面進行データは、「面進行データ列と、そのデータのインデックス」の組が集まって構成されています。
インデックスについては、1面初回のイコンのy座標の表示位置と等しかったことから、その差分/増分データの処理場所を表すy座標値であるとも言えます。

つまり、面進行データはこのようなデータの集まりとなります。

  [y座標値(2バイト)]  [現在のy座標がその値になったら処理したいデータ列]
  [y座標値(2バイト)]  [現在のy座標がその値になったら処理したいデータ列]
  [y座標値(2バイト)]  [現在のy座標がその値になったら処理したいデータ列]
  [y座標値(2バイト)]  [現在のy座標がその値になったら処理したいデータ列]
           ・
           ・
           ・

データ列は、先頭の1バイトが処理の種別を示し、続くデータが各種処理に渡されます。
例えば、0x85で始まれば差分データ処理、0x82で始まれば境界データ処理を示します。

また他には、あるラウンドが終了し、次のラウンド用の面進行データにポインタを移したい場合に利用される、 「面進行データのジャンプ」データ列は、0x89で始まるデータ列なので、

  [y座標値(2バイト)]  0x89(データジャンプを示す1バイト) [ジャンプ先の面進行データアドレス下位(1バイト)] [上位(1バイト)]

の5バイトで構成されています。
前回のseqviewを実行でき、マップデータをテキストとして見ることができる方は、各面の最後に

ROUND END. jump to xxxx

というコメントがある行が「面進行データのジャンプ」を示しています。

さて、面進行データ中、最も重要なのがマップ描画関連の処理ですが、その概要を以下に説明します。


差分/境界描画用ワーク
ZANACのマップ表示ルーチンは、

「差分/境界データ描画用のワーク領域を複数用意し、スクロールに合わせて、デフォルトパターンに対して各ワークが差分/境界線を描画する」

という感じの動作をしています。

解析を始めた当初のプログラムは、面進行データのうち0x85で始まる差分データと、0x82で始まる境界データのみを扱ってマップを表示させてみました。

前回「マップデータ形式を調査」のところで触れましたが、差分データや境界データを参照しているデータ部の先頭に、 不明な数値n1がありました。この数値は、「差分/境界データ描画用に、どのワークを使うか?」を示す「ワーク番号」でした。

ワークの内容は、マップスクロール時に新しい1行を作成するときにチェックされ、登録済みワークがあった場合、その内容を新しい行に反映して表示します。

なお、本物のZANACでは、ワーク領域は

  ・境界データ描画用に、8バイト×4つ
  ・差分データ描画用に、4バイト×8つ

の計12個、用意されているようです。



ワーク処理順位
ワーク番号は、描画時の優先順位にも関係します。小さい番号ほど、先に処理されます。後に処理されるデータは、先に描画された内容を上描きします。 また、境界データは、差分データより優先して描画されます。
例えばマップに「川」を描画したい場合には、

  (1)あるワークに、川の左岸を表す「陸地と水面」の境界データを登録
  (2)前の(1)で登録したワークより後のワークに、川の右岸を表す「水面と陸地」の境界データを登録

という形でワークを登録してやる必要があります。



大型地上物(ボス画面)の描画とワークの働き
マップデータ展開プログラムで次につまづいたのが、ボス画面の処理でした。
0x8Bで始まるデータ列で、恐らく戦闘の制限時間(00になるとスクロール開始)、破壊時スコアのデータ、およびボス描画の為の 差分データ、という形で格納されていると思っていたのですが、データ列中には、差分データへのポインタは含まれていませんでした。
その代わりに「ボス用データ」とも言うべきデータへのポインタが含まれていました。

1面の初めのボス(小さい一つ目のやつ)のボス用データには、確かに差分データへのポインタが含まれていました・・・が、それ以外にも各種データが含まれていました。
恐らく、キャラクタ種別などの、敵キャラクタとして登録する為のデータは、ここに含まれているのでしょう。

しかし、1面の2番目のボス(1面の初めのボスが集まり、計4つのコアを持つ。)のデータを見ると、2面のボスを描画する為の、差分データへのポインタがやはり存在しません。

トレースデータ等と格闘して調査するうち、驚くべきことに気づきました。

ボスを描画する際、一つのワークが言わば「司令塔」となり、他のワークにボスの各部の差分データを次々と渡していたのです。

また、司令塔から呼び出されるワークは、「大きい番号のワークによる描画は、小さい番号のワークによる描画結果に上書きされる」というルールについても完全に考慮されていました。



4つのコアを持つボスを描くのに、全体の差分データが用意されている・・・という考えは浅はかでした。
この方式であれば、かなりのデータ節約となるはずです。
しかし、反面、この形式のデータを作成する際には、ワークの優先度を考慮しなければならず、相当な苦労があったのではないでしょうか。


境界が消える 〜 x座標値の調整について 〜
今まで特に触れませんでしたが、ZANACのマップデータ展開には、もう一つ、特色があります。
それは、「画面に見えない左8パターン分を常に生成している」という点です。

面進行データ中の差分データや境界データには、「データをどのx座標に描画するか」というデータも含まれています。
しかし、解析を始めた当初気づいたのですが、不思議なことにどのx座標値も8だけ大きい値が格納されていたのです。

初めのうちは、「まあ、ZANACのプログラマにとっては、常人には計り知れない理由があったのだろう。では、全ての座標値から8を引いておこうっと」
と単純に考えてプログラムを作っていたのですが、ある境界データの処理の際、ハマることとなってしまいました。
境界線(川、もしくは草原と茶色い地面の切れ目など)が、左に曲がりながら画面左端に消えてゆく際、どうしても実機のように描画できなかったのです。

理由としては、ワーク破棄のタイミングにありました。

境界線を描いているワークについて、画面左端を越えてしまった(x座標値がマイナスになった)ワークについては破棄する、という処理を組んだのですが、どうしても実機の通りに描画できません。
「これじゃあ画面の左端に消えた後も、ずっと境界線を描いていることになるなあ」と思って調べたところ、実機では本当に描いていました。
その幅は8パターン分で、ちょうどx座標値のずれである8と一致していました。

このプログラムでは、相当ややこしい処理を行って、なんとか実機の通りの表示(もちろん、チェックを行った限りですが)を行えるようになりましたが、 本物のZANACでは、スピード、および簡略さの為に、あえて無駄な左8パターン分を描画しているようです。
画面左端の方向に8パターン分進んだ位置を x=0 とし、画面の左端は x=8 としています。

「ZANACの無駄と思える部分、不思議な部分には理由がある」ということを痛感しました。
これだけのコンパクトなプログラムであれば、それも当然と言うところでしょう。


マップデータ解析のまとめ
以上で、ZANACのマップデータ解析についてはほぼ完了とします。
というか、そっくりの動作をするプログラムを作ったんだから、いいってことにしてくれませんか・・・ね?ね?

まあ、細かい話では、マップパターンのパレットに一部おかしな点がありますが、おそらくデフォルトパターンの変更のタイミングでパレット変更を行っているのではないか?と思っています。
でも、ZANACのマップ(凄まじい難易度で、ゆっくり見ているヒマも無いような0面も)を、ほぼ表示できたということで、個人的には大変満足しています。

昔、ファミ通かなにか、ファミコンの雑誌で読んだ覚えがあったのですが、当時のファミコン雑誌には、「完全マップ付き!」という名のもとに、 ゲームの画面を一枚一枚写真に撮り(!)、それらを張り合わせて、ゲームのマップを掲載する記事が流行っていたのですが、 ファミコン版ZANACについては、面のあまりの長さから、完全マップは掲載できなかった、ということでした。
ファミコン版とMSX版の違いこそあれ、ZANACのデータ格納手法・効率の素晴らしさを示す逸話ではないでしょうか。



ダラダラと引っ張りやがって、長いだけで中身の無い文章よ!と憤りつつ、ZANAC解析のトップへ戻る

トップへ戻る