ポケモンの不具合を解明したい?質問する

ポケモンの不具合を解明したい?質問する

(この質問が不適切な場所であればお詫び申し上げます。これは間違いなくプログラミング関連だと思いますが、他のサイトに属する質問であればお知らせください)

私はポケモン赤と青をプレイして育ちました。このゲームはとても楽しいのですが、悪用可能なバグが多数あることで悪名高いです(例えば、この馬鹿げたゲームのスピードランメモリ破損を利用してアイテム画面を 16 進エディターに変換するもの)。

最近、このゲームの興味深いスピードランを見つけました。「ZZAZZグリッチ」と呼ばれるグリッチを使用して重要なメモリ位置を破壊し、プレイヤーがほぼ即座にゲームに勝つことができるというものです。作者によるスピードランの説明ZZAZZ グリッチは次のように動作します。

トレーナーバトルを開始するには、ゲームは [...] 負けた場合に譲るお金など、多くのデータを読み込む必要があります。お金を読み込むと、事態はひどく悪化します。理由はわかりませんが、お金はまったく異なる方法で保存されます。ゲームは 3 バイトのデータ構造を使用し、値をバイナリに変換する代わりに「人間」表現で保存します。たとえば、$123456 は、適切な変換である 0x01E240 ではなく、0x123456 として保存されます。

[トレーナー テーブル内の一部の無効なエントリ] は、無効なお金データのある場所を指しています。ゲームが上記の構造内のこれらのデータを使用して演算を実行しようとすると、ゲームは異常動作を起こし、RAM の大部分を上書きし始めます。具体的には、3 バイトのブロックごとに、2 つに 0x9999 (トレーナーが与えることができる最大の金額) が含まれます。このパターンは、RAM を通じて何度も繰り返されます。これをよりよく確認するには、ZZAZZ トレーナーに直面した後、エミュレーターでビデオを一時停止し、VBA のメモリ ビューアーを 0xD070 に設定することをお勧めします。

この分析は理にかなっています。しかし、プログラマーである私は、プログラマーが一体どうやってこれを可能にするコードを書いたのか疑問に思わずにはいられません。入力が有効な 16 進数でエンコードされた 10 進数でない場合、16 進数でエンコードされた 10 進数を 10 進数に変換する関数を作成するために私が思いつくアプローチでは、メモリのランダム ブロックを 0x9999 で埋め始めることはありません。

私の質問は、このように失敗するアルゴリズムを特別に設計しなくても、無効な値が入力されたときにこのようなメモリ破損を引き起こす可能性のある、16 進コード化 10 進数から 10 進数への変換の簡単な実装はありますか?

繰り返しになりますが、これが話題から外れていたら申し訳ありません。このサイトの他のプログラマーもこのゲームで育った可能性があり、このような不具合がどのようにして起こり得るのかを解明することは、リバースエンジニアリングの興味深い演習のように思われます。

ベストアンサー1

正直に言うと、これは最初のゲームをターゲット通りに書いた人が作った、ただの愚かでひどい不具合だと思います。ポケットモンスター 赤/青はシリーズの最初の作品で、任天堂が通常ロットテストから除外するような不具合が他にもたくさんあったので、どうやって通過したのか不思議です。スクロール画面のシフトの問題は、私を悩ませているものです。とにかく、彼らが何を考えていたのかは誰にもわかりません。おそらく、この領域はスクリプト経由で書き込まれたため、異なる方法で保存されたのでしょう。おそらく、ビットパターンは0x0101メモリが解放されたことを示すために使用され、そのコードは奇妙な場所で誤っておかしな動作をします。Z80 コードを詳しく調べて、そのプラットフォームでの自分のゲーム開発時代を追体験することもできますが、まあ、どうでもいいことです。彼らが何を考えていたのかを解読するのは、あまりにも手間がかかりすぎます。

確かに大金を稼いだけど…

編集1:

わかりました。賞金をあげました。もう少し時間をかけてメモリを調べて、ちょっとした情報を見つけました。GBC/DMG には、DAA. Decimal Adjust Accumulator (A) と呼ばれるオペコードがあります。これは、アキュムレータ内の値をBCDフォーマットに変換します。メモリ内の表示されている領域は、すでにBCDフォーマットされています。http://en.wikipedia.org/wiki/バイナリコード化10進数

4 年ほどゲーム用に Z80 アセンブラを手作業でコーディングしていたとき、この命令コードが必要になったことは一度もなく、スコアを表示するために作った野球ゲームで 1 回だけ使用されていたのを目にしたことがあります。1 サイクルの演算命令ですが、通常のコーディングでこれをうまく使う方法はまったく見つかりませんでした。うーん。実は任天堂の DMG 技術文書はまだ持っています。考えてみてください ;) とにかく、奇妙な方法で多数のフラグをいじる以外は、これについても特に面白いことはありません。

私の推測では、テーブルはフォーマットされていると想定さBCDれています。そのフォーマット外に変更すると、内部の計算が極端に狂ってしまいます。キャリー フラグとゼロ フラグが、本来は設定されるべきでないときに設定されます。これにより、1 つの列から次の列へのオーバーフローが発生し、非常に大きな数値が計算されます。この領域を読み取る問題のオペコードを直接確認しないと、確かなことは言えませんが、私の推測では、計算の完了時にキャリーがまだ設定されている場合はBCD、負の値または範囲外の値を格納するのではなく、最大値を設定するという包括的なチェックがここにあると思います。またはDAA、ガベージ データを受信したときに0x99その戻り値を返す命令ですが、これについてはあまり確信がありません。

お役に立てれば...

おすすめ記事