無題

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
新人

無題

#1

投稿記事 by 新人 » 16年前

C++でゲームを製作中なのですが、敵の思考スクリプトやテクスチャ素材を外から参照できないようにファイルパックするツールを作りたいのですが、
ファイルを読み込んでパック化するアルゴリズムがいまいちわかりません。

必要なファイル情報はfreadで抜き出して結合させるのでしょうか?
アドバイスお願いします。

御津凪

Re:無題

#2

投稿記事 by 御津凪 » 16年前

プログラムでバイナリファイルを扱ったことはありますか?

ファイルをパックするなら、下記のようなデータ構造でまとめると簡単に出来ます。
・ファイルヘッダ部分
  0x0000 : パックされているファイル数(4Byte)
  0x0004 : 各ファイルデータへのファイル先頭からのバイトオフセット値(4Byte * パックされているファイル数)
  …以降、各ファイルデータ

・各ファイルデータ部分
  0x0000 : ファイル名(64Byte)←扱いやすいように64文字の固定長にしているが、構造を変えれば可変長も可。
  0x0040 : ファイルデータサイズ(4Byte)
  0x0044 : ファイルデータ(ファイルデータサイズ)
少々抽象的ですが、このようなデータ構造とそれを処理する読み書き処理を実装すれば、
実現が可能です。

ただし、単に一つのファイルにパックしているだけなので、
データ構造を解析されてしまえば、簡単にファイルを抜き出されてしまいます。
そのため、機密性を高めるためには、この構造に加え、データの暗号化を施す必要があります。


# ちなみに、私のサイトのところで(構造は全く違いますが)同様のファイルパックツール・ライブラリがあります。
# 動作や処理の参考までにどうぞ。

新人

Re:無題

#3

投稿記事 by 新人 » 16年前

ファイルの読み込みはfopenのバイナリモードで読み込んで、freadでデータ抽出し、fwriteでまとめて書き込む形で可能なのでしょうか?
 

御津凪

Re:無題

#4

投稿記事 by 御津凪 » 16年前

パックファイルへの書き込みならそれで可能です。

ただ、上記にあるデータ構造は、ヘッダ部分にパックしたファイル分のデータオフセット値があるので、
書き込むファイル数が事前に確定していなくてはなりません。
(別の構造にすれば、後から追加書き込みできるようにも出来ます)

これは、書き込むファイルのリストを用意してから、まとめて処理を行なえば可能です。


ファイルデータ書き込み部分だけですが、書いてみたので載せておきます。
/*
    read_fp からデータを読み出し、
    pack_fp にデータサイズとデータを書き込む。
    戻り値は正しく書き込めたかどうか。
*/
bool WriteDataToPack( FILE* pack_fp, FILE* read_fp ){
    long            size = 0;
    long            file_size;
    size_t          read_size;
    unsigned char   buffer[65536];
    // ↑バッファサイズは出来るだけ大きいほうが処理が高速化出来るが、
    //   スタックオーバーフローの問題もあるためこの程度(64K)にしておく。

    // ファイルサイズ取得
    fseek(read_fp, 0, SEEK_END);
    file_size = ftell(read_fp);
    // シーク位置を先頭に戻す
    fseek(read_fp, 0, SEEK_SET);

    // ファイルサイズを書き込む
    fwrite(&file_size, 1, 4, pack_fp);

    // 以下ファイルデータ書き込み
    do {
        read_size = fread(buffer, 1, sizeof(buffer), read_fp);
        if(read_size == 0) break;

        if(fwrite(buffer, 1, read_size, pack_fp) != read_size){
            return false; // 書き込みエラー
        }
        size += read_size;
    }while(feof(read_fp) == 0);

    return (read_size == size); // true: 成功, false: 読み込みエラー(サイズ不一致)
}
# コンパイル・実行チェックはしておりませんので悪しからず。

新人

Re:無題

#5

投稿記事 by 新人 » 16年前

書き込み方法は理解できました。

{
  CHAR[/url] ファイル名
  LONG データサイズ
UCHAR[/url] データバッファ
}

のような構造体をリスト構造のようにし、for文で書き込み行うという考え方か

それともヘッダとデータ部に分けて

ファイル名
データ部の先頭アドレス(位置)
ファイル名
データ部の先頭アドレス(位置)
     ・
     ・
     ・
データ本体
データ本体

のような構造のどちら方が読み込みしやすいのでしょうか?

御津凪

Re:無題

#6

投稿記事 by 御津凪 » 16年前

前者の場合、後からファイルを追加できますが、
ある特定のファイルを読み込もうとした場合、ファイルの先頭からチェックしていく必要があるので、
ファイルシーク処理を多用する必要があります。
(その分ファイルへのアクセス量が増える)

後者の場合、(上記事にも書きましたが)後からファイルを容易に追加することは出来ませんが、
ヘッダ部分を一度に読むだけで、パックしたファイルの情報が全て読み取れる利点があります。


書き込む場合は前者の形式が楽で、
読み込む場合は後者の形式が楽、
と、どちらも長所・短所がありますが、私は後者の方を薦めます。

ヘッダを読み込むだけでひとまず何のファイルがあるか分かる上、
CPU負荷やファイルアクセスなど、色々な負担も軽減できます。

新人

Re:無題

#7

投稿記事 by 新人 » 16年前

後者の場合、ヘッダを先に書き込むと思ったですが、データの先頭アドレスを取得するにはデータを先に書き込み、変数に格納する形になるのでしょうか?

御津凪

Re:無題

#8

投稿記事 by 御津凪 » 16年前

ファイル内のとある位置のデータを示す時、アドレスの0ベース位置はファイルの先頭位置にあります。
(メモリのアドレスとは違いますので注意・ここではオフセットと呼んだ方が良いかも知れません)
ファイルデータへのオフセット位置は、

データ先頭へのオフセット位置 = ヘッダ部のサイズ + 今までのデータの格納されたサイズ

となるので、ヘッダのサイズ・書き込むファイルの順番とサイズが決まっていれば、
これらの情報を使って先にヘッダ部を書き込むことが出来ます。
逆に、これらが決まっていないと最後にヘッダ部を書き込む形になります。
(例外もありますが話がそれるので割愛します)


上記の式ですが、分かりやすく例えてみます。
ヘッダサイズが 100 Byte とし、ファイルを以下の順番で格納(読み込み)するとします。
・ファイルA(64 Byte)
・ファイルB(32 Byte)
・ファイルC(96 Byte)

このとき、
ファイルAのデータ先頭へのオフセット値は、 100 (ヘッダサイズのみ)、
ファイルBのデータ先頭へのオフセット値は、 164 (ヘッダサイズ + ファイルAのサイズ)、
ファイルCのデータ先頭へのオフセット値は、 196 (ヘッダサイズ + ファイルA・Bのサイズ)、
となります。

閉鎖

“C言語何でも質問掲示板” へ戻る