処理解析奮闘記 FFTA編

テーマ:

はじめに

この記事はPokémon RNG Advent Calendar 2017 4日目の記事です。

なおタイトルの通り、ポケモンではないゲームの乱数処理についての記事ですのでご了承ください。

 

FFTAって?

正式タイトルはFINAL FANTASY TACTICS ADVANCE。2003年に発売されたGBA用ソフトです。

ジャンルはシミュレーションRPG(「リルルと鉄人兵団」みたいな戦闘システムです)。

WiiUバーチャルコンソールで配信されているみたいなので、ぜひプレイしてみてください。

 

動機

1. 3世代のポケモンの処理はあらかた調べつくしてしまったし、誰も手を付けていないようなゲームの乱数処理を1から解析してみたかった。

2. FFTAのユニットの加入時の能力とレベルアップによる能力上昇にはランダム要素があるらしい。乱数調整を駆使して主人公のステータスを極めてみたい。

 

準備したもの

・FFTAのソフト…これが無いと遊べませんね。

・コードフリーク…ROMの吸い出しに使用。解析やROMHackで遊ぶときは必ず自分でデータを用意しましょう。

・VBA-lua…エミュレータ。Luaスクリプトを走らせられるやつ。

・Disarm…逆アセンブラ。

・Giga Text Viewer…逆汗したROMを読むために。

・Stirling…バイナリエディタ。処理を書き換えたりするために。

・時間…暇な時間とやる気が無いとやってられません。間違っても試験前にやるものではない。

 

Step.0 ネットで調べてみる

「誰も手を付けていないような」とは書いたものの、既に処理が割れていたら面白くありません。

「FFTA 乱数」等のキーワードで検索。

見つからない。

まぁそうですよね。安心して解析に移りました。

 

Step.1 目星をつけて調べてみる

「GBAのゲームなんだからMTとか使われてるわけないし、いつものLCGでしょ」という安易な予想を立ててStirlingで検索。

乗法定数0x41c64e6dで検索をかけて、ひっかかれば儲けもの。

 

ありました。

しかもその次の値は0x3039。どうやらFFTAで使われている擬似乱数は

S[n+1] = 0x41c64e6d*S[n] + 0x3039 & 0x7FFFFFFF

の標準的なLCGのようです。

 

 

 

 

 

…いえいえ、これでようやくスタート地点です。

 

Step.2 LCGを回す処理を特定する

使われている擬似乱数の種類が特定できたら、次はLCG関数の特定作業です。

これを探し当てないと、個々の処理を調べるのがとても困難です。

逆にこれさえ見つければLCG関数呼び出しにブレイクを仕掛けることで乱数を使う処理の特定が非常に楽になります。

逆アセンブルしたテキストファイルをGiga Text Viewerで読み込み、先ほど0x41c64e6dを見つけた付近の処理を読んでみましょう。

"ldr r0 , [pc, #16](&00002860)"は「r0に$00002860の値を読み込んでね」という命令です。

この命令によってr0に0x41c64e6dが格納されます。その下の"ldr r1 , [pc, #16](&00002864)"ではr1に0x3039が格納されています。

"mul r0, r1"は「r0にr1の値をかけてね」という命令、"add r0, r0, r1"は「r0にr0+r1の値を格納してね (=r0にr1の値を足してね)」という命令なので、ここがLCG関数と思ってよさそうですね。

ということは、その上の処理は

「seedを読み込む処理」と見て間違いないでしょう。seedの格納されているアドレスは$0000285cの値、つまり

$030084b0のようです。

 

ここまでをまとめると、

・00002848から始まる処理でLCGを回している。

・030084b0にseedが格納されている。

ということがわかりました。

 

Step.3 エミュで確かめてみる

VBA-luaの出番です。Luaスクリプトはポケモン用のやつにちょちょっと手を加えてみました。

スクリプトを走らせつつ、ゲームを起動してみた結果がこちら。


毎フレーム処理が行われていますし、遷移関数に従ってseedも動いています。

やったね!

 

Step.4 描画消費を止める

毎フレームごとの乱数消費は調整には欠かせませんが、解析するうえでは邪魔なことこのうえない厄介者です。そこで、この処理を消してしまいましょう。0x0000047bはLCG関数を抜けた後に戻るアドレスを示しているので、そこに書かれた処理を消してしまいましょう。

ここですね。

ここをStirlingでnop(何もしなくていいよ、という命令)に書き換え、もう一度ゲームを起動してみます。

見事に時が止まりました。(ただしOP画面では時間経過での消費が別途行われているようです)

ついでに、ゲーム起動後にもう一度seedが初期化されているっぽいこともわかりました。(ロゴが出終わってムービーに移るところです)

さらに進めるともう一回seedが初期化されていました。

「つづきから」を押したタイミングでもseedが0x1に初期化されているようです。タイミングが合わせやすくて大変都合がよろしい。

これ以降はセーブデータをロードするときなども初期化は行われていませんでした。

つまり乱数調整をするときは「つづきから」でSTARTボタンを押すのに合わせてエメタイマーを起動すれば良いようです。

 

Step.5 処理を調査する

時間経過による乱数消費を無くしたことで、ランダム処理の中で乱数が消費されるタイミングとアドレスが一発でわかるようになりました。

あとは調べたい処理をエミュ上で実行して調べるだけですね。

長くなるので個々の処理を調べた過程は省略しますが、その後無事にユニット加入時のステータス決定処理、レベルアップ時の成長ボーナス決定処理、交易品の決定処理を調べてツールを作り、乱数調整が可能なところまで持っていくことができました。

交易品乱数ツール

ユニット加入乱数ツール

Lv.UPボーナス乱数ツール

…と同時に、ステータスを極めるのが非常に困難であることも判明してしまいました。

ポケモンで言うところの「性格一致最速」程度の条件でも10万Fで1~2体しかいません。ステータス理論値をたたき出すのはあきらめたほうがよさそうです…。そもそもそんなに拘らなくてもクリア余裕ですし。。。二刀流で殴ってるだけでラスボス倒せますし。。。

 

おわりに

以上、FFTAの乱数処理を解析してみたお話でした。

乱数処理は読みやすく、見つけやすいので※要出典、個人的には解析を始めてみるのにちょうどいいと思います。興味を持った方はエミュ上で動かせる環境を自分で整えて挑戦してみてください。

Emピラミッドや戦闘乱数なんかはまだ整備されていないので、誰かが代わりにやってくれるととても助かります開拓する楽しさが味わえると思います。

実は交易品乱数を調べている途中で海外サイトに乱数処理がまとめられていたのを見つけてしまいました。なんだか悔しい。「FFTA RNG」で調べたら一番上に出てきてたよ。。。

 

5日目は@seed_6genさんの担当です。

 

 

穴が開くほど参考にさせていただいたページ

GBAFE逆汗講座