Doc.15 PCゲーム「雫」の移植方法(1)

「雫」(しずく)とは、1996年にLeaf(現アクアプラス)が企画・販売していて、ノベルタイプの18才以上を対象としたゲームです。とある業界(汗)には大変有名な作品で、特に「雫」はリーフ・ビジュアルノベル・シリーズ三部作(雫、痕、To Heart)の1つと言われています。

 

詳しい歴史についてはWikipediaを参照してください。昔のゲームなので今に比べて機能も随分劣っていると思います。今回、移植対象としているものはリメイク前なのでグラフィックは16色、メモリも最大で640KBしかないPCで動いていました。

GBAに移植したくなった理由

インターネット上では一時期、ものすごい勢いでPCゲームを別プラットフォーム(りなざう、Linuxなど)に移植するブームが起きていました。そのときに詳細な解析資料がインターネットに公開され、当時は市販レベルのアドベンチャーゲームの中身が見れると思って大変驚いたものです。

 

ただ残念なことに、そのブームは2001〜2004年までを境にぱったり終わってしまい、2007年現在では解析資料の消失が目立ってきています。このまま消えていくのはあまりにもったいない。この資料を元に自分のつたない技術でもいいから移植してみようと思ったわけです。ほかにも以下のような理由がありました。

  • アドベンチャーゲームの仕組みを知りたかった。
  • ついでにスクリプトエンジンの勉強をしたかった。
  • 解析資料が充実していて、内容も大変読みやすかった。
  • UNIX系でオープンソースがあり、設計方法を学びたかった。
  • GBAに移植してもスペックに十分耐えられそうだった。
  • その方面のダメ人間に仲間入りをしたかった(ぉ。
clip_1.png

GBAの移植例

移植に関しては、いろいろな方がすでに実践していらっしゃっています。どのようなGBAの機能を使っているか調べてみました。既にできあがっているものを見れるというのは、大変心強いです。

  • 細雪 (大賀 きゆき 様)
    • 移植対象は「痕」
    • Mode4(Frame x 2, 256色)
    • 文字表示にはSpriteを使用。画面には同時に128文字まで。
    • 色番号は、背景(0〜127の128色) + キャラクター(128〜255の128色)。
    • 背景だけフェードイン、フェードアウトすることが容易。
    • 音楽はDirectSoundで再生。
  • 掌でひぐらしがなく 体験版 (帽子屋インサイド 様)
    • 移植対象は「ひぐらしのなく頃に 体験版」
    • Mode3(32768色)
    • 文字表示にはSpriteを使用。64x64サイズのスプライトを画面に数個配置。
    • スプライトバッファ領域に、文字フォントをドット単位でコピーしている様子。
    • とにもかくにも動作が機敏。なんでこんなに早いんでしょうか(^^;。
    • 音楽は独自企画。AXAKA SOUND。
  • 希望入りパン菓子 体験版 (Moonlight 様)
    • 移植対象は「希望入りパン菓子 体験版」
    • Mode3(32768色)
    • 文字表示にはSpriteを使用。32x16サイズのスプライトを画面に数個配置。
    • エフェクト処理がとてもきれいなことが特徴。
    • 音楽はDirectSoundで再生。


調べていて、だいたいですが以下のようなことがわかりました。

  • 画面は一番きれいなMode3が良い。エフェクト処理はフェードイン、アウトぐらいにする。
  • ROMのサイズは最大で32MBまでなので、ゲームの容量を抑えなくてはいけない。
  • GBA上で処理を軽くする為には、コンバータ側で処理しやすいデータ構造に変換しておかないとダメ。
  • メッセージ表示はスプライトで行った方が速い。退避もラクらしい。

移植するに当たって必要なこと

移植というはやったことがない人からみると随分漠然としたものです。自分もそうだったのでよくわかります。そこで肝心なのは、移植するための視点を持ってみることだと後で気がつきました。ある意味でゲームとは「小さな機能がよりあつまり、構成されているものだ」といえます。アドベンチャーゲームを思い浮かべてみると、背景画像がって、キャラクターがいて、メッセージウィンドウなどがあって・・・などと言うことができます。プログラマからみると、アドベンチャーゲームの構成している要素は次のように移るらしいです。

  • 画像
    • 背景
    • キャラクター
    • エフェクト
    • 音楽
    • 効果音
  • プログラム
    • メッセージの表示
    • メッセージの早送り
    • セーブ、ロード
    • オープニング、エンディングなど

小さな機能の構成がゲームを作っているように、移植するときも小さな単位ごとにしていった方が簡単です。ここでは画像、音、プログラムを単位としてGBAに移植していくことにします。

資料集め

ゲーム本体の.exeファイルを逆アセンブルしていって、全体像を把握する方法もありますがそれはあまりに無謀です。「雫」に関しては、既にその手にかけてエキスパートな職人さんが解析を終えていて、さらにwindowsユーザには馴染みのデータフォーマットに変換するプログラムも用意されていました。ちなみに当方は解析についてはサッパリなので、資料がそろわなかった場合移植することを断念していたと思います(^^;。

  • 音(音楽)
    • Sox - CD-DA形式なのでをWAVファイルに変換する。
  • 音(効果音)
    • リーフ独自のフォーマット?wav形式に近いらしいがよくわからないので保留。
  • プログラム(スクリプト)
    • スクリプトとは「表示するメッセージや、背景の表示や音楽の再生などのタイミング」が書かれているものです。詳しいことは後述します。
  • アーカイブ
    • Leafpak - .pakアーカイブの分割ツール。
    • アーカイブとは「背景、キャラクター、効果音、スクリプトデータ」を、1つのファイルにまとめているもののことです。上記の素材を個々に処理をする前に、アーカイブファイルの中身を分割する必要があります。


このような情報から、作業の流れが自然と浮かび上がってくると思います。

  • BMP形式は、gritを使ってGBA上に表示する。
  • wav形式は、soxを使ってGBA上に再生する。
  • その他の形式は、そのままバイナリデータとしてROMにつける。

データフォーマット

以下にデータフォーマットを表します。すでに変換プログラムが用意されているので知る必要はないのですが、ここでは簡単に概要のみを説明します。*1

アーカイブファイル

8Bマジックナンバー("LEAFPACK")
1Wファイルの数
?ファイルのデータ
?ファイルの一覧情報
  • ファイルの一覧情報は、登録されているファイル数分存在します。1つのファイルにつき24バイトです。
    struct {
    	u8  name[12]; //ファイル名(8+4)
    	u16 pos;      //ファイルの先頭
    	u16 len;      //ファイルのサイズ
    	u16 next;     //次のファイル先頭位置
    } ST_FILE_INFO;
  • ファイル情報については以下のように読み込みます。
    fseek(fd, -ファイル数*24, SEEK_END);
    fread(fd, buf, ファイル数*24)
  • 各データは暗号化されているので、以下の位置の11バイトをkeyとして復号します。詳細が知りたい場合、Leafpakのソースコードを読んでみて下さい。
    ファイルの先頭 + ファイルサイズ - 24 * ファイルのデータ数

画像ファイル(.lfg)

画像は16色で、24バイトのパレットを使用します。基本はリングバッファのLZ法です。バッファサイズを4096バイト、初期状態は全て0、バッファへの書き込み開始位置は0xfeeです。読み取る前にアーカイブのkeyで復号する必要があります。

8Bマジックナンバー("LEAFCODE")
24Bパレット
2W開始位置(x0,y0)
2W終了位置(x1,y1)
1B方向
1B透明色の指定
2B???
1L展開後の長さ
?B圧縮データ
  • パレット - 1つの色要素毎に4bit(16階調)でRG BR GB ...といった感じで2色分を3Bに収めてます。
  • 開始、終了位置 - 横は8ドットを1単位とした数です。よって横のドット数は、(x1 - x0 + 1) * 8となります。
  • 方向 - 展開後の列がどの方向に進んでいくかの指定。0ならば縦、1ならば横。
  • 透明色の指定 - 0xffならば透明色はなし。
  • 詳細が知りたい場合、lfview(X11用リーフ画像ローダ)のソースコードを読んでみて下さい。

スクリプトファイル

スクリプトファイルには大きく分けて、イベントと、メッセージという2つ構造に分かれています。データの最初の部分(イベントデータ、メッセージデータの位置)以外はリングバッファのLZ圧縮がされており、さらにbit反転されています。

1Wイベントデータの位置
1Wメッセージデータの位置
6W?
?イベントデータ
?メッセージデータ
  • イベントデータ、メッセージデータの位置には、0x10を掛けた値を使用します。
  • 以下にイベントデータの詳細を表します。サイズが2バイト以上のコマンドは、パラメータがその後に存在します。
    コマンドサイズ説明
    0x001ブロック終了
    0x013特殊効果(画面)
    0x032不明
    0x044別スクリプトへジャンプ
    0x05?選択肢
    0x061不明
    0x071前選択肢に戻る位置保存
    0x0a2背景のみロード
    0x142画面クリア
    0x162Hシーンロード
    0x223キャラクタロード
    0x0a2背景のみロード
    0x142画面クリア?
    0x162Hシーンロード
    0x223キャラクタロード
    0x243キャラクタロード2?
    0x281選択肢に存在するデータ
    0x382表示処理(画面に反映)
    0x3d4if文
    0x3e4if文(否定)
    0x473フラグの値設定
    0x483フラグの加算
    0x542テキストメッセージ
    0x5a1不明
    0x5c2不明
    0x601不明
    0x613不明
    0x622不明
    0x631不明
    0x641不明
    0x651不明
    0x661不明
    0x6e2BGM再生
    0x6f1不明
    0x731不明
    0x7c2エンディング関係(不明)
    0x7e2エンディング番号設定
    0x7d2エンディングBGM&起動
    0xff1本来アクセス不可
  • 以下にメッセージデータの詳細を表します。サイズが2バイト以上のコマンドは、パラメータがその後に存在します。
    コマンドサイズ説明
    & 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'7Hシーン
    'M'2BGM再生
    'P'2-4PCM関連(効果音)
    'X'2不明
    's'2表示速度指定?
  • スクリプトファイルの処理の順番は、まずイベントデータの先頭を順番に読み込み、BGM, フラグ, 背景, キャラクタなどの設定を済ませます。準備がととのったところでテキストメッセージコマンド(0x54)を発行し、パラメータを使ってメッセージデータのオフセットを読み込みます。
  • メッセージデータに読み込みが移っても、メッセージ終了('$')コマンドが来るまでメッセージデータを読み進めます。$が発行された場合、次のイベントデータ(テキストメッセージコマンド(0x54)発行後)のコマンドを読み込みます。
  • メッセージとイベントの命令では重複しているものもあります。

移植手順

移植の流れは以下のようになります。実際の作業はこんなに綺麗に分かれているわけではなく、色々作業を同時進行していました。

  • スクリプトエンジン部分のプロトタイプを作る
  • .gbaファイルのサイズを確定させる
  • 画像ファイルを表示するプログラムの作成
  • 音楽ファイルを再生するプログラムの作成
  • スクリプトファイルを実行するプログラムの作成

スクリプトエンジン部分のプロトタイプを作る

途中で挫折しないようにする為、一番難しいことを最初にやってしまうことにしました。このプログラムは「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ファイルのサイズを確定させる

次の問題は.gbaファイルの最大サイズ32MBに収まるように考慮することになります。この問題については(雫とゲームシステムがほぼ同じ)痕が移植を成功していることもあり、あまり大きな問題にはなりませんでした。ただし、痕を移植しました大賀 きゆき 様の日記から伺いできるように、様々なトレードが絡んでいることが窺い知れます。たとえば、データのサイズを抑える為に圧縮してしまうと解凍に時間がかかり、無圧縮にした場合はデータのサイズが大きくなってしまいます。理想はPCゲームと同じ品質であることが望まれます。

画像ファイルを表示するプログラムの作成

ここでのポイントは画像ファイルを表示するという目的以外にも、データ容量について調べてみることです。作業の流れはだいたい次のようなものです。

  • アーカイブファイルを展開する
  • その中の画像ファイル(*.lfg)だけをビットマップファイルに変換する
  • GBAの画面用に240x160リサイズをする。無圧縮の場合、容量が大きくなるのでLZ圧縮をしておく
clip_2.png

以下にその結果を表します。

データの形式サイズ備考
lfg8bpp3.94MB
bmp8bpp15.1MB
bmp(resize)24bpp13.5MB倍率約37%
img(GBA用)15bpp3.98MBLZ圧縮あり。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 -b29.8MB
-r 8192 -c 1 -b22.2MB

次にGBAで定評のあるライブラリを使用してみます。今回はGSM player for GBAと、8ad codecを調べてみることにしました。ビットレートの変更はGSMはできなかったのですが、8adはちょうど容量に収まりそうなパラメータに変更しています。

名前容量
GSM10.1MB
8ad18MB

GSMはビットレートの低い特有のシャワシャワ感(?)が出てしまうので少し気になっていたのですが、8adはあまり気になりません。決定打となったは、8adの広告に「6 percent of the GBA's CPU time.」という記載があることと移植しやすいソースコードであったことです。詳細が知りたい場合、それぞれの公式サイトを見てみることをオススメします。サンプルプログラムは旧サイトのNo.77を参照してください。

スクリプトファイルを実行するプログラムの作成

Doc.16 PCゲーム「雫」の移植方法(2)に続きます・・・。


*1 yatsushiさんの痕解析結果(閉鎖)http://member.nifty.ne.jp/yatsushi/を参考にさせていただきましたm(__)m。

添付ファイル: filescn3.c 968件 [詳細] fileclip_2.png 466件 [詳細] fileclip_1.png 641件 [詳細]

Last-modified: 2008-10-04 (土) 15:06:29 (3907d)