2.3. Cygwin

2.3.1. 基数変換ツールとして利用
2.3.2. 連番生成ツールとして利用
2.3.3. 構造体データ配列をテキストダンプする
2.3.4. DisPel のラッパースクリプトを書く

図 2.6 Cygwin Console Window

Cygwin Console Window

Linux 環境でゲーム機エミュレータが可能な読者は、 この節を読むのを飛ばして結構だ(そんなに長いものでもないが)。

サイズの巨大な逆アセンブルしたテキストファイルをいくつも抱え込むと、 どうしても高度なテキスト処理能力が欲しくなる。 そして、それは愛用のテキストエディタが処理する以上の能力がなければ意味がない。 そこで、知名度も実績も十二分にあるテキスト処理用の GNU ツールを利用することを考えた。 Windows 環境で解析作業をするので、Cygwin をインストールしておく。

各種ツールを利用するのは Cygwin コンソール上からだけとは限らない。 現代的な各種 IDE や高水準なテキストエディタが提供する「外部プログラム呼び出し」機能、 すなわち利用者がコマンドラインを指示し、 メニュー項目選択時もしくは即時にそれらを起動するという作りになっている。 そこから awk, sed のような軽いテキスト処理コマンドを呼び出してもよし、 逆アセンブリコード処理専用のシェルスクリプトを書いて、それを呼び出すもよし、だ。

高水準アプリがマクロ言語によるテキスト処理機能を提供する場合もある。 しかし、本書ではそういうものを利用するという考えはしない。

以降、本書で Cygwin コンソールを利用する状況を説明する場合、シェルは Bash とする。 プロンプト (PS1) は $ で表記する。

2.3.1. 基数変換ツールとして利用

C/C++ の関数相当の機能を有する printf コマンドを利用することができる。 電卓でまかなえる陳腐な例だが、十進数を十六進数に変換するには以下のようにする。

$ printf "%X\n" 1000000 1
F4240 2

1

printf に書式文字列と 1000000 を渡す。

2

文字列 F4240 が標準出力に表示される。 これが十進数 1000000 の十六進数表現だ。

2.3.2. 連番生成ツールとして利用

しばしば連番をテキストファイルにコメントしておきたい状況になる。 そういう場合にはエクセルを起動でも構わぬが、好みで seq を用いる。

$ seq 4 1
1
2
3
4
$ seq 10 3 20 2
10
13
16
19
$ seq 0x3925 0x3C 0x3BFF | xargs printf "7E%04X\n" 3
7E3925
7E3961
7E399D
7E39D9
… 省略
7E3BF5

1

seq に単一の整数 N を与えると、1 から N まで 1 ずつ増加する連番が得られる。

2

seq に3 つパラメータを渡す場合、それぞれの意味は FIRST, INCREMENT, LAST として処理される。

3

SFC 版ドラクエ 3 の通称 3925 構造体のアドレスを書きたくなって seq を利用したとする。 バンクは皆 $7E なので入力には含めず、出力書式に込めることにした。 勇者のアドレスが $7E3925 で、ルイーダの酒場にいる順番に +$3C ずつアドレスが増えていくので、 FIRST, INCREMENT パラメータは 0x3925, 0x3C を与えた。 LAST には最後の仲間のアドレスを指定するべきだが、それを計算するのが面倒なので、とりあえず値を指定しておく。

出力結果を十六進数で表示したいのだが、seq の書式指定オプションはそれをサポートしない。 よって、前項で説明した printf にパイプラインを作る。 仲間の人数がわかっていれば、head にさらなるパイプラインを作ってもよい。

7E3925, 7E3961, … と望みの結果が得られた。

2.3.3. 構造体データ配列をテキストダンプする

ドラクエのスーファミ ROM イメージから、特定の構造体データ配列をすべてテキストダンプしたいことがある。 例えば、SFC 版ドラクエ 3 のモンスター構造体配列(5.5.1 構造体 $C20000: モンスター)を見たくなったとしよう。 まずは頭を使わずにコンソールから直接コマンドラインを入力してみよう。

$ od --address-radix=x1 \ 1
    --skip-bytes=$(( 0x20000 )) \ 2
    --format=uC \ 3
    --read-bytes=$(( 0x25 * 0xA0 )) \ 4
    --output-duplicates \ 5
    --width=$(( 0x25 )) \ 6
    dq3.smc | sed -e '$d' 7

1

od が出力するアドレスを十六進数で表示させるのオプション。

2

モンスター配列の先頭のアドレスを --skip-bytes オプションに渡す。

3

ダンプを 1 バイト単位で行い、符合なし整数として解釈させる。

4

読み込むことになる総バイト数。シェルに計算させた結果を渡す。 ここで 0xA0 はモンスター総数のつもりだ。

5

これを指定しないと、同じデータが連続して表れた場合に省略されてしまう。

6

モンスター構造体のサイズ。C/C++ で言うところの sizeof の値。

7

ロムイメージファイル名を渡してダンプする。 最後の sed は、出力の最後の行がゴミなので、それを消しているだけだ。

構造体データ配列のダンプのたびに上記のような長いコマンドラインを入力するのは面倒なので、 通常はこれを不変部分と可変部分を分離してスクリプトにしておく。 具体的には、ロムイメージファイル、オフセット、sizeof 構造体、そして配列長が可変である。 これらはスクリプトの引数として指定できるようにしておけばよい。

DisPel-d オプションによるダンプの方が指定は楽なのだが、 od--size オプションに相当する値の上限がどうやら 255 らしいので、 キャラクターのレベルアップ構造体データ等の巨大なデータのダンプができない。

2.3.4. DisPel のラッパースクリプトを書く

特定の ROM イメージに対して DisPel をコンソールから起動する作業を続けていると、 やがて位置オプションの指定が面倒なことに気付く。例えば SFC 版ドラクエ 3 の場合は -h, -s は必ず指定するし、 古いバージョンの DisPel を利用しているのならば、 ROM イメージは普通はヘッダを削っておくものだから -n も指定する。 指定するたびに違う値になるのは、逆アセンブル範囲を示す部分だけだ。 そこで、以下のようにスクリプトを書いておき、dispel を直接利用するのではなく、 これを利用することにすると多少は楽だ。

#!/bin/bash
#
# script: disdq3
# example:
#   disdq3 -b C2
#   disdq3 -r C20000-C28000

DISPEL=~/bin/dispel.exe
ROM=~/data/dq3.smc

# このスクリプトに渡されたすべてのコマンドライン引数を
# DisPel にそのまま引き渡す
#
# DisPel のバージョンが v1 未満の場合は -n は不要となるので注意
$DISPEL -n -h -s -p $* "$(cygpath -d $ROM)"