パックデータの利用

ツール「pack」を使用すると、各種リソースファイルを1つにまとめて、パックデータファイルを作成することができます。ツール「pack」の使用方法および、パックデータの利用方法を説明していきます。

ツール「pack」によるパックデータの作成

まず、まとめるリソースファイルの一覧を記述したテキストファイルを用意します。記述方法は、ファイルを改行で連ねるだけです。例えば、次のようになります。

image\a.png
image\b.png
image\c.png
stage1.txt
stage2.txt

なお、改行のみの行を含めることもできます。


ツール「pack」使用法:
pack <in_list_file> <out_file> <out_header_file> <prefix>

in_list_file
まとめるリソースファイルの一覧を記述したテキストファイルを指定します。

out_file
出力するパックデータファイル名を指定します。

out_header_file
出力するヘッダファイル名を指定します。

prefix
ヘッダ内の定義名の接頭辞を指定します。例えば、テキストファイルに
image\a.png
と記述されていて、このパラメータに "DATA" と指定した場合、ヘッダ定義では
DATA_IMAGE_A
となります。このパラメータの文字列の後ろに"_"が付加され、フォルダ名とファイル名が大文字に変換され、"\"は"_"に変換されます。

パックデータファイルからリソースデータを取り出す

メモリに空きがある場合、パックデータをメモリに取り込んでしまうことで、処理の高速化を図ってみます。


_Main クラスを継承したクラスにメンバ変数を追加します。

unsigned char* pak_data;


start() 関数内で NULL を設定しておきます。

pak_data = NULL;


メモリ取り込みに時間がかかることを考え、paint() 関数内で"ロード中"等の画面表示をしてからメモリ取り込みを行うほうがユーザーフレンドリーです。実際に取り込む処理のプログラムは次のようになります。

_String* file = new _String( "" );
file->set( g_appDir );
file->add( "\\data.pak" );
HANDLE hFile;
hFile = ::CreateFile(
    (LPCTSTR)file->str(),
    GENERIC_READ,
    FILE_SHARE_READ,
    NULL,
    OPEN_EXISTING,
    NULL,
    NULL
    );
if( hFile != INVALID_HANDLE_VALUE )
{
    DWORD tmp;
    DWORD size = ::GetFileSize( hFile, NULL );
    if( (pak_data = (unsigned char*)GlobalAlloc( GMEM_FIXED, size )) != NULL )
    {
        ::ReadFile( hFile, pak_data, size, &tmp, NULL );
    }
    ::CloseHandle( hFile );
}
delete file;

この例では、パックデータファイルは、アプリケーションディレクトリ直下の「data.pak」となっています。


destroy() 関数内で、メモリ解放を行います。メモリ不足等で取り込み出来ていなかったことも考慮に入れます。

if( pak_data )
{
    GlobalFree( pak_data );
}


パックデータの先頭部分には、実際のリソースデータへのオフセット値(バイト数)が unsigned long 型の数値で格納されています。1つのリソースデータのサイズは、次のリソースデータへのオフセット値との差分で取得できます。パックデータからリソースデータを取り出す関数は次のようになります。なお、メモリ不足等で取り込み出来ていなかったことも考慮に入れています。

_Memory* MyMain::read_data( int id )
{
    _Memory* data = NULL;
    if( pak_data )
    {
        unsigned long top;
        top = ((unsigned long*)pak_data)[id];
        data = new _Memory();
        if( data->alloc( ((unsigned long*)pak_data)[id + 1] - top ) )
        {
            memcpy( data->ptr(), &pak_data[top], data->size() );
        }
        else
        {
            delete data;
            data = NULL;
        }
    }
    else
    {
        // メモリ取り込み出来ていなかった場合、ファイルから直接取り出す
        _String* file = new _String( "" );
        file->set( g_appDir );
        file->add( "\\data.pak" );
        FILE* fp;
        unsigned long top;
        unsigned long size;
        if( (fp = fopen( file->str(), "rb" )) != NULL )
        {
            fseek( fp, id * sizeof(unsigned long), SEEK_SET );
            fread( &top, sizeof(unsigned long), 1, fp );
            fread( &size, sizeof(unsigned long), 1, fp );
            size -= top;
            data = new _Memory();
            if( data->alloc( size ) )
            {
                fseek( fp, top, SEEK_SET );
                fread( data->ptr(), data->size(), 1, fp );
            }
            else
            {
                delete data;
                data = NULL;
            }
            fclose( fp );
        }
        delete file;
    }
    return data;
}

これで、パラメータの id にリソースデータごとのヘッダ内の定義名を指定することで、リソースデータを取り出すことができます。


リソースデータ取り出し処理は、例えば次のようになります。

_Memory* data = read_data( DATA_IMAGE_A );

また、取り出したリソースデータは、不要になったら delete 演算子で破棄する必要があります。

delete data;