GBFS

GBAにはOSがない為、当たり前に使っている恩恵を受けることができません。ファイル管理システムもその1つで、fopenやfseek関数を呼び出せばファイルの中身にアクセスするという、単純なこともできません。プログラミング初心者にありがちなのは、自分のプロジェクト内で1つ1つのデータをポインタで管理してしまうことです。これでは規模が大きくなったら破綻します。そこでお奨めなのがGBFSです。ツールはdevkitPro標準装備となっています。

原理

仕組みはとても簡単です。gbaファイルの終端にファイルを繋げて読み込ませてしまおうというものです。例としてgbaファイルが0x1000のサイズだった場合、起動したときのアドレスは0800:0000h~0800:0FFFhになります。gbaファイルの後ろにデータを追加したとき、その内容は0800:1000h以降に配置されるわけです。もちろんこのアドレスはROM領域に配置される為、リードオンリーであることは忘れないでください。

#define ADR_ROM_END  (u8*)&__rom_end__
extern u8 __rom_end__;

「__rom_end__」は「C:\devkitPro\devkitARM\arm-none-eabi\lib\gba_cart.ld」(github)に定義されています。このアドレスを利用して次の関数をいっしょに使います。

// ExtendTinyFileSystem

u8* EtfsReadu8(u32 offset)
{
	u8* p = (u8*)((u32)&__rom_end__ + offset);
	return *p;
}

u16* EtfsReadu16(u32 offset)
{
	u16* p = (u16*)((u32)&__rom_end__ + offset);
	return *p;
}

u32* EtfsReadu32(u32 offset)
{
	u32* p = (u32*)((u32)&__rom_end__ + offset);
	return *p;
}

u8* EtfsGetPointer(void)
{
	return (u8*)((u32)&__rom_end);
}

改良

GBFSは、上記の原理を拡張して複数のファイルを参照可能にし、さらに詳細なファイル情報を追加したものです。仮に以下のようなファイルがあったとします。

1.txt
2.png
3.bmp

これらをGBFSファイルにアーカイブ化するには以下のコマンドを入力します。

gbfs.exe test.gbfs *.txt *.png *.bmp

test.gbfsのファイル構造は次のとおりです。

GBFSヘッダ
ファイル情報×3
1.txtのバイナリデータ
2.pngのバイナリデータ
3.bmpのバイナリデータ
// ヘッダ(32バイト)
typedef struct {
	char sig[16];			// シグネチャ "PinEightGBFS" + 0x0d + 0x0a + 0x1a + 0x0a
	u32  size;			// アーカイブの大きさ
	u16  dirOff;			// ファイル一覧のオフセット(ROM終端からの相対)
	u16  fileCnt;			// ファイルの登録数
	u8   noUse[8];			// 空き領域
};
// ファイル情報(32*3バイト)(今回の例では3つ分存在します)
typedef struct {
	char fname[24];			// ファイル名
	u32  size;			// サイズ
	u32  dataOff;			// ファイルのオフセット(ROM終端からの相対)
};
// 1.txtのバイナリデータ
// 2.pngのバイナリデータ
// 3.bmpのバイナリデータ

さらに次のコマンドで.gbaファイルと連結します。

padbin.exe 256 x.gba
copy /b x.gba + x.gbfs x2.gba

padbinコマンドは256バイト境界に合わせて連結させる為の処理です。この処理を忘れるとアドレスが誤って奇数位置で連結してしまう可能性があります。データがうまく取り出せない場合はpadbinの処理忘れを疑ってください。バッチ処理を作っておくのもいいでしょう。

GBFSとMode3の画像表示

公式にあるソースコードはモジュール化に失敗しておりユーザ側にGBFS情報を持つ必要があります。その為、ここでは自作したものを使用しました。

ファイルのポインタを取り出す関数には2種類あり、Safe付きかどうかがあります。自分で用意する場合は100%存在するのでNULLの返却を許さず、Safeなしはユーザ側が用意し、NULLでも許容するというコードになっています。もちろんNULLで返却された場合、例外処理を作らなくてはいけません。

動作画面

1.png

履歴


トップ   一覧 検索 最終更新   ヘルプ   最終更新のRSS