DQ3 固定文字列2バイト化対応2

固定文字列を2バイト化するにあたって必要な作業は以下の2つです。

  1. 固定文字列(2バイト)の定義
  2. 固定文字列(1バイト)→固定文字列(2バイト)への変換処理

1は意外と大掛かりな作業になります。もともと存在していない可変長のデータを定義し、かつ既存の固定文字列(1バイト)のように変更できるようにしないと手間がかかってしょうがなくなるので、これはSFCGENEditorのDQ3プラグインを変えてそういう可変長のデータを変更できるようにする必要があります。正直この手の独自の変更をプラグインに入れるのは気持ちのいいものではないのですが、「必要ない人には関係ない」話なので割り切ることにしました。

はるか昔に作った固定文字列のデコード、編集機能をパクって使う文字列を2バイト文字に変えるだけなので思ったほど手間はかかりませんでした。実装後ある程度固定文字列を追加してからバグが見つかったのはご愛嬌。

次に2ですが、現状想定している変換対象の文字は職業名(16) + フロア名(約50) + 性格名(約30)+ アイテム名(最大300?) で512レコードあれば十分というところです。通常は「固定文字列(1バイト)デコード→1文字ずつ2バイト文字(ひらがな)に変換して表示」という処理を「固定文字列(1バイト)ID を 変換テーブルを見て固定文字列(2バイト)IDに変換 → 固定文字列(2バイト)デコード→そのまま表示」という処理に変えることになります。この「固定文字列(1バイト)ID を 固定文字列(2バイト)IDに変換」するという処理がどれだけ重い処理なのかわからなかったので(変換テーブルにヒットしない場合毎回最大512レコードを空サーチする羽目になる)おっかなびっくり実装してみましたが、毎回512レコードサーチさせても表示時につっかかるように感じることもなく、結局杞憂だということがわかりました。当初アイテム名まで対象にすると各キャラクターの持ち物の表記とメッセージの表記が異なるケースが頻繁に発生する(「やくそう」→「薬草」など)ので対象外としていましたが、一部のイベントアイテムなど(变化のつえ、英雄指南6など)だけ2バイト文字を使うということにしました。現状トータル100レコードくらい変換テーブルにあるという状態です。

また、メッセージ中に出てくる固定文字列を意味する[B2]、アイテム名を意味する[B5]、[C7]は戦闘中でも使うため([C7]は不明)、余計なバグを防ぐということで新たに[D7]を固定文字列(2バイト)用に、[DE],[DF]をアイテム名(2バイト)用に使うことにします。当然メッセージ中で使用されている[B2],[B5],[C7]は適宜[D7],[DE][DF]に書き換える必要があります。

その他、当然[D7],[DE],[DF]に相当するデコード処理を実装する必要があります。

  • SR:$01EA36 特殊文字処理[D7](1バイト固定文字列->2バイト固定文字列変換対応)(新SR)
$01EA36 JSR $B998 SR: $01B998 $BE59に$FFFFをセット
$01EA39 LDA $BE77 A=$BE77
$01EA3C JSL $C1EA06 SR: $01EA06 固定文字列ID2バイト変換(未変換c=off)
$01EA40 BCS #$05 if(c==on) goto $01EA47
$01EA42 JSL $C1B75C SR: $01B75C 文字列特殊処理_SR_0007 ([B2]固定文字列ID)
$01EA46 RTL return
$01EA47 STZ $BDFB $BDFB=#$00
$01EA4A LDX #$0000 X=#$0000
$01EA4D JSL $C1E99B SR: $01E99B 固定文字列(2バイト)先頭位置取得
$01EA51 JSL $C1EA01 SR: $01EA01 固定文字列(2バイト)デコード
$01EA55 STA $BDFD,X $BDFD+X=A
$01EA58 CMP #$00AC A==#$00AC?
$01EA5B BEQ #$04 if(z==on) goto $01EA61
$01EA5D INX X++
$01EA5E INX X++
$01EA5F BRA #$F0 goto $01EA51
$01EA61 LDA #$00AB A=#$00AB
$01EA64 STA $BDFD,X $BDFD+X=A デコードした文字を別バッファに保存
$01EA67 LDA #$0003 A=#$0003 別バッファを使用+2バイト文字に変換済みフラグをON
$01EA6A STA $BDF5 $BDF5=A
$01EA6D RTL return

変換テーブルを見て変換対象が見つかれば固定文字列(2バイト)デコード処理をして見つからなければ通常の固定文字列(1バイト)デコード処理をするということをしています。変換テーブルは1レコード4バイトで変換対象の固定文字列ID(1バイト)と変換後の固定文字列ID(2バイト)を並べるだけの簡単なテーブルです。また、ここでの処理のキモは$BDF5に$01ではなく$03をセットすることです。オリジナルでは$01しか想定していませんが、この領域をビットフラグ扱いして、2ビット目を「2バイト文字に変換済み」という意味にすることで実際に画面に文字を表示する時に処理を切り替えられます。

  • SR:$01EA06 固定文字列ID2バイト変換(未変換c=off)(新SR)
$01EA06 PHA Push A
$01EA07 PHX Push X
$01EA08 LDX #$0000 X=#$0000
$01EA0B LDA $D8B740,X A=$18B740+X 変換元の固定文字列(1バイト)ID
$01EA0F CMP #$FFFF A==#$FFFF? #$FFFFなら検索終了
$01EA12 BEQ #$1A if(z==on) goto $01EA2E
$01EA14 CMP $03,S A==Stack($03)?
$01EA16 BNE #$0D if(z==off) goto $01EA25
$01EA18 LDA $D8B742,X A=$18B742+X
$01EA1C CMP #$FFFF A==#$FFFF? 変換対象レコードが見つかっても変換後のIDが#$FFFFなら変換しない
$01EA1F BEQ #$0D if(z==on) goto $01EA2E
$01EA21 STA $03,S Stack($03)=A
$01EA23 BRA #$0D goto $01EA32
$01EA25 INX X++
$01EA26 INX X++
$01EA27 INX X++
$01EA28 INX X++
$01EA29 CPX #$07FC X>=#$07FC?
$01EA2C BCC #$DD if(c==off) goto $01EA0B
$01EA2E PLX Pull X
$01EA2F PLA Pull A
$01EA30 CLC c=off
$01EA31 RTL return
$01EA32 PLX Pull X
$01EA33 PLA Pull A
$01EA34 SEC c=on
$01EA35 RTL return
  • SR:$01E99B 固定文字列(2バイト)先頭位置取得(新SR)(省略)
  • SR:$01EA01 固定文字列(2バイト)デコード(新SR)(省略)

毎回512レコード分検索するのは意味が無いので#$FFFFのレコードが出てきたら終了としています。また、変換後の値が#$FFFFの場合も変換しないというようにしておきます。これにより「2バイト文字で表現するつもりだったがやっぱりやめる」というようなことが容易に実現できるようにしておきます。

これで2バイト文字がデコードできたので画面に表示する際に細工をして終わりです。

  • SR:$01B024 1バイト文字を2バイト文字に変換
$01B037 JSR $EA6E SR: $01EA6E 固定文字列をバッファから返す
  • SR:$01EA6E 固定文字列をバッファから返す
$01EA6E PHA Push A
$01EA6F LDA $BDF5 A=$BDF5
$01EA72 AND #$0002 A&=#$0002 変換する必要があるかをチェック
$01EA75 BEQ #$02 if(z==on) goto $01EA79
$01EA77 PLA Pull A
$01EA78 RTS return
$01EA79 PLA Pull A
$01EA7A JSR $B078 SR: $01B078 1バイト文字を2バイト文字に変換
$01EA7D RTS return

固定文字列が1バイト文字で展開されている場合はここで2バイト文字のひらがなに変換する必要がありますが、すでに2バイト文字(漢字)に変換済みの場合は余計な処理をする必要が無いので別バッファの文字をそのまませばいいだけになります。

アイテム名についても同様で、アイテムIDから固定文字列IDを取得した後、同様の変換処理をかませばいいということになります。

  • SR:$01EA87 特殊文字処理[DE](アイテム名2バイト化対応)(新SR)
$01EA87 JSR $B998 SR: $01B998 $BE59に$FFFFをセット
$01EA8A LDA $BE79 A=$BE79
$01EA8D JSL $C44E32 SR: $044E32 引数:1#$FF 引数:2#$FF アイテム名称ID取得
$01EA93 JSL $C1EA06 SR: $01EA06
$01EA97 BCS #$03 if(c==on) goto $01EA9C
$01EA99 JMP $B762 ($01B762) goto $01B762 文字列特殊処理_SR_0007 ([B2]固定文字列ID)の途中にジャンプ
$01EA9C JMP $EA47 ($01EA47) goto $01EA47 特殊文字処理[D7](1バイト固定文字列->2バイト固定文字列変換対応)の途中にジャンプ

ということで一通りの作業が終わりました。枠組みは作ったので個人的な悩みどころは「アイテムをどこまで2バイト文字を使って表現するか」になりました。真面目にやるとキリがないので主にイベントでしか出番のないアイテム(入手時と使用時くらいしか出番がないようなもの)や、個人的な好みで「これは漢字で表記したい」というアイテムを中心に漢字を使っています。あまりやりすぎると「どうぐ」メニューでの表記とメッセージでの表記の乖離が大きくなるので程々にしたいところです。

コメント

  1. ダミー17 より:

    「変換元の固定文字列 ID」がレコード配列 $D8B740 中で一意であるのならば、レコードを「変換元の固定文字列 ID」でソートしておけば、探索のアルゴリズムに線形探索ではなく二分探索が採用できて、最悪 512 回の比較が最悪 10 回の比較に抑えられませんか。

    TAX, TXA, ASL, LSR 各命令を駆使したコードになると思います。

    管理者より返信:

    身もふたもないことを言ってしまうと、一番処理時間がかかると思われる「ヒットしない512レコード全回しでも処理速度に違和感は覚えなかった」のが2分検索法などを使わなかった最大の理由です(実際はヒットしないケースでも#$FFFFが出てくると途中で検索をやめるので一番時間が掛かるケースでもトータルの処理量は現状約110レコード分のループです)。2分検索法の前提となる「全IDをソート」は変換不要のレコードまで含めておく必要がある、追加の必要がある時にソートしなおす必要がある、管理が煩雑になる(?)等いろいろ面倒になりそうなので見送っています。他には各項目(職業名、地名、アイテム名)のインデックスに対応する固定文字列(2バイト)用の配列を別個で用意して固定文字列ID(1バイト)ではなく必要に応じて固定文字列ID(2バイト)のほうを参照させる(512レコードサーチのような馬鹿げた処理はいらないが、各所に変換処理を書く必要があるので対応が面倒)、各項目ごとにの変換テーブル中の検索対象のインデックス範囲を指定するなど考えていましたが杞憂だったということで全部ボツにしています。

    そのうち出す現物で動作を見てみてください。

タイトルとURLをコピーしました