Doc.9 擬似ファイルシステムGBFS

GBAには元々fopenや、fseekなどのファイル関数がありません。
その為、1つ1つをポインタでやりとりしてて管理が面倒とか起こりえます。

そんな時にお奨めなのがGBFSです。
公式サイトはhttp://www.pineight.com/gba/#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__」は「gba_cart.ld」に定義されています。
このアドレスを利用して、次のような関数といっしょに使うのがいいと思います。

// ExtendTinyFileSystem

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

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

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

u8* EtfsGetPointer(void)
{
	return ADR_ROM_END;
}

マルチブート用には「gba_mb.ld」内に定義されています。
コンパイル時の指定には注意してください。

・gba_mb.ld(259行目)

	__rom_end__ = __ewram_overlay_end;

・gba_cart.ld(271行目)

	__rom_end__ = __pad_lma + SIZEOF(.pad);

改良

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

1.txt
2.png
3.bmp

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

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

test.gbfsのファイル構造は次のとおりとなっています。

  • ヘッダ
  • 登録したファイルリスト
  • 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];			// 空き領域
} __PACKED ST_GBFS_HEADER;
// 登録したファイルリスト(今回の例では3つ分存在します)
typedef struct {
	char fname[24];			// ファイル名
	u32  size;			// サイズ
	u32  dataOff;			// ファイルのオフセット(ROM終端からの相対)
} __PACKED ST_GBFS_LIST;
// バイナリデータ(1.txt、2.png、3.bmp)

これを次のようなコマンドで.gbaファイルと連結します。

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

padbinコマンドは256バイト境界に合わせて追加させる為の処理です。
これはアドレスが誤って奇数位置で連結しないようにする為の処置となっています。
忘れるとデータがうまく取り出せなくなるので注意してください。

GBFSを使ってMode3の画像を表示する例

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

  • gbfs.h
    #ifndef GBFS_H
    #define GBFS_H
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #include "gba.h"
    
    
    //---------------------------------------------------------------------------
    
    
    //---------------------------------------------------------------------------
    // ヘッダ(32バイト)
    typedef struct {
    	char sig[16];				// シグネチャ "PinEightGBFS" + 0x0d + 0x0a + 0x1a + 0x0a
    	u32  size;				// アーカイブの大きさ
    	u16  dirOff;				// ファイル一覧のオフセット(ROM終端からの相対)
    	u16  fileCnt;				// ファイルの登録数
    	u8   noUse[8];				// 空き領域
    } __PACKED ST_GBFS_HEADER;
    
    
    // ファイルリスト(32バイト)
    typedef struct {
    	char fname[24];				// ファイル名
    	u32  size;				// サイズ
    	u32  dataOff;				// ファイルのオフセット(ROM終端からの相対)
    } __PACKED ST_GBFS_LIST;
    
    
    typedef struct {
    	ST_GBFS_HEADER* pHeader;
    	ST_GBFS_LIST*   pList;
    	u32             pos;
    } ST_GBFS;
    
    //---------------------------------------------------------------------------
    EWRAM_CODE void  GbfsInit(void);
    
    IWRAM_CODE void* GbfsGetPointer(char* fname);
    IWRAM_CODE void* GbfsGetPointer2(u32 cnt);
    IWRAM_CODE void* GbfsGetSafePointer(char* fname);
    IWRAM_CODE void* GbfsGetSafePointer2(u32 cnt);
    
    EWRAM_CODE char* GbfsGetFileName(void);
    EWRAM_CODE u32   GbfsGetFileSize(void);
    EWRAM_CODE u32   GbfsGetArcSize(void);
    EWRAM_CODE u32   GbfsGetArcCnt(void);
    
    
    #ifdef __cplusplus
    }
    #endif
    #endif

ファイルのポインタを取り出す関数には2種類あり、Safe付かどうかがあります。
自分で自前に用意する場合は100%存在するのでNULLの返却を許さず、
Safeなしはユーザが用意し、NULLでも許容するというコードになっています。

  • gbfs.c
    #include "gbfs.h"
    
    
    //---------------------------------------------------------------------------
    // gba_cart.ld
    extern u8 __rom_end__;
    
    
    //---------------------------------------------------------------------------
    ST_GBFS Gbfs;
    
    
    //---------------------------------------------------------------------------
    EWRAM_CODE void GbfsInit(void)
    {
    	_Memset(&Gbfs, 0x00, sizeof(ST_GBFS));
    
    
    	char* pHeader = (char*)(((u32)&__rom_end__ + 0xff) & 0xffffff00);
    
    	if(_Strncmp(pHeader, "PinEightGBFS", sizeof("PinEightGBFS")-1) == 0)
    	{
    		Gbfs.pHeader = (ST_GBFS_HEADER*)pHeader;
    		Gbfs.pList   = (ST_GBFS_LIST*)(pHeader + Gbfs.pHeader->dirOff);
    	}
    	else
    	{
    		SystemError("[Err][GbfsInit] .gbfs File Not Found(pHeader = %x)", pHeader);
    	}
    }
    //---------------------------------------------------------------------------
    IWRAM_CODE void* GbfsGetPointer(char* fname)
    {
    	s32 left  = 0;
    	s32 right = Gbfs.pHeader->fileCnt;
    	s32 mid;
    	s32 ret;
    
    	while(left <= right)
    	{
    		mid = (left + right) / 2;
    		ret = _Strncmp(fname, Gbfs.pList[mid].fname, 24);
    
    		if(ret == 0)
    		{
    			Gbfs.pos = mid;
    			return (u8*)Gbfs.pHeader + Gbfs.pList[mid].dataOff;
    		}
    
    		if(ret > 0)
    		{
    			left  = mid + 1;
    		}
    		else
    		{
    			right = mid - 1;
    		}
    	}
    
    	return NULL;
    }
    //---------------------------------------------------------------------------
    IWRAM_CODE void* GbfsGetPointer2(u32 cnt)
    {
    	if(cnt >= Gbfs.pHeader->fileCnt)
    	{
    		return NULL;
    	}
    
    	Gbfs.pos = cnt;
    	return (u8*)Gbfs.pHeader + Gbfs.pList[cnt].dataOff;
    }
    //---------------------------------------------------------------------------
    IWRAM_CODE void* GbfsGetSafePointer(char* fname)
    {
    	void* p = GbfsGetPointer(fname);
    
    	if(p == NULL)
    	{
    		SystemError("[Err][GBFSGetSafePointer] .gbfs File Not Found: %s\n", fname);
    	}
    
    	return p;
    }
    //---------------------------------------------------------------------------
    IWRAM_CODE void* GbfsGetSafePointer2(u32 cnt)
    {
    	void* p = GbfsGetPointer2(cnt);
    
    	if(p == NULL)
    	{
    		SystemError("[Err][GBFSGetSafePointer2] .gbfs File Not Found: No.%d\n", cnt);
    	}
    
    	return p;
    }
    //---------------------------------------------------------------------------
    EWRAM_CODE char* GbfsGetFileName(void)
    {
    	return Gbfs.pList[Gbfs.pos].fname;
    }
    //---------------------------------------------------------------------------
    EWRAM_CODE u32 GbfsGetFileSize(void)
    {
    	return Gbfs.pList[Gbfs.pos].size;
    }
    //---------------------------------------------------------------------------
    EWRAM_CODE u32 GbfsGetArcSize(void)
    {
    	return Gbfs.pHeader->size;
    }
    //---------------------------------------------------------------------------
    EWRAM_CODE u32 GbfsGetArcCnt(void)
    {
    	return Gbfs.pHeader->fileCnt;
    }
  • main.c
    #include "lib/gba.h"
    #include "res.h"
    
    //---------------------------------------------------------------------------
    void WaitForVsync(void)
    {
    	while (*(volatile u16*)0x4000006 >= 160) {};
    	while (*(volatile u16*)0x4000006 <  160) {};
    }
    //---------------------------------------------------------------------------
    int main(void)
    {
    	SetMode(MODE_3 | BG2_ENABLE);
    
    	GbfsInit();
    	u16* pImg = GbfsGetSafePointer("image.img");
    
    	LZ77UnCompVram(pImg, (void*)VRAM);
    
    	for(;;)
    	{
    		WaitForVsync();
    	}
    }

動作画面

clip_1.png

履歴

  • 2014/11/14
  • 2007/09/25

添付ファイル: fileclip_1.png 215件 [詳細]

Last-modified: 2018-01-24 (水) 19:20:08 (1366d)