ドラゴンクエスト[

キーワード
参照箇所の検索
相対アドレス
ベース
レジスタ


内容
ステータス確認するとスキルLV指定値に変更
アイテム袋にいれると指定個数に変更
アイテム袋にいれると全種入手


ステータス確認するとスキルLV指定値に変更

これは偶然見つけたという方が正しいのですが、探し方を説明していきます。
攻略サイトからスキルLVの最大値は100であるという情報を基に探していきます。
攻略サイト、説明書はコードを作る為に役立つ情報がありますので、見ておいて損はないです。

では探していきますが、$0064で16bitで検索するとかなり多くのaddu等の加算命令や比較命令がヒットします。
そうすると見つけたい箇所を見逃してしまったり時間がかかりすぎる為、以前血風録の時に使った
参照箇所の検索を使います。
こちらをご覧下さい。
仕様上色々ヒットしますが続けると次の様な箇所がありました。

002156a0 bltz  a1, $002156b8
002156a4 daddu  v0, zero, zero
002156a8 slti  v0, a1, $0005
002156ac bne   v0, zero, $002156c0
002156b0 sll   v0, a1, 1
002156b4 daddu  v0, zero, zero
002156b8 beq   zero, zero, $002156c8
002156bc nop
002156c0 addu  v0, v0, a0
002156c4 lh   v0, $003a(v0)            
002156c8 jr   ra
002156cc nop
002156d0 bltz  a1, $002156e8
002156d4 daddu  v0, zero, zero
002156d8 slti  v0, a1, $0005
002156dc bne   v0, zero, $002156f0
002156e0 sll   v0, a1, 1
002156e4 daddu  v0, zero, zero
002156e8 beq   zero, zero, $00215724
002156ec nop
002156f0 addu  v0, v0, a0
002156f4 lh   v0, $003a(v0)
002156f8 addu  v1, v0, a2
002156fc bgez  v1, $0021570c
00215700 slti  at, v1, $0065
00215704 daddu  v1, zero, zero
00215708 slti  at, v1, $0065
0021570c bne   at, zero, $00215718
00215710 sll   v0, a1, 1
00215714 addiu  v1, zero, $0064     v1=$00000064
00215718 addu  v0, v0, a0
0021571c sh   v1, $003a(v0)
00215720 lh   v0, $003a(v0)
00215724 jr   ra

見ると解るかと思いますが、$0065と比較しそれよりも大きければ分岐せず修正値である$64が
p$003aへ入るというものです。
今回見て頂きたいのはそのすぐ上にある処理で、p$003aの値をロードしています。

その処理を見ますと、a1レジスタが0未満・以上であるかで分岐するかしないかが決まり、
分岐した場合はv0レジスタに0を格納する様になっています。
a1レジスタが5以上であっても0となります。
そうしてa1レジスタが0〜4の場合その分だけ左へシフトした数値をa0レジスタへ加算し、v0レジスタへ格納。
それをベースとしてロードして復帰となる流れです。

0〜4シフトつまり5種類のデータをこの処理で処理していることと下の処理で最大値が100であることを考えますと
ここはスキルLVの処理をしている箇所で間違いなさそうです

試しにこの青くマークしたところをaddiu v0,zero,$0064と変えてみるとスキルLVが100になっていました。
しかしこのままではセーブデータに残りません。原因はスキルLVをロードして表示やどのスキルを覚えているかを
表示させる為のものでデータに記録させる必要がない為にストア命令が入っていないのだと思います。
理由はともかく九龍のアイテムと同様にストアさせる事でセーブに反映されます。
書き換えもそれと同様で
元の命令がlh v0, $003a(v0)という様にストアのベースレジスタとロードした数値を格納するレジスタが同じである為、
このまま
アドレス$002156c4をaddiu v0,zero,$0064 アドレス$002156c8をsh v0, $003a(v0)
と書き換えるとストアするアドレスが$009eとなってしまいます。
ですから

002156c0 addu  t7, v0, a0             
002156c4 addiu  v0, zero, $0064     v0=$00000064
002156c8 jr   ra
002156cc sh   v0, $003a(t7)            

この様にストアのベースとなるレジスタと灰色のマークの箇所のレジスタを同じにすることで正常に動作します。
幸いこの処理の復帰命令後に1行の空きがあったのでストア命令を書けましたが空きがなければ
空きメモリへジャンプさせるというもう1手順必要になります。

アイテム袋にいれると指定個数に変更

これは最大値が999個であるということが解っていましたので参照箇所の検索で最大値修正箇所を探してみましたが
それらしいものはありませんでした。
その為ふくろに渡すなどの1個ずつの加算の場合は1000個以上の際に修正値を999にする
という処理ではなく1000個以上の場合は加算しないというもので、
他の場合は1000個以上であれば減算して修正しているのだと思います。
減算の際にも999は必要なのですがざっと見た限りでは見あたらなかったので
少し離れた箇所で999をレジスタに入れているのかもしれません。

前者の場合は比較命令の数値
slti 〜,〜,$〜の
$〜部分の数値で16bit検索をする必要がありますが今回の場合比較する数値は最大値より1多い数値で比較していることが
所持金やステータスで分かっていますので$03e8で検索していきます。
その際に注目すべき点は比較前に加算命令で+1している箇所であること。
これはふくろに渡す際には1個ずつしか入れられない為です。
その点を見ながら探していくと次の様な処理が見つかりました。

00215094 lh   v1, $0002(v0)
00215098 addiu  v1, v1, $0001
0021509c slti  at, v1, $03e8            
002150a0 beq   at, zero, $00215114         
002150a4 nop
002150a8 sh   v1, $0002(v0)
002150ac addiu  s0, s0, $0001      s0=$00000001
002150b0 addiu  a0, a0, $0001      a0=$00000001
002150b4 slt   v1, a0, s1
002150b8 bne   v1, zero, $00215094

始めに推測した通り$03e8以上であれば分岐して加算せず、それ未満であれば分岐ぜずに
加算した数値をそのままストアしています。
試しに$32をストアさせてみます。

00215094 lh   v1, $0002(v0)
00215098 addiu  v1, v1, $0001
0021509c slti  at, v1, $03e8
002150a0 addiu  v1, zero, $0032     v1=$00000032
002150a4 nop
002150a8 sh   v1, $0002(v0)

このコードを使いふくろにアイテムを渡してみると入れたアイテムが50個になっていました。

イベントアイテムの為に入れると1個というものを作りパッドコードで切り換えるコードにしたのですが、
その後に袋に入れるとアイテム全種入手コードが出来た為、削除用コードを作ることにしました。

ワークコードから2hのずれで種類、個数という様に分かれていることが分かっていたので
p$0000,p$0002それぞれにzeroをストアさせる事で袋に入れた際にアイテムの種類・個数が無くなる筈です。
しかし、

00215094 lh   v1, $0002(v0)
00215098 addiu  v1, v1, $0001
0021509c slti  at, v1, $03e8
002150a0 sh   zero, $0000(v0)           
002150a4 nop
002150a8 sh   zero, $0002(v0)           

この様にすると切り換え式コードを作る際にどうしてもOFF用個数変更用に1行足さなければなりませんし、
Eコードを使うとエクスプローダーで使えないという報告を受けることは目に見えています。
その為A・Eコードは出来れば使いたくないので1行に出来るならば無理矢理にでもしようと思い、この様に変えました。

00215094 lh   v1, $0002(v0)
00215098 addiu  v1, v1, $0001
0021509c slti  at, v1, $03e8
002150a0 sw   zero, $0000(v0)           
002150a4 nop
002150a8 sh   v1, $0002(v0)            

こうすると灰色にマークしている箇所の処理で問題が起きそうなのですが、
自分で試したところ問題がなかった為、このままにしました。

その後削除のみコードが効かないという方に最初の安定する方法で試して頂きましたが、
効果が出なかった為その点の修正はしていません。

しかしこのコードはふくろに同じアイテムがなければ効果が出ないという報告を受けましたので見直しをしてみました。
そうすると

00215078 addiu  a2, zero, $018c     a2=$0000018c
0021507c jal   $00215660
00215080 daddu  s0, zero, zero
00215084 beq   v0, zero, $002150c8         
00215088 addiu  a0, s3, $00c0
0021508c beq   zero, zero, $002150b4
00215090 daddu  a0, zero, zero
00215094 lh   v1, $0002(v0)
00215098 addiu  v1, v1, $0001
0021509c slti  at, v1, $03e8
002150a0 beq   at, zero, $00215114
002150a4 nop
002150a8 sh   v1, $0002(v0)
002150ac addiu  s0, s0, $0001      s0=$00000001
002150b0 addiu  a0, a0, $0001      a0=$00000001
002150b4 slt   v1, a0, s1
002150b8 bne   v1, zero, $00215094
002150bc nop
002150c0 beq   zero, zero, $00215118
002150c4 daddu  v0, s0, zero       v0=$00000001
002150c8 jal   $00215600

青くマークした箇所で0であれば分岐し、今までの処理を通らないということが分かりました。
分岐した場合にも同じような処理があるのでそこを変えてもいいのですが今までのコードの倍の量になってしまう為
それは没にし、分岐した場合の処理から今までの処理にとばすことにしました。
ではどこからとばせばいいのかですが、
$002150d0〜$00215110であればほぼどこでも問題ないので好きなところで大丈夫です。
私はこの様にしました。

00215094 lh   v1, $0002(v0)
00215098 addiu  v1, v1, $0001
0021509c slti  at, v1, $03e8
002150a0 beq   at, zero, $00215114
002150a4 nop
002150a8 sh   v1, $0002(v0)
002150ac addiu  s0, s0, $0001      s0=$00000001
002150b0 addiu  a0, a0, $0001      a0=$00000001
002150b4 slt   v1, a0, s1
002150b8 bne   v1, zero, $00215094
002150bc nop
002150c0 beq   zero, zero, $00215118
002150c4 daddu  v0, s0, zero       v0=$00000001
002150c8 jal   $00215600
002150cc addiu  a1, zero, $018c     a1=$0000018c
002150d0 beq   v0, zero, $00215114
002150d4 nop
002150d8 sh   s2, $0000(v0)
002150dc daddu  a0, zero, zero
002150e0 j    $00215094              
002150e4 sh   zero, $0002(v0)

ちなみに$002150c8のjalでは種類の設定をしているようです。

アイテム袋にいれると全種入手

これはアイテムの個数が変動すると最大の様なコードが出来なければ話にならないものです。

探し方は
1.アイテムの個数の相対アドレスから16bitで探す。
2.アイテムの個数変動処理の前後を見ていく。
3.勘(適当)。等

しかし今回は個数のpが$0002である為1の方法は取れません。
では2の方法はどうでしょうか?
先ほどの個数の変動処理を見ます。

00215074 addiu  a1, s3, $00c0
00215078 addiu  a2, zero, $018c     a2=$0000018c
0021507c jal   $00215660              
00215080 daddu  s0, zero, zero
00215084 beq   v0, zero, $002150c8
00215088 addiu  a0, s3, $00c0
0021508c beq   zero, zero, $002150b4
00215090 daddu  a0, zero, zero
00215094 lh   v1, $0002(v0)
00215098 addiu  v1, v1, $0001
0021509c slti  at, v1, $03e8

青くマークしてある箇所のjalへ飛んでみます。

00215660 beq   zero, zero, $00215684
00215664 daddu  v1, zero, zero
00215668 lh   v0, $0000(a1)
0021566c bne   v0, a0, $0021567c
00215670 daddu  v0, a1, zero
00215674 beq   zero, zero, $00215690
00215678 nop
0021567c addiu  v1, v1, $0001      v1=$00000001
00215680 addiu  a1, a1, $0004            
00215684 slt   v0, v1, a2             
00215688 bne   v0, zero, $00215668
0021568c daddu  v0, zero, zero
00215690 jr   ra

するとv1レジスタをzeroにし、カウンタとして使っています。a1レジスタは見た目通りアドレスに4hずつ加算しています。
カウンタの数値と比較しているa2レジスタはjalで飛ぶ前の$018cです。
この処理はロードするアドレスに4hずつ加算しながら$0000〜$018cまでループするという処理をしています。
その直後の処理の$0002が個数の相対アドレスであることから$0000は種類の相対アドレスであると見て間違いありません。
このループを利用し種類・個数をストアさせることでアイテム全種入手コードが出来ます。

00215660 beq   zero, zero, $00215684
00215664 daddu  v1, zero, zero
00215668 addiu  v0, zero, $03e7     v0=$000003e7
0021566c sh   v0, $0002(a1)
00215670 daddu  v0, a1, zero
00215674 sh   v0, $0000(a1)
00215678 nop
0021567c addiu  v1, v1, $0001      v1=$00000001
00215680 addiu  a1, a1, $0004
00215684 slt   v0, v1, a2
00215688 bne   v0, zero, $00215668
0021568c daddu  v0, zero, zero
00215690 jr   ra

この様に変えることでアイテムの種類をストアしつつ、個数999個をストアしていくことが可能になります。

ちなみに3の勘はアイテムの個数変動処理前後からロードやストアしている箇所を適当に変えると出来る場合もあります。
勘といっても本当の適当ではなく、前後の処理を見ながらですね。
ステラデウスのアイテム全種コードは3の方法です。
しかしステラデウスは何故バイトやハーフワードデータでいい数値までもワードデータで管理していたのでしょうか…
バイト・ハーフワード・ワードのストア・ロードもコードを作る際に何の処理か判断する基準になるので
ワードばかりだとやりづらかったり。
嫌がらせですか?

戻る