2006.06.25
No.009 パラメータテーブルを利用したサーチ

はじめに
多くのゲームは講座No.003の最大値比較(99999999のような値を参照する処理を探す)でサーチでき、
DSエミュレータでプレイ可能なゲームはもっと簡単に講座No.004のような方法でサーチできます。
しかし、それらの方法では「最大値比較も無い、エミュレータでもサーチできない、特徴的な数値も無い……」
そういったパラメータに対しては全く太刀打ちできません。

大合奏!バンドブラザーズ
さて、このゲームは現時点(2006/06/24現在)ではまともにエミュレータで動作しません。
一応、画面が化けてはいるものの進行だけはするので、どうにか「導火線」はサーチできるものの、
このゲームで重要なパラメータは「点数」であり、導火線が燃え尽きなくなったとしても、
クリア点数を満たさない限りは先に進むことができません。
頑張って演奏終了画面まで進行しても画面が化けているせいで点数を見ることはできません。

つまり、単純にエミュレータとhasteDSの組み合わせだけでは、クリア点数をサーチできません。
バンブラ化けまくり

そこでパラメータテーブルを利用します。

パラメータテーブルとは?
例えば、RPGでゲームを始めたとき「HP100 MP20 攻撃力19 防御力15 すばやさ9」
というステータスだった場合に「これらの値は予めプログラム内で定義されている」と考えられ、
これらのパラメータは大半が同じ場所に密集している可能性が高いです。

上記の例の場合、メモリ上では 63 00 14 00 13 00 0F 00 09 00 と並んでいると考えられますね。
(全ての値をハーフワードと仮定。ゲームによってワードだったりバイトだったりします)。
これらのブロックになっているものを「テーブル」と呼称しています。

さて、バンドブラザーズの場合、楽譜を手に入れるための「ノルマ」という値があり、
アマチュアの初回は300点満点中200点で合格、中盤は300点満点中240点
最終難関はなんと300点満点中297点でクリアという強烈な難易度になっています。
(最後のスタッフロールを自力で見た人、ほとんど居ないのでは……)。

ノルマ値「200、240、240、240、240........297」
…つまり「このノルマ値そのものがテーブルになっているのではないか?」と考えられます。

テーブルの位置を効率よく特定する
さて、テーブルになっていると仮定したところで、特徴的な値を元に場所を特定します。
200や240はよくある値のため、他の要素に使用されている可能性が高く、サーチには向きません。
ここで着目すべきは最終難関の 297 (16進数129h) でしょう。

値がバイト255(0xFF)以上なので、ハーフワード(0x0129h)もしくはワード(0x00000129h)
であることは間違いありません。
で、値を探すときは「ワード(4桁)→ハーフワード(2桁)→バイト(1桁)」の順で探します。
これは、桁数が多いほうが絞り込める確率が高い(該当数が少ない)ためですね。
特に任天堂のゲームは大半のパラメータをワード型で定義する傾向にあるので、
ワードから探すのは効果的です。
というわけで、129hをワードでリトルエンディアン変換すると 29 01 00 00 となります。

さて、この値をバイナリエディタで検索すると…
 
-OFFSET- +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F
0011BD60 C8 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00 00
0011BD70 F0 00 00 00 F0 00 00 00 F0 00 00 00 F0 00 00 00
0011BD80 F0 00 00 00 F0 00 00 00 F0 00 00 00 29 01 00 00
こんな感じの場所にヒットします。
さらに、オフセット0011BD60のC8は10進数200、間に並ぶF0は10進数240……これは確定でしょう。

後はこのノルマ値を 00000000 で埋めてやれば、クリアに必要なスコアが0点になり、
どんなヘタクソな演奏でもクリアできるようになる、と考えられます。
これを元に改造コードを作ってみましょう。

オフセット→アドレス変換
NDSROMのオフセットとNDS実機のアドレスは異なりますので、オフセット→アドレス変換を施す必要があります。
ここでは拙作のDS逆アセンブラndsdis2のヘッダ出力を利用しましょう。
「-7 -9」オプションを付けると逆アセンブルを行わず、ヘッダだけを表示できます。

>ndsdis2 -7 -9 ABBJ.nds

ARM9_source            : 00004000
ARM9_execute address   : 02000800
ARM9_copy to address   : 02000000
ARM9_binary size       : 00129c38
ARM7_source            : 0012de00
ARM7_execute address   : 02380000
ARM7_copy to address   : 02380000
ARM7_binary size       : 0002619c
バンドブラザーズのヘッダからアドレス定義値周辺を抜粋したものですが、
これらの値から、NDSROMのオフセット4000〜12DC38(+00129c38)がARM9コードエリアで、
オフセット0012DE00〜00153F9C(+0002619c)がARM7コードエリアであることがわかります。
よって、先ほどテーブルがヒットしたオフセット0011BD60〜0011BD8FはARM9コードエリアに
格納されているということになります。
変換の計算式は ARM9_copy to address - ARM9_source + OFFSET なので、
02000000 - 00004000 + 0011BD60 = 2117D60 〜 2117D8F
となります。
(基本的には、ARM9領域であればオフセット+0x1FFC000と考えて良いでしょう)。
後は、この範囲を00000000で埋める改造コードを作ればOKです。

@マスターコード
C0000000 023FF000
F20036E4 E12FFF1E

@クリアノルマ点数を全ステージ『0点』にする(どんな酷い演奏でもクリア)
42117D60 00000000
2000000C 00000004

これで1曲演奏するだけで強制クリアになれば成功です。

さいごに
今回の方法は様々なゲームに応用することができます。
例えば、マリオで敵を連続で踏んだときの 100 200 400 800 1000 2000 4000 8000 1UP!
ですら、実はテーブルで管理されています。
こういったテーブルを1つ見つけておけば、後は芋づる式にずるずると格納アドレスを
探すことができますので、是非マスターしておきましょう。