DQ6 Extended(拡張ベースパッチ)作成にあたっての考慮点2

DQ6 Extendedの作成はモンスターレコードの上限を+256にする部分が一番厄介だったものの、サイズの大きいデータの移動は終わっており、残すは本丸のアイテムレコードの上限を+256にする部分以外はだいたい終わったりしています(ショップやおしゃれな鍛冶屋のアイテムを9bit対応するのは先駆けて終わっています。こっちのほうが楽)。思ったより作業速度が早いのはバイナリwikiの(おそらく)45氏によるドキュメントのお陰でどこに何があるかはだいたいわかるので助かっているというのもあります。先人の知識の蓄積に感謝します。

アイテムの拡張はセーブデータをオリジナルと非互換にして拡張する必要と思われるので正直あまり気が進まず、とりあえず今作業ができているところまででテストプレイをちんたらやっているところです。自分の作ったインターフェース改良パッチを当てたいところですが、単体で問題がないことを確認することも兼ねてグッと我慢してオリジナルのままやっていますが、まあ使いづらい。アイテムを渡しても装備してくれないし、いちいちアイテムを使うとウィンドウは閉じちゃうし、ふくろからアイテムを直接実行できないし、全ウィンドウクローズもないのでステータスを見ている途中でウィンドウを閉じれないし。このへんはDQ6 Extendedベースならいくらでもいじれるので(領域もあるし、ウィンドウも新規定義可能なのでやりたい放題)、ゲーム性を変えるパッチを作る暁にはDQ6 IFIMPをベースにDQ3レベルまで持って行きたいなと思うところです(DQ6 IFIMPも細かいところで使い勝手はDQ3オリジナルには及びません)。とはいえ、DQ6はもともと使っているROMの容量がDQ3よりかなり多いので、空き領域はそれほど多くないです。今回いろいろ調子に乗って各固定長のレコードサイズを増やしましたが、残りは可変長のデータ(スプライトグラフィックやらマップ実データ)やプログラムのためにある程度空けておく必要があります。

今回一つの山だと思っていたのは「移動中メッセージをハフマン圧縮を解凍した状態でROMに書き戻し、さらにROM中の移動中メッセージのデコードプログラムも合わせて変更する」点でしたが、意外とあっさり終わってしまいした。ハフマン圧縮を解凍するのはSFCGENEditorのDQ6プラグインですでにできていたので、解凍した状態の文字列をバイナリ変換して本体、インデックスをバイナリファイルに書き出すという作業を行い、ROMの適当な場所に埋め込みます。次にメッセージデコードSRを変更し、ハフマン木を参照せずにそのままデコードするように変えます。この辺はDQ3 Extendedの該当部分を参照し、真似しただけです。メッセージインデックスは$122300-に、メッセージ本体は$123200-に移動してあります。

  • SR: $002B69 メッセージインデックスから開始アドレスを取得
002B69 LDA $5998 A=$5998
002B6C AND #$0007 A&=#$0007 インデックスは8毎なのでターゲットのメッセージの頭出しの回数を取得
002B6F STA $5A1E $5A1E=A
002B72 LDA $5998 A=$5998
002B75 LSR A>>1
002B76 LSR A>>1
002B77 LSR A>>1
002B78 PHA Push A
002B79 ASL A< <1
002B7A ADC $01,S A+=(Stack($01)+c)
002B7C TAX X=A インデックスの該当位置を決定
002B7D PLA Pull A
002B7E LDA $D22300,X A=$122300+X インデックスの指す開始アドレスを取得
002B82 STA $A0 DP($A0)=A
002B84 LDA $D22302,X A=$122302+X
002B88 AND #$00FF A&=#$00FF
002B8B STA $A2 DP($A2)=A
002B8D LDA $A0 A=DP($A0)
002B8F CLC c=off
002B90 ADC #$3200 A+=(#$3200+c) インデックスのアドレスから本体のアドレスを取得
002B93 STA $A0 DP($A0)=A
002B95 LDA $A2 A=DP($A2)
002B97 ADC #$00D2 A+=(#$00D2+c)
002B9A STA $A2 DP($A2)=A
002B9C LDA $5A1E A=$5A1E
002B9F BEQ #$12 if(z==on) goto $002BB3
002BA1 JSR $2BD4 SR: $002BD4 メッセージ1文字読み出し?
002BA4 CMP #$00AC A==#$00AC?
002BA7 BEQ #$05 if(z==on) goto $002BAE
002BA9 CMP #$00AE A==#$00AE?
002BAC BNE #$F3 if(z==off) goto $002BA1
002BAE DEC $5A1E $5A1E–
002BB1 BRA #$E9 goto $002B9C
002BB3 RTS return

メッセージのインデックスは8個ごとに設定されているので、ターゲットのメッセージの開始位置を特定するには、8で割った商の位置にあるアドレス(これが直近の8で割り切れるメッセージの開始位置)を取得してから8で割った余りの分だけデコードを繰り返し、区切り文字が出てきたら1カウントするようにしてターゲットのメッセージの位置を特定します。これにより、頭出しのためのデコードにかかる手間は最大6?メッセージ分ということになり、メッセージ表示にかかる時間をある程度の範囲内に収められるということになるようです。

  • SR: $002BD4 メッセージ1文字読み出し?
002BD4 LDA $59AE A=$59AE
002BD7 CMP #$0001 A==#$0001?
002BDA BNE #$0F if(z==off) goto $002BEB
002BDC LDX $59B4 X=$59B4
002BDF LDA $59B8,X A=$59B8+X
002BE2 INX X++
002BE3 INX X++
002BE4 STX $59B4 $59B4=X
002BE7 JSR $2C28 SR: $002C28
002BEA RTS return
002BEB LDA $A0 A=DP($A0)
002BED STA $5BDC $5BDC=A
002BF0 LDA $A2 A=DP($A2)
002BF2 STA $5BDE $5BDE=A
002BF5 LDA $A0 A=$(DP[$A0])
002BF7 INC $A0 DP($A0)++
002BF9 BNE #$02 if(z==off) goto $002BFD
002BFB INC $A2 DP($A2)++
002BFD INC $A0 DP($A0)++
002BFF BNE #$02 if(z==off) goto $002C03
002C01 INC $A2 DP($A2)++
002C03 RTS return

このSRは現在アドレスをインクリメントしながらメッセージを1文字ずつ読みだして返しているようです。このSRをコールしているSRで頭出しの判断をしているようです。ハフマン木の参照がなくなった分シンプルになっています。正直これだけの変更でなんで未圧縮のデータが読めているのかいまいち不明ですが、オリジナルのDQ3とDQ3 Extendedの関連部分を比較する限り、他に変更している箇所は見当たりません。気持ち悪いですが、とりあえず解析は後回しにします。