「りも」トップページへ
TIPS index へ

ファイルアーカイブとレジストリ制限の実装

Feb.29.2000

ファイルアーカイブとレジストリ制限の実装について。
ゲームのプログラムとデータ部分を明確に分離するとともに、データ部分に おいてファイル数が多数になり管理が複雑にならない様データファイルを アーカイブする構造を模索する。
また、データの読み込みに対し制限を設け、あらかじめ設定してある レジストキーがないとデータにアクセスできない仕組みを実装する。


TIPS ではなくて製作したプログラムライブラリの解説といったところです。 なにかの参考になるでしょうか。


・ポリシー、…のようなもの

今テキスト中心のアドベンチャータイプのゲーム製作を想定します。
このようなゲームタイトルの場合、プログラム部分はシナリオインタープリタであり、 ゲームコンテンツ本体はシナリオスクリプトと画像データによる多数のデータ群で あることがほとんどです。 そういったゲームタイトルの場合、シナリオスクリプトや画像データを 1ファイル ずつディスクにおくと無数(場合によっては千とか万)のファイルを置くことになり 大変美しくありません。ユーザーにおいても扱いにくいと思われます。
そこで tar みたいに複数ファイルをひとまとめにし、巨大な 1アーカイブファイル としてしまうことを提案します。これによってデータファイル管理をすっきりと簡略化 するとともに、データ指定(差し替え)によって複数のコンテンツ(シナリオ)を提供 出来るような形態を提供します。このことは、プログラム部分は共通で、データのみを 入手すれば新しいコンテンツが楽しめるという将来的な供給形態を示唆します。
それと併せて、データにスクランブルをかけ、特定のキーコード(レジストキー)が 無いとアクセスできないデータが出来るような仕組みも提供します。 これは将来的にコンテンツを有料化しようとした場合、レジストキーを販売する という形態を採るためです。
あくまでプログラム(ソース)は無償で、ゲームコンテンツをなすデータにのみ 課金するというところにこだわります。そのため、ソースがオープンであっても レジストキーが効力を発揮せねばなりません。レジストキーの判別とその許諾が プログラム側にあった場合、ソースを眺めることでレジストキーがわかってしまい 制限を設ける意味が無くなります。そこでプログラム側への依存を極力減らし データ内にレジストキーの仕組みを持つ事によって問題を解決します。 どのような形で制限がかかっているかはソースを見るとわかりますが、実際に 利用するには特定の文字列を入手しないと手がでないと。
そのレジストキーに関する仕組みも実はあんまり機密度は高くないのですが、 それはそれで良しとしてしまいます。どんなに手を凝らしても暴こうと熱意を 燃やす人の前では無意味に近いものですから (^^;
そのへんは程度問題で、そこそこのスクランブルで一般的な利用者が利用する 分には十分制限となるレベルならいいんじゃないでしょうか。(割と御気楽に)


・ファイル構造オーバービュー

ファイルの束ね方と管理の仕方について。
データファイルはひとまとめの 1ファイルで、その中にデータファイルが ぐっちゃりと詰まっています。もちろん、アーカイブのどのあたりに何が 入っているのかっていう情報があって、その情報をみて目的のデータを 取り出します。このデータの位置情報をディレクトリといいます。
データにはレジストキー無しで取り出せるものと、レジストキー無しでは 取り出せないものの 2種類に分けます。これにより 「ここから先をプレイしたかったらレジストキーを(買って)入手してね」 といった構造にすることが可能になります。もちろん、レジストキーなし のフリー部分のみという構造にも出来ますからフリーウェアの配布にも 利用できます。
ファイル内にはディレクトリデータとデータ群の 2種類があって、それぞれに フリー/制限付きがありますから都合 4つのセクションがあります。
ファイルの先頭は必ずフリーエリアのディレクトリデータでプレーンテキストに なっています。以後のデータはバイナリで gzip で圧縮したファイルを結合した 形になっています。ファイルの最終は制限エリアのディレクトリデータですが、 このディレクトリデータにも gzip 圧縮とレジストキースクランブルがかかって いて、レジストキーがないとどのようなファイルが制限エリアにあるのかが 見えない様になっています。

[File format block]


・ディレクトリファイル

ディレクトリファイルにはデータにアクセスするための情報が書かれています。 データの情報は「データ名」「どこから入っているか」「どれくらいのサイズか」 の3つで、アプリケーションからデータへのアクセスはデータ名を与えることに よって行われます。データ名は元のファイル名というわけではなくデータを アーカイブするときに任意で付けたものとなります。
ディレクトリファイルは以下の様なフォーマットです

1行目
先頭 4byte マジックナンバー "RRDA"
その後ろは改行まで無視。バージョンナンバーとか書いておく
2行目
当ディレクトリファイルのサイズ
16進数8桁
3行目
制限パートディレクトリファイルの位置とサイズ
16進数8桁(位置),16進数8桁(サイズ)
4行目以降
データ情報
16進数8桁(位置),16進数8桁(サイズ),データネーム
最終行
終了コード "END"
制限パートディレクトリファイルにおいて 2行目、3行目の情報は意味を持ちません ので 00000000 (ヌルコード)で埋められています。また、制限パートディレクトリ ファイルには他の制限パートデータと同様 gzip 圧縮とレジストキースクランブル がかかっています。
実際のディレクトリファイル例


・データ

データはフリーエリアのディレクトリの直後に繋がっており、個々のデータは gzip にて圧縮された *.gz ファイルです。その *.gz ファイルを単純に 結合して一つのファイルとなっているだけです。


・レジストキーの仕掛け

レジストキーは今回のサンプルでは 4文字の文字列になっています。 (後日改良予定)
このレジストキーがどのようにデータに影響しているかを解説します。
制限領域にはレジストキーが xor にて練り混んであります。それだけじゃ ちょっと単純なので 0xaa (10101010B) もついでに練り混んであるので、 ちょっと眺めただけではわかりにくいと思われます。
レジストキーはファイルのトップからサイクリックになっていて、 先頭から n byte 目のデータは元のデータを d として

  data = (d ^ 0xaa) ^ key[n%4]
といった感じでスクランブルされています。
サンプルはレジストキー 4byte 固定になっていますが、gz 形式のヘッダーから 推測できる部分が多く 4byte 程度では全然スクランブルにはなりません。 実際には 8〜16byte のキーを利用することになると思います。


・ファイルアーカイバ

以上のフォーマットにそったファイルを生成するアーカイバを製作します。
ruby で目的のスクリプトを作成しました。
makerrda.rb
アーカイブしたいファイルをリスト記述したテキストファイルを makerrda.rb に食わせるとファイルを圧縮し、自動的にリストを作った後アーカイブします。
(例、になっていない例)

$ ./makerrda.rb archive_list.txt
ファイルアーカイブの為のリストのフォーマットは以下の通り。
\name filename
出力するアーカイブファイルのファイル名を指定します。
\key hoge
レジストキーを指定します。
このバージョンでは 4byte の文字コードのみを受け付けます。
\restriction
これ以降のファイルは全て制限エリアのものとして扱います。
name, filepath
name というデータ名で filepath のファイルをアーカイブします。
#
# で始まる行はコメントです。空改行も無視されます。

実際のアーカイブリストファイル例


・zlib とデータの解凍

さて、ファイルを gz 形式で圧縮して束ねることによりファイルサイズを 小さくすることが出来ていますが、これを元に戻すにはどうすればよいので しょうか。
gzip 形式のアーカイブを解凍するために zlib を利用します。zlib の 使い方などについては 奥村氏の「zlib入門」を参照してください。
但し、zlib が標準で扱う圧縮ファイルは gz 形式の物ではありません。 正確に言うと gz 形式にはヘッダー部分が存在し、ヘッダーを除いた本体部分が zlib で扱う圧縮データになります。このあたりどのようにしたら良いかは zlib のソースを覗くとヒントがあります。zlib には gz ファイルを ストリームとみなして扱う事の出来る関数が存在します(gzio.c)これを 参照すると色々と参考になります。
具体的に言うと、gz 形式ファイルのヘッダーをすっとばして本体部分のみを zlib に食わせて解凍します。ただし、gz 形式のファイルを解凍するには inflateInit ではなく inflateInit2 という関数を使う必要があります。


・現在のインプリメントにおける問題点

ゲームという特性上、データ内容はなるべく機密にしておきたいところがあります。 なるべくハックされないように、データのとりまわし過程で 「テンポラリファイルに落さない」というのを目標としています。 故にデータ展開やその結果を全てメモリ上に持つのですが、その関係上 malloc, realloc, free を頻繁に使います。結果アプリケーションのヒープが 肥大するのですが、現状やむ無しとしてあります。 (ポータビリティの都合も考えて)
将来的にスマートな解決法が見付かったら手直しして行く予定です。


・サンプルソース

以上のようなファイルシステムをインプリメントしたソースコードです。
lm_rrda.c
lm_rrda.h

ソースの利用/配布条件は GPL に準じます。


・あとがき

「りも」プロジェクトにとっては結構重要な部分だったのでそれなりにしっかり 作ったパーツだったのですが、何を解説していいのやらわからなくてかなり 読みにくい解説だったと思います。
以上の解説でわからない事とかありましたら御気軽に御連絡ください。


rero2@yuumu.rim.or.jp