ファイルの入出力
ゲームを作っていると外部のファイルの参照や出力が必要になることが多々あります。
- テクスチャー
- モデル
- 音楽
- 動画
- セーブデータ
- etc…
様々な用途で外部ファイルの参照や出力が必要になってきます。
今回はそう言った場合に使用するファイルの入出力の基本を記載していきます。
ファイルとは?
PC上に存在しているファイルは基本的にバイナリ(数値)データとして存在しています。
そのバイナリデータを駆使してテクスチャー、モデル、音楽といったデータを形作っています。
ファイルの読み込み
バイナリファイルの操作の方法はいくつかあります。
今回はCreateFileを使った方法を記載していきます。
CreateFile
CreateFileを呼び出すことでPC上のファイルを開くことができます。
又、ファイルを開くときには開き方の指定する必要があります。
HANDLE CreateFile( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile );
- lpFileNameはファイルのパスを指定します。
- dwDesiredAccessはアクセスタイプを指定します。
- dwShareModeはファイルの共有方法(他のファイルが開いている場合の挙動)を指定します。
- lpSecurityAttributesはNULLで構いません。
- dwCreationDispositionはファイルの有無による生成方法を指定できます。
- dwFlagsAndAttributesはファイルの属性やフラグを指定します。
- hTemplateFileはNULLで構いません。
この関数は成功するとファイルハンドルを返します。
失敗した場合には INVALID_HANDLE_VALUE が返ってきます。
アクセスタイプ
アクセスタイプは主に読み込みと書き込みの2種類です。
読み込み時は GENERIC_READ を指定します。
書き込み時は GENERIC_WRITE を指定します。
※bitフラグなので複数を同時に指定することもできます。
共有モード
共有モードは他のアプリケーションなどで使用されている場合の挙動の設定になります。
- FILE_SHARE_READ
読み込みでアクセスされている場合でオープンを許可する - FILE_SHARE_WRITE
書き込みでアクセスされている場合でオープンを許可する - FILE_SHARE_DELETE
削除でアクセスされている場合でオープンを許可する
※bitフラグなので複数を同時に指定することもできます。
生成方法
生成方法の指定によってファイルの有無による開いた時の挙動を指定できます。
- CREATE_NEW
ファイルが存在しない場合は生成する。
既に存在する場合は失敗する。 - CREATE_ALWAYS
ファイルを存在しない場合は生成する。
既に存在する場合は上書きする。 - OPEN_EXISTING
ファイルが存在する場合は開く。
存在しない場合は失敗する。 - OPEN_ALWAYS
ファイルが存在する場合は開く。
存在しない場合は作成する。
ファイルの属性
- FILE_ATTRIBUTE_NORMAL
特に属性はありません。単独で指定します。 - FILE_ATTRIBUTE_HIDDEN
隠しファイル - FILE_ATTRIBUTE_READONLY
読み取り専用 - FILE_ATTRIBUTE_SYSTEM
システムファイル - FILE_ATTRIBUTE_TEMPORARY
テンポラリファイル - FILE_FLAG_DELETE_ON_CLOSE
ファイルを閉じると削除されます。
他にもありますが、概ねこれぐらいで事足りるかと思います。
ファイルの読み込み
それではこのページではファイル読み込みの基本的な流れを記載していきます。
ファイルを開く
data.datというファイルを開く場合は下記のようになります。
HANDLE hFile = CreateFile("data.dat", GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
これでdata.datというファイルを読み込みモードで開いています。
もしファイルが存在しない場合は hFile に INVALID_HANDLE_VALUE が返ってきます。
ファイルを読み込む
次に開いたファイルからデータを読み込みます。
DWORD dwSize; int data; ReadFile(hFile, &data, sizeof(data), &dwSize, NULL);
これで hFile で制御されたファイルから int サイズのデータを data に対して読み込むことができます。
dwSize には実際に読み込めたサイズが返ってきます。
基本的には第三引数で指定したサイズがそのまま返ります。
第五引数は基本的にNULLで構いません。
※ファイルを読み込んだ後は読み込んだサイズ分だけファイルポインタ(読み込み位置)が進みます。
戻り値は成功か失敗かのBOOL値が返ってきます。
ファイルポインタの移動
ファイル操作時に読み込み位置を変えたい場合があります。
SetFilePointer(hFile, 32, NULL, FILE_BEGIN);
そんな時はSetFilePointerを使うと指定した位置に移動できます。
第二引数で移動する位置
※第三引数は移動する位置が32bitでおさまらない場合に上位の32bitとして扱えます。
第四引数でどこから移動するかを指定できます。
今回は先頭(FILE_BEGIN)から先に32byteを指定しています。
- FILE_BEGIN
先頭からの位置 - FILE_CURRENT
現在位置からの位置 - FILE_END
末尾からの位置
戻り値で移動後の位置がバイト単位で帰ってきます。
これを応用して下記のように記載すると現在の位置が取得できます。
DWORD dwCurrent = SetFilePointer(hFile, 0, NULL, FILE_CURRENT);
dwCurrent が現在のファイルポインタの位置になります。
ファイルを閉じる
ファイルを操作した後は閉じる必要があります。
CloseHandle(hFile);
ファイルを閉じるのは簡単でこれだけです。
ファイルの書き込み
この最後にファイルの書き込みについて記載していきます。
ファイルを開く
data.datというファイルを書き込みで開く場合は下記のようになります。
HANDLE hFile = CreateFile("data.dat", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
これでdata.datというファイルを書き込みモードで開いています。
今回の設定ではファイルの有無に関わらずファイルは新しく生成されます。
ファイルを書き込む
次に開いたファイルにデータを書き込みます。
DWORD dwSize; int data = 10; WriteFile(hFile, &data, sizeof(data), &dwSize, NULL);
これで hFile で制御されたファイルに data の内容を書き込むことができます。
dwSize には実際に書き込んだサイズが返ってきます。
基本的には第三引数で指定したサイズがそのまま返ります。
第五引数は基本的にNULLで構いません。
※ファイルを書き込んだ後は書き込んだサイズ分だけファイルポインタ(書き込み位置)が進みます。
戻り値は成功か失敗かのBOOL値が返ってきます。
他は操作は基本的に読み込みと同じになります。
ファイル操作はゲームを作っていると必ず必要になる処理なので活用してより良いゲームを作っていきましょう!
おまけ
ファイルサイズの取得
ファイルを開いた時にファイル全体のサイズを知りたい場合があります。
そういった時は下記の関数で取得することができます。
DWORD dwSize = GetFileSize(hFile, NULL);
これでファイルのサイズが取得できます。
必要に応じて使ってみてください!