update 2004.4.18

ばんがいへん1 コンバータの作り方
〜その1 はじまり〜


概要
本編「コンバータ」はゲームを対象とした説明のため、そのデータを所持していない場合は理解が難しく、
「コンバータを作ろうとするきっかけ」には少々力不足です。
そこで番外編である当コンテンツでは「きっかけ」になるためのサンプルを公開します。
>>コンバータ「きっかけサンプル」のダウンロード

きっかけサンプルとは?
うちのページの由来ですな〜
きっかけサンプルは、上の画像の元となる独自の文字コードを用いたメッセージデータ(MESSAGE.DAT)を変換して、
可視(文字コードShift-JIS)テキストデータを出力するサンプルです。
ここで行うチュートリアルは実際にファミコンやスーパーファミコンのROMデータ等から
テキストデータを抽出(変換)する際の手順と全く同じものになります。

つまり、この項の内容を理解できれば誰でもテキストコンバータを作れます。
(無論、プログラミング言語の知識は必要ですが、概要がわかればBASICでもCでもPerlでも構いません)。

サンプルの中身
ファイル CONV_SMP.LZH のアーカイブを展開した中身は以下のファイルになります。
MESSAGE.DAT
表示画面.PNG
CONV_SMP.EXE
CONV_SMP.CPP
CONV_SMP.TBL
… 独自の文字コードを用いたメッセージデータ。
… メッセージデータを表示している画面の画像(上の画像と同じものです)
… MESSAGE.DATを読み込んでテキストを出力するコンバータ本体サンプル。
… コンバータ本体のC++ソースコード。
… コンバータで利用する変換用フォントテーブル。

今回から、数回に分けて「解析→テーブル作成→プログラミング」の手順を説明してゆきます。
第1回は画面(画像)を分析しながら、フォントテーブルを作るです。

フォントテーブルを作る理由
「変換が簡単になるので都合が良いから」です。
詳しいことは第2回で説明しますので、今はとりあえず気にせずに作りましょ〜。

GCCODEできっかけサンプルを開く
こちらでGCCODEを落としてアーカイブを展開してGCCODE.EXEを実行したら、
ターゲットファイルの指定で、きっかけサンプルの MESSAGE.DAT を読み込ませます。
(ファミコンソフトの解析の場合などはここでROMを読ませるわけですね)。
その状態で解析スタートです。
(GCCODEの使い方は同封マニュアルをご参照ください。そんなに難しくないので…)。

画像(画面)を元に、英字を解析
まず1行目 「IRREGULAR CHILD」
カッコ記号1文字 + 英字9文字 + スペース1文字 + 英字5文字 + カッコ閉じ記号1文字ですが、
このうち IRREGULAR をGCCODEでサーチすれば英字の文字コードを得ることができます。
英字以外には記号と平仮名しか使われていないところを見て、
1バイトコードであると仮定してためしに検索すると…
-- BYTE型 文字コード検索 --
ファイル読み込み ... 完了
データ解析開始
(候補発見 001) OFFSET 00000001 : 12 1b 1b 0e 10 1e 15 0a 1b 
文字列テーブル出力(アルファベット)
 A:0a B:0b C:0c D:0d E:0e F:0f G:10 H:11 I:12
 J:13 K:14 L:15 M:16 N:17 O:18 P:19 Q:1a R:1b
 S:1c T:1d U:1e V:1f W:20 X:21 Y:22 Z:23
 差異 : SJIS A(0x41) - 0x0a = 0x37

解析終了
これで英字の文字コードが取得できましたが、さらにカッコやスペースの文字コードも調べてしまいましょう。
旧GCCODEではバイナリエディタと併用して調べていたのですが、その手間を省くために
今のバージョンでは「フィルタリング出力」が拡張されたので、
これを利用して1行目の範囲を出力します。
オフセット : 00000000〜00000010
差異 : 37
バイトサイズ : 半角
非表示文字をスペースに置き換え : そのまま
文字間にスペース挿入 : そのまま
オフセットと16進データを付加 : チェック
この設定で「ログ出力」をクリックします。
-- フィルタリング出力(ログ) --
 範囲 : 00000000 〜 00000010
 バイトサイズ : 1(半角文字)
 基準値 : 37 (加算)
== オフセット & 16進データ付加出力 ==
00000000 : 24 [
00000001 : 12 I
00000002 : 1B R
00000003 : 1B R
00000004 : 0E E
00000005 : 10 G
00000006 : 1E U
00000007 : 15 L
00000008 : 0A A
00000009 : 1B R
0000000A : 28 _
0000000B : 0C C
0000000C : 11 H
0000000D : 12 I
0000000E : 15 L
0000000F : 0D D
00000010 : 25 \

出力完了
これによって、カッコ(「) → 24、スペース → 28、カッコ閉じ(」) → 25であるということが判明します。
現段階のフォントテーブル *=未知 / □=スペース
   +0+1+2+3+4+5+6+7+8+9+A+B+C+D+E+F
00 **********ABCDEF
10 GHIJKLMNOPQRSTUV
20 WXYZ「」**□*******

平仮名の文字コードを得る
さて、改行の後に平仮名で ふと、そんなことばをおもいだした。
記号が入り混じるこの文字列をそのまま検索しても当然見つかりませんので、
GCCODEの機能ワイルドカードで必要箇所だけを見るようにします。
検索文字列:フト?ソ?ナコト
記号文字「、」と依存の強い「ん」をスキップし、続きの「ばをおもいだした。」については、
濁点文字などが含まれるため、あえてサーチしません。
これでBYTE検索を実行すると…
-- BYTE型 文字コード検索 --
ファイル読み込み ... 完了
データ解析開始
(候補発見 001) OFFSET 00000013 : 4b 43 26 3e 5d 44 39 43 
文字列テーブル出力(半角カナ 暫定)
 ア:30 イ:31 ウ:32 エ:33 オ:34 カ:35 キ:36 ク:37 ケ:38
 コ:39 サ:3a シ:3b ス:3c セ:3d ソ:3e タ:3f チ:40 ツ:41
 テ:42 ト:43 ナ:44 ニ:45 ヌ:46 ネ:47 ノ:48 ハ:49 ヒ:4a
 フ:4b ヘ:4c ホ:4d マ:4e ミ:4f ム:50 メ:51 モ:52 ヤ:53
 ユ:54 ヨ:55 ラ:56 リ:57 ル:58 レ:59 ロ:5a ワ:5b ン:5c
差異 : KANA ア(0xb1) - 0x30 = 0x81

解析終了
オフセット 00000013 にヒットしました。
上のテーブルは完璧なものではないので、あくまで「参考程度」に見ておくとして、
この段階で記号文字「、」は 26 であり「ん」は 5D であることがわかります。

前回のカッコ閉じ(」)がオフセット 00000010 で終わっていたのに対して、
この「ふと、そんなことばを〜」はオフセット 00000013 から始まっています。
つまりオフセット 00000011〜00000012 には「改行コードがある」と断定できます。
1行目と2行目の間には隙間があるので、改行が2個並んでいると考えるのが妥当でしょう。

そしてここから基本的な解析は一気に終局に向かいます。
ここより後の文章には「1」「。」「ー」という文字以外は全て発見済みの文字しか使われていないので、
改行(オフセット00000011)から最後の「つかわせてもらった。」までの、
この範囲をフィルタリング出力機能で一気に吐かせて穴を埋めます。
オフセット : 00000011〜00000066
差異 : 81
バイトサイズ : 半角
非表示文字をスペースに置き換え : チェック
文字間にスペース挿入 : そのまま
オフセットと16進データを付加 : チェック
この設定で「ログ出力」をクリックします。
-- フィルタリング出力(ログ) --
 範囲 : 11 〜 66
 バイトサイズ : 1(半角文字)
 基準値 : 81 (加算)
== オフセット & 16進データ付加出力 ==
00000011 : FF  
00000012 : FF  
00000013 : 4B フ
00000014 : 43 ト
00000015 : 26 ァ
00000016 : 3E ソ
00000017 : 5D  
00000018 : 44 ナ
00000019 : 39 コ
0000001A : 43 ト
0000001B : 6F  
0000001C : 5C ン
0000001D : 34 オ
0000001E : 52 モ
0000001F : 31 イ
00000020 : 6A  
00000021 : 3B シ
00000022 : 3F タ
00000023 : 27 ィ
00000024 : FF  
00000025 : 24  
00000026 : 54 ユ
00000027 : 36 キ
00000028 : 48 ノ
00000029 : 35 カ
0000002A : 44 ナ
0000002B : 3F タ
0000002C : 25 ヲ
0000002D : 43 ト
0000002E : 31 イ
0000002F : 32 ウ
00000030 : 3A サ
00000031 : 37 ク
00000032 : 4A ヒ
00000033 : 5D  
00000034 : 45 ニ
00000035 : FF  
00000036 : 6D  
00000037 : 42 テ
00000038 : 36 キ
00000039 : 3F タ
0000003A : 39 コ
0000003B : 43 ト
0000003C : 6F  
0000003D : 48 ノ
0000003E : 01  
0000003F : 41 ツ
00000040 : 6A  
00000041 : 38 ケ
00000042 : 6E  
00000043 : 26 ァ
00000044 : 3E ソ
00000045 : 48 ノ
00000046 : FF  
00000047 : 4A ヒ
00000048 : 70  
00000049 : 36 キ
0000004A : 60  
0000004B : 55 ヨ
0000004C : 35 カ
0000004D : 5E  
0000004E : 3F タ
0000004F : 48 ノ
00000050 : 6D  
00000051 : 26 ァ
00000052 : 77  
00000053 : 5F  
00000054 : 66  
00000055 : 48 ノ
00000056 : 44 ナ
00000057 : FF  
00000058 : 4E マ
00000059 : 33 エ
0000005A : 43 ト
0000005B : 3B シ
0000005C : 42 テ
0000005D : 41 ツ
0000005E : 35 カ
0000005F : 5B ワ
00000060 : 3D セ
00000061 : 42 テ
00000062 : 52 モ
00000063 : 56 ラ
00000064 : 5E  
00000065 : 3F タ
00000066 : 27 ィ

出力完了

かなり大きい出力ですが、
サンプル画像
元画面の画像と、出力結果を比較しながら穴を埋めてゆきます。
00000011 : FF =改行
0000001B : 6F =「ば」
0000001C : 5C =「を」
00000020 : 6A =「だ」
00000023 : 27 =「。」
00000036 : 6D =「で」
0000003E : 01 =「1」
00000042 : 6E =「ど」
00000048 : 70 =「び」
0000004A : 60 =「が」
0000004D : 5E =「っ」
00000052 : 77 =「ぺ」
00000053 : 5F =「ー」
00000054 : 66 =「じ」

そしてこれをフォントテーブルに反映させると…
現段階のフォントテーブル *=未知 / □=スペース
   +0+1+2+3+4+5+6+7+8+9+A+B+C+D+E+F
00 *1********ABCDEF
10 GHIJKLMNOPQRSTUV
20 WXYZ「」**□*******
30 あいうえおかきくけこさしすせそた
40 ちつてとなにぬねのはひふへほまみ
50 むめもやゆよらりるれろわをんっー
60 が*****じ***だ**でどば
70 び******ぺ********

これで事実上「画像に表示されている文字コードは全て得た」ということになります。
また「表示されていない文字」も、フォントの並びを見れば「*」の箇所に何が入るかわかるはずです。

ちなみに実際にテキストコンバータを作る際にはわかっていても放っておくという手もあります。
実際にフォントテーブルを参照して出力するプログラムを組んでみて、
出力時にオフセット+バイナリデータ+変換後文字データを出力するようにします。
そして出力したテキストをエディタで開き「*」の文字を検索するのです。
大抵、解析段階で見つけきれなかった記号や漢字と思われる箇所がいくつか見つかるので、
前後のテキストから予想できる文字をフォントテーブルに反映させます。
再び出力して「*」を検索……と繰り返し、最終的に見つからなくなるまで繰り返せば、
理想的な出力結果を得ることができます。

あとは
MESSAGE.DATを読み込んで、上のフォントテーブルを参照しながらテキストデータを出力すれば、
テキストコンバータの完成です。

次回は、フォントテーブルを用いることで変換できる理由・概念の解説です。
いきなりプログラムを組むのも良いですが、動きを知ることも大切ですので(^^
では、おたのしみに〜

>>第2回へ

TOPに戻る