「雫」(しずく)とは、1996年にLeaf(現アクアプラス)が企画・販売していて、ノベルタイプの18才以上を対象としたゲームです。とある業界(汗)には大変有名な作品で、特に「雫」はリーフ・ビジュアルノベル・シリーズ三部作(雫、痕、To Heart)の1つと言われています。
詳しい歴史についてはWikipediaを参照してください。昔のゲームなので今に比べて機能も随分劣っていると思います。今回、移植対象としているものはリメイク前なのでグラフィックは16色、メモリも最大で640KBしかないPCで動いていました。
インターネット上では一時期、ものすごい勢いでPCゲームを別プラットフォーム(りなざう、Linuxなど)に移植するブームが起きていました。そのときに詳細な解析資料がインターネットに公開され、当時は市販レベルのアドベンチャーゲームの中身が見れると思って大変驚いたものです。
ただ残念なことに、そのブームは2001~2004年までを境にぱったり終わってしまい、2007年現在では解析資料の消失が目立ってきています。このまま消えていくのはあまりにもったいない。この資料を元に自分のつたない技術でもいいから移植してみようと思ったわけです。ほかにも以下のような理由がありました。
#ref(): File not found: "clip_1.png" at page "Ex.21"
移植に関しては、いろいろな方がすでに実践していらっしゃっています。どのようなGBAの機能を使っているか調べてみました。既にできあがっているものを見れるというのは、大変心強いです。
調べていて、だいたいですが以下のようなことがわかりました。
移植というはやったことがない人からみると随分漠然としたものです。自分もそうだったのでよくわかります。そこで肝心なのは、移植するための視点を持ってみることだと後で気がつきました。ある意味でゲームとは「小さな機能がよりあつまり、構成されているものだ」といえます。アドベンチャーゲームを思い浮かべてみると、背景画像がって、キャラクターがいて、メッセージウィンドウなどがあって・・・などと言うことができます。プログラマからみると、アドベンチャーゲームの構成している要素は次のように移るらしいです。
小さな機能の構成がゲームを作っているように、移植するときも小さな単位ごとにしていった方が簡単です。ここでは画像、音、プログラムを単位としてGBAに移植していくことにします。
ゲーム本体の.exeファイルを逆アセンブルしていって、全体像を把握する方法もありますがそれはあまりに無謀です。「雫」に関しては、既にその手にかけてエキスパートな職人さんが解析を終えていて、さらにwindowsユーザには馴染みのデータフォーマットに変換するプログラムも用意されていました。ちなみに当方は解析についてはサッパリなので、資料がそろわなかった場合移植することを断念していたと思います(^^;。
このような情報から、作業の流れが自然と浮かび上がってくると思います。
以下にデータフォーマットを表します。すでに変換プログラムが用意されているので知る必要はないのですが、ここでは簡単に概要のみを説明します。*1
8B | マジックナンバー("LEAFPACK") |
1W | ファイルの数 |
? | ファイルのデータ |
? | ファイルの一覧情報 |
struct { u8 name[12]; //ファイル名(8+4) u16 pos; //ファイルの先頭 u16 len; //ファイルのサイズ u16 next; //次のファイル先頭位置 } ST_FILE_INFO;
fseek(fd, -ファイル数*24, SEEK_END); fread(fd, buf, ファイル数*24)
ファイルの先頭 + ファイルサイズ - 24 * ファイルのデータ数
画像は16色で、24バイトのパレットを使用します。基本はリングバッファのLZ法です。バッファサイズを4096バイト、初期状態は全て0、バッファへの書き込み開始位置は0xfeeです。読み取る前にアーカイブのkeyで復号する必要があります。
8B | マジックナンバー("LEAFCODE") |
24B | パレット |
2W | 開始位置(x0,y0) |
2W | 終了位置(x1,y1) |
1B | 方向 |
1B | 透明色の指定 |
2B | ??? |
1L | 展開後の長さ |
?B | 圧縮データ |
スクリプトファイルには大きく分けて、イベントと、メッセージという2つ構造に分かれています。データの最初の部分(イベントデータ、メッセージデータの位置)以外はリングバッファのLZ圧縮がされており、さらにbit反転されています。
1W | イベントデータの位置 |
1W | メッセージデータの位置 |
6W | ? |
? | イベントデータ |
? | メッセージデータ |
コマンド | サイズ | 説明 |
0x00 | 1 | ブロック終了 |
0x01 | 3 | 特殊効果(画面) |
0x03 | 2 | 不明 |
0x04 | 4 | 別スクリプトへジャンプ |
0x05 | ? | 選択肢 |
0x06 | 1 | 不明 |
0x07 | 1 | 前選択肢に戻る位置保存 |
0x0a | 2 | 背景のみロード |
0x14 | 2 | 画面クリア |
0x16 | 2 | Hシーンロード |
0x22 | 3 | キャラクタロード |
0x0a | 2 | 背景のみロード |
0x14 | 2 | 画面クリア? |
0x16 | 2 | Hシーンロード |
0x22 | 3 | キャラクタロード |
0x24 | 3 | キャラクタロード2? |
0x28 | 1 | 選択肢に存在するデータ |
0x38 | 2 | 表示処理(画面に反映) |
0x3d | 4 | if文 |
0x3e | 4 | if文(否定) |
0x47 | 3 | フラグの値設定 |
0x48 | 3 | フラグの加算 |
0x54 | 2 | テキストメッセージ |
0x5a | 1 | 不明 |
0x5c | 2 | 不明 |
0x60 | 1 | 不明 |
0x61 | 3 | 不明 |
0x62 | 2 | 不明 |
0x63 | 1 | 不明 |
0x64 | 1 | 不明 |
0x65 | 1 | 不明 |
0x66 | 1 | 不明 |
0x6e | 2 | BGM再生 |
0x6f | 1 | 不明 |
0x73 | 1 | 不明 |
0x7c | 2 | エンディング関係(不明) |
0x7e | 2 | エンディング番号設定 |
0x7d | 2 | エンディングBGM&起動 |
0xff | 1 | 本来アクセス不可 |
コマンド | サイズ | 説明 |
& 0x80 | ? | 連続したメッセージ |
'$' | 1 | メッセージ終了 |
'r' | 1 | 改行 |
'p' | 1 | ページ更新 |
'K' 'k' | 1 | キー入力待ち |
'O' | 1 | 不明 |
'C' | 4 | キャラクタ位置交換 |
'B' | 7 | 背景ロード |
'S' | 10 | 背景付きキャラ表示 |
'D' | 4 | キャラ全消去後表示 |
'A' 'a' | 10 | キャラ3人表示 |
'Q' | 1 | 画面を揺らす |
'E' | 7 | 背景ロード(2)? |
'F' | 1 | フラッシュ |
'V' | 7 | ビジュアル? |
'H' | 7 | Hシーン |
'M' | 2 | BGM再生 |
'P' | 2-4 | PCM関連(効果音) |
'X' | 2 | 不明 |
's' | 2 | 表示速度指定? |
移植の流れは以下のようになります。実際の作業はこんなに綺麗に分かれているわけではなく、色々作業を同時進行していました。
途中で挫折しないようにする為、一番難しいことを最初にやってしまうことにしました。このプログラムは「DOSプロンプト上でスクリプトメッセージを表示する」使い捨てでしたが、アドベンチャーゲームの本質と思える部分で、一番の難関になると思います。
プロトタイプのソースコードを以下に表します。見せるために用意したものではないので、ワーニング出しまくりのヒドイものですが必要ならダウンロードしてみてください(^^;。アーカイブファイル(MAX_DATA.PAK)からスクリプトファイル(SCN002.DAT)をデコードして、メッセージを表示しています。コンパイラはBorland C++ 5.5.1 for Win32(ダウンロード版)を使用しました。
file(SCN002 DAT) event size = 40 message size = 2225 ...(中略) unknown!![cmd:31 1] [22] 42 僕は黙々とシャープペンシルの先を走らせ、青い罫 [59] 72 線が刻まれた真新しい大学ノートに、ひとつの円を描 ...(以下略)
次の問題は.gbaファイルの最大サイズ32MBに収まるように考慮することになります。この問題については(雫とゲームシステムがほぼ同じ)痕が移植を成功していることもあり、あまり大きな問題にはなりませんでした。ただし、痕を移植しました大賀 きゆき 様の日記から伺いできるように、様々なトレードが絡んでいることが窺い知れます。たとえば、データのサイズを抑える為に圧縮してしまうと解凍に時間がかかり、無圧縮にした場合はデータのサイズが大きくなってしまいます。理想はPCゲームと同じ品質であることが望まれます。
ここでのポイントは画像ファイルを表示するという目的以外にも、データ容量について調べてみることです。作業の流れはだいたい次のようなものです。
#ref(): File not found: "clip_2.png" at page "Ex.21"
以下にその結果を表します。
データの形式 | 色 | サイズ | 備考 |
lfg | 8bpp | 3.94MB | |
bmp | 8bpp | 15.1MB | |
bmp(resize) | 24bpp | 13.5MB | 倍率約37% |
img(GBA用) | 15bpp | 3.98MB | LZ圧縮あり。240x160サイズ固定 |
lfgファイルの数は195。サイズはLZ圧縮が掛っているとはいえ、無圧縮のbmpに比べて約5倍ほどの違いがあります。リサイズ時には縦横比(アスペクト)を無視して240x160のサイズに変更をしている為、若干横に太めに見えてしまうかもしれません。ここでわかったことは、オープニングアニメーション用の画像ファイルはスピードが気になるので無圧縮にして、他の画像は圧縮することで約4.5MBぐらいになると想像できます。詳細が知りたい場合、旧サイトのNo.76を参照してください。ちなみに最終版では容量に問題がならなかった為、無圧縮にしています。
「雫」はCD-DA形式の為、ターゲットはWAVファイルということになります。全25ファイル(曲)で無圧縮の場合、517MB(16bit 44khz stereo)です。当初はsoxというツールを使って変換をかける予定でしたが、容量がかなりきびしいことがわかりました。そもそも圧縮していない点で場違いみたいです(^^;。
soxのオプション | サイズ |
-r 11025 -c 1 -b | 29.8MB |
-r 8192 -c 1 -b | 22.2MB |
次にGBAで定評のあるライブラリを使用してみます。今回はGSM player for GBAと、8ad codecを調べてみることにしました。ビットレートの変更はGSMはできなかったのですが、8adはちょうど容量に収まりそうなパラメータに変更しています。
名前 | 容量 |
GSM | 10.1MB |
8ad | 18MB |
GSMはビットレートの低い特有のシャワシャワ感(?)が出てしまうので少し気になっていたのですが、8adはあまり気になりません。決定打となったは、8adの広告に「6 percent of the GBA's CPU time.」という記載があることと移植しやすいソースコードであったことです。詳細が知りたい場合、それぞれの公式サイトを見てみることをオススメします。サンプルプログラムは旧サイトのNo.77を参照してください。