update 2003.08.24

ファミコン版「レインボーアイランド」 ステージクリアメッセージ変換


概要
ファミコン版のレインボーアイランドでは、ステージクリア毎におまけの ショートストーリーがあります。
半角かな・記号文字で構成されているこのメッセージを
 半角かな→全角かな
 記号文字→全角記号
に変換し、コマンドプロンプト(DOS窓)に出力する変換ツールを作成します。

宝箱を開けたりする選択肢があるのですが、とりあえず「ぼび〜にはなしかける」を
選択した場合のみのストーリーを吸い出します。
(このデータがステージ1〜LASTまで延々連なった形で格納されてたりします。
 ひじょ〜〜に解析がやりやすかったですねぇ)。

準備
この解析・変換ツール作成には以下のものを使用します。
・GCCODE 解析ツール無しだとキツイです。
・バイナリエディタ 数列から内容を解析したり、パターンテーブルを作るのに使います。
・VisualC++6.0 ツール作成用です。Borland C++ Compiler(フリー)で代用できます。

解析
とりあえず適当なエミュレータを使ってレインボーアイランドのステージ1をクリアしましょう。
クリア画像
とまあこんな感じのストーリーが展開されますので、これを GCCODE で解析して大まかな目星を付けます。

GCCODE でレインボーアイランドを選択して、以下の手順で文字列テーブルを得ます。
1.検索用文字列に半角カナで「コ?ナトコロニ」(「ん」は大抵配置が違うのでスキップさせます)。
2.検索処理「BYTE検索」を実行(2個該当するうちの前者(offset=0001f318〜)が当たりです。
3.フィルタリング出力で 0001f318 〜 0001f500 くらいの範囲を入力し、
 差異を 89(+)、バイトサイズは 1 、一文字ごとにスペース挿入をチェック、
 これで「ログ出力」をクリックします。
GCCODEの画面
微妙に文字化けしながらもある程度の話は抽出できるので、後は化けている部分を整合させて、
文字列以外の部分を除去、制御コードをある程度実装させればOKですね。
(言うのは簡単なんですが…(^^;;;

ゲーム画面との比較で解析
先ほどの解析で「ASCIIの配置との差異は 89 で、一部文字の配置が異なる」というのが判明したので、
続いて画面表示と見比べて穴埋めを行います。。

なお、以下のリストでは半角かなは全角かなで表しており、「×」は文字化けもしくは非整合の文字を表しています。
画面表示ほ゛ひ゛
GCCODEの文字列××゜ほ゜ひ××゜か×
これが初めの1行になりますが、この比較では「ん」「読点」「濁点」「伸ばし」「句点」
が正常に変換されていないことが判ります。
また濁点が文字より先に格納されているところから「文字の後に゛を付ける」ではなく
「濁点(半濁点)のコードがあった場合、その次の文字に加える」という処理が行われている
という動作をしていると考えられます。
他にも「!」や「?」などの記号、句点・読点などの制御含む特殊文字なども調べることができます。

バイナリエディタで解析
以上の方法で絞り込めなかった制御コードなどはこの段階で解析します。
例えば、レインボーアイランドでは 65 , 66 , 67 の3通りの改行コードが存在しており、
それぞれが「喋り終わった後の改行」「メッセージを消去して最初の行に戻る」
「喋っている最中に改行して、次の行は3文字スペースをはさんでから続く」
という処理を行うようになっています。
コンバータを作る際にはこれら制御用コードも処理をする必要があります。

このような比較を続け、全文字の差異を見つけ出すことができれば解析完了です。

変換処理プログラムを組む
今回のような比較的難易度の低いパターンの文字列の変換処理となると、
以下の二通りの手順が考えられます。

・差異吸収を数式で行う
 基本的に「オフセット 0001f318 以降のデータに +89h すると半角カナになる」
 ということがわかっているので、これと同じ発想で変換します。
 「(ASCII)ア=B1 イ=B2」と「(Rainbow Island)あ=28 い=29」
 つまり変換には「89(B1-28)+文字データ」という基本式から構成され、
 「ん」などの配列パターンの異なる文字に対しては個別に処理を行います。

・パターンテーブルを使用する
 あらかじめ配列に文字を定義し、元データを指標として使います。
 例えば「str[28]="あ" str[29]="い"...」のような形で定義しておけば、
 str[(文字データ)]という参照で直接文字を得ることができます。
 特殊文字に対しては同様に個別に処理を行います。

前者の利点としては、必要最小限の計算式のみで構成するため非常に小さいことが挙げられます。
ただし「半角→全角」のような変換は文字コードパターンが異なるため
(半角の配置「あいうえおかきくけこ」 → 全角「ぁあぃいぅうぇえぉおかがきぎくぐけげこご)
逆に手間が増えてしまうことがあります。

後者の利点としては、一旦テーブルを定義すればそれに当てはめればよいだけで、
かつ常時テーブルを参照できるためデバッグが容易になります。
さらに文字配列の互換性が全くないデータですら変換が可能になります。
(たとえ配置が「あかさたなはまやらわいきしちに…」でも問題無いです)。
ただし使われない文字分もデータを定義するため容量の無駄が発生するところや、
根本的に「テーブルパターンを作る作業が大変」という弱点があります。

これらを組み合わせて使うのも手で、方法次第ではとてもスマートに組むこともできるはずです。

変換画面

Cソース
VisualC++ 6.0で作ったコンバートサンプルプログラムです。
今回は全角変換も含まれるので「パターンテーブルを使用する」のルーチンで組んでいます。
パターンテーブルは別ファイル RAINBOW.TBL として用意しており、
実行時にそれを読み込んで参照するようにしています。
この手法を使えばテーブルが間違っていた場合は RAINBOW.TBL を修正するだけでよいため、
プログラム内のテーブル修正→コンパイルの手順を省くことができます。
(それに、変数宣言時の配列で文字列テーブルを定義するのは大変だったりしますので…)
なお、言うまでもありませんがパターンテーブルの作成はバイナリエディタの新規作成で
行いますので改造専用のバイナリエディタ(たにしげ師匠のSFXなど)では作れないです。

プロジェクトは Win32 console application の "Hello,World!"アプリケーション を選択し、
丸ごとメインのソースに上書きすればコンパイル&ビルドができます。
そして同一ディレクトリに RAINBOW.TBL を置いた状態で実行してください。
>>コンバータ本体・Cソース・RAINBOW.TBLのダウンロード

※追記 Borland C++ Compilerの場合はfcraincv.cの1行目のstdafx.hのインクルードをコメントアウトしてください。
 変数宣言部(使用されない変数代入等)でアラートは出ますが、一応コンパイルできます。


おまけ パターンテーブルの配置
文字コード番号テーブル上のオフセット文字列
00〜0900〜120123456789
0A〜2314〜46ABCDEFGHIJKLMNOPQRSTUVWXYZ
274Eスペース[ ]
28〜4050〜80あいうえおかきくけこさしすせそたちつてとなにぬねの
41〜5582〜AAはひふへほまみむめもやゆよらりるれろわをん
57〜61AE〜C2ゃゅょっ〜、。!?「」
63C5


TOPに戻る