グランディアV

キーワード
相対アドレス
レジスタ


内容
必殺技を使うと必ず秘訣を得る
コンボ選択で必ず必殺技を習得


必殺技を使うと必ず秘訣を得る

結構適当に見ていきましたが、まずはステータスの最大値検索をしベースとなる部分を見つけます

001df3a8 lw   v0, $004c(s1)
001df3ac addu  v0, v0, v1
001df3b0 sw   v0, $004c(s1)
001df3b4 lw   v0, $004c(s1)
001df3b8 bgez  v0, $001df3cc
001df3bc slti  at, v0, $2710
001df3c0 beq   zero, zero, $001df3dc
001df3c4 daddu  v0, zero, zero
001df3c8 slti  at, v0, $2710
001df3cc bne   at, zero, $001df3dc
001df3d0 nop
001df3d4 beq   zero, zero, $001df3dc
001df3d8 addiu  v0, zero, $270f     v0=$0000270f
001df3dc sw   v0, $004c(s1)

このsw v0, $004c(s1)の部分に最終的に$270fが入る様にしてみると戦闘中・非戦闘中双方で有効だという事が
分かりました。
$001df3acを見るとlh v1, $0000(t0) + lw v0, $004c(s1)がストアされています
ここでセーブデータをみてみると2バイト単位でHPが扱われている為lh v1, $0000(t0)が基本となるステータスで
lw v0, $004c(s1)が補正をする値という推測が出来ます
以上を踏まえ、セーブデータでアドレス差を出すと

セーブアドレス 相対アドレス 効果
14134 0000 最大HP
14136 0002 現在HP
14138 0004 最大MP
1413a 0006 現在MP
1413c 0008 最大SP
1413e 000a 現在SP
14140 000c 攻撃
14142 000e 防御
14144 0010 魔力
14146 0012 抵抗
14148 0014 行動
1414a 0016 移動
14150 001c Exp
14154 0020 Lv
14155 0021 スペシャルLv
14156 0022 マジックLv
14157 0023 スキルLv
14158 0024 スペシャルExp
1415c 0028 マジックExp
14160 002c スキルExp
14164 0030 武器
14166 0032 防具
14168 0034
1416a 0036 アクセサリー
1416c 0038 マナエッグ
1416e 003a スキルブック
14170 003c 魔法装備数
14171 003d スキル装備数
14172 003e 装備魔法1
14174 0040 装備魔法2
14190 005c 装備スキル1
14192 005e 装備スキル2
141ae 007a
141af 007b
141b0 007c 必殺技Lv
141b8 0084 必殺技・コンボ使用回数?
セーブアドレス+100hで次のキャラ
という事がわかります。Exp以降はセーブアドレス差でこうなると推測したもの。コンボ使用回数の方は自信なし

最後の2つは必殺技を習得する直前と直後のデータを比較してみるとLv1で習得している
Lv2で1つ目の秘訣を得るという事が分かり、更に秘訣を得た場合は使用回数が0にリセットされます

必殺技を使えば使う程秘訣を得る確率が上がる筈なので、この使用回数が重要になります

最初に戻りlh v1, $0000(t0)のベースの部分であるt0レジスタがどの様に設定されているか遡ってを見ていくと

001def0c lhu   v1, $0004(s1)
001def10 lui   at, $0022        at=$00220000
001def14 lw   v0, $d788(at)      v0=$0021d788
001def18 addiu  v1, v1, $8000
001def1c addiu  v1, v1, $8fff
001def20 sll   v1, v1, 8
001def24 addu  t0, v0, v1             

アドレス$0021d788に入っている値にキャラのナンバー毎に+100hしてt0と設定されています
sll v1, v1, 8でv1の256倍している為100h
後は戦闘用の外部ファイルであるBATTLE.BINを開き、$0021d788を参照箇所として必殺技使用回数である
p$0084でロードしている箇所を探していきます
外部ファイルの展開は自然さんの実際の解析手順で説明されている為省略。
ps2disにつっこんでも700KB程度なのでそれほど大変はなく、29カ所しか引っ掛からないので丁寧に
見ていけば見つかります

00369f20 addiu  s1, s2, $000c            
00369f24 addu  v1, a2, s1
00369f28 lbu   v0, $0070(v1)
00369f2c addiu  v0, v0, $ffff
00369f30 sltiu  v0, v0, $0005
00369f34 beql  v0, zero, $00369ff4
00369f38 ld   s0, $0000(sp)
00369f3c jal   ランダム         ▲$001210b0
00369f40 nop
00369f44 lui   at, $3780        at=$37800000
00369f48 mtc1  at, $f0
00369f4c lw   v1, $027c(s0)
00369f50 sra   v0, v0, 15
00369f54 mtc1  v0, $f1
00369f58 cvt.s.w $f1, $f1
00369f5c addiu  a2, s2, $0004            
00369f60 addiu  a1, v1, $0080
00369f64 lui   at, $3c23        at=$3c230000
00369f68 ori   at, at, $d70a      at=$3c23d70a
00369f6c mtc1  at, $f2
00369f70 addu  a1, a2, a1
00369f74 addu  a3, v1, s1
00369f78 lb   v0, $0000(a1)
00369f7c daddu  a0, s0, zero
00369f80 mul.s  $f1, $f1, $f0
00369f84 lbu   v1, $0000(a1)
00369f88 mtc1  v0, $f0
00369f8c cvt.s.w $f0, $f0
00369f90 mul.s  $f0, $f0, $f2
00369f94 c.lt.s $f1, $f0
00369f98 bc1f  $00369fc8
00369f9c addiu  v1, v1, $0001
00369fa0 lbu   v0, $0070(a3)
00369fa4 sh   zero, $0280(s0)
00369fa8 addiu  v0, v0, $0001
00369fac jal   $0035efb0
00369fb0 sb   v0, $0282(s0)

ここが該当箇所ですが、この少し前で$0021d788に入っている値+v0の8ビット左シフト。
上で書いた様にキャラ毎に+100hしており、
その他の要素を加算したものがs2レジスタに入っています
他のところもほとんど8ビットシフトしていますが、重要な所はここではなくその後の$0004と$0080の加算、
それをベースとしたp$0000の使用回数のロードの部分と
$000cの加算とそれをベースとしたp$0070の部分
つまりp$0084・p$007cと同じ。その使用回数のロードと浮動小数点$3c23d70a=0.01と乗算し
c.lt.s $f1, $f0の$f0としています

$f1はjal $00369f3cで乱数を取得し復帰
ジャンプ先の様な構造は乱数を作る(?)処理という事が某コード雑誌の講座で書かれていました
そうして復帰直前のv0に乱数が入っているそうなのでこのv0を見ていきます
復帰後、15ビット右シフトをし、浮動小数点である$37800000=0.000015と乗算して
c.lt.s $f1, $f0の$f1となります

この復帰する値は最大でも$7fffffffなので、15ビット右シフトすると$ffff。
10進数にして65535×0.000015で最大であっても約0.983になります

c.lt.s $f1, $f0で$f1<$f0であればcフラグに1、それ以外であればcフラグに0が入る
bc1f $00369fc8でcフラグ=0だと分岐、cフラグ=1だと分岐しないとなっています

使用回数×0.01=確率が$f0
乱数15ビット右シフト×0.000015が$f1
乱数が確率を超えると分岐、そうでなければ分岐しないという事で
分岐した場合使用回数に+1しストア。分岐しなければ$00369fa0で必殺技Lvをロードして+1しています
結果分岐しなかった場合は秘訣である必殺技Lvが上がるという事なので
bc1f $00369fc8を潰せば必ず秘訣を得ることが可能です

コード化する際には外部ファイルである為必ずDコードと併用。
必殺技を使うと必ず秘訣を得る
0CA120BC 1456DFD1
1CA120C0 1456E7C2

コンボ選択で必ず必殺技を習得

こちらは必殺技習得に必要なスペシャルLvとコンボの回数の兼ね合いを見て。といいたいのですが、
面倒だったので上と同じようなものを見つければ出来るだろうと0.01の浮動小数点である$3c23d70aの
$d70aを16ビットで検索して見つけました。
秘訣の処理から下の方だとアドレス$0036fe8cでヒットしましたが、同系統の処理であれば
付近にある筈なので秘訣の$00369f68の付近を探します
shft+F4で上の方を見るとアドレス$00369d58でヒットしました
この付近を見てみると

00369d48 lui   at, $3780        at=$37800000
00369d4c mtc1  at, $f21
00369d50 addiu  s7, zero, $0064     s7=$00000064
00369d54 lui   at, $3c23        at=$3c230000
00369d58 ori   at, at, $d70a      at=$3c23d70a
00369d5c mtc1  at, $f20
00369d60 addiu  s0, s6, $0084            
00369d64 addiu  s2, s6, $007c
00369d68 lb   v0, $0000(s2)
00369d6c bgtz  v0, $00369ddc
00369d70 addiu  s2, s2, $0001
00369d74 lb   v1, $0001(s4)
00369d78 lb   v0, $0021(s6)            
00369d7c slt   v0, v0, v1
00369d80 bnel  v0, zero, $00369de0
00369d84 sll   v0, a0, 24
00369d88 jal   ランダム         ▲$001210b0
00369d8c nop
00369d90 lbu   v1, $0000(s0)
00369d94 lb   a0, $0000(s0)
00369d98 sra   v0, v0, 15
00369d9c mtc1  v0, $f1
00369da0 cvt.s.w $f1, $f1
00369da4 addiu  v1, v1, $0001
00369da8 mtc1  a0, $f0
00369dac cvt.s.w $f0, $f0
00369db0 sll   v0, v1, 24
00369db4 sra   v0, v0, 24
00369db8 mul.s  $f1, $f1, $f21
00369dbc mul.s  $f0, $f0, $f20
00369dc0 c.lt.s $f1, $f0
00369dc4 bc1t  $00369e00
00369dc8 slti  v0, v0, $0065
00369dcc bne   v0, zero, $00369dd8
00369dd0 sb   v1, $0000(s0)
00369dd4 sb   s7, $0000(s0)

秘訣と似た様なものなので、説明は少し省略。まずは
$00369d6cで必殺技Lvが1以上であれば分岐してループ。これは必殺技を習得していれば1よりも多い値が入っている為。
習得していない場合は0が入っているので分岐せず進み、その後
lb v0, $0021(s6)でスペシャルLvをロードし、何らかの値(恐らくは習得可能になるLv)を超えていなければ分岐。
習得に必要なスペシャルLvを超えていた場合分岐しないはずなので、ここを
bnel zero, zero, $00369de0にして潰しておきます

後は同じようなものでjal $001210b0で乱数を取得し、復帰した値を15ビット右シフト×0.000015がc.lt.s $f1, $f0の$f1
アドレス$00369d94のコンボ使用回数をロードした値×0.01がc.lt.s $f1, $f0の$f0

c.lt.s $f1, $f0
bc1t $00369e00
bc1tはcフラグ=1で分岐、cフラグ=0で分岐しないので

乱数15ビット右シフト×0.000015がコンボ使用回数の確率を超えれば分岐しない、そうでなければ分岐なので
これを上と同じ様に乱数が確率を超えない場合になる様
j $00369e00にする事でコンボ選択で必殺技を習得となります

スペシャルLvのロード部分での分岐潰しと、確率を超えない場合にする強制分岐で必殺技を習得
コンボ選択で必ず必殺技を習得
0CA11EA4 1456D7BF
1CA11EA8 C856E7B4
0CA11EA4 1456D7BF
1CA11EEC 0C518825

必殺技・秘訣双方共にコンボ・必殺技使用回数が画像より下の部分で$0064の最大値チェック・修正。ストアとなっているので
画像の前後も見ておいて下さい。99回超えたら必ず習得する仕様になってます

ついでに
アドレス$00369da4は今見るとコンボか何かのカウントですね。何であるかの断言は出来ないですが、
使えば使うほど確率が上がるものです。全部の秘訣必殺技を習得済みなので1度無理矢理未習得にしてセーブデータを
見ながら確認するのはめんどうなのでやりませんが、これでいけるんじゃないでしょうか

コンボの使用回数?増加量x倍
0CA11EC8 1456DF85
1CA11ECC 3873E7xx

この辺りにp$001cやp$0024等をロード、加算、ストアとするとコンボ選択の度に経験値獲得等
出来るかもしれませんね

戯れ言
内容はともかくとして、コード作り自体は楽しかったです。
ゼノサーガ2も同じ様な事を言った気が・・・あれなソフトはコードを作るのが何故か楽しい
でもこのソフトの戦闘なら処理落ちしてもおかしくなさそうなのにしないって事は凄い事だと思うんですよ

秘訣・必殺技習得判定の相対アドレスが上に書いたリストのままで使えたんで今回は見付かったんですが、
大体の場合戦闘なら相対アドレスが変わって、そのままでは使えないんじゃないでしょうか
その場合は戦闘用のデータに移行している箇所を見つけ、それを追って見ていくなんて事は出来ないんで
乱数の復帰の値を変えて地道な作業をするしかない。
けど復帰の値によっては他の処理で使われる時にフリーズするんだよな

戻る