GBFS is an archive format created by Damian Yerrick in April 2002.
In the early days of the GBA homebrew scene, there was no efficient way to include a binary file as a read-only data array in a GBA program.
The alternative at the time was to translate each binary file to a C source code file containing a large
This technique was inefficient in GCC, the compiler used by both commercial and homebrew GBA game developers, especially around the turn of the millennium when new PCs were still shipped with 64 MB of RAM.
The DJGPP FAQ explains:
Some innocent-looking programs are known to cause GCC to gobble preposterous amounts of memory, which could cause it to crash or abort after printing "Virtual memory exhausted". One particular case of such programs is when you initialize very large arrays. For example, to compile a source which initializes a char array of 300,000 elements requires more than 60MB(!) of memory. You should avoid such constructs in your programs.
The design of GBFS was inspired by that of datafiles in the Allegro library versions 3 and 4. GBFS is designed to be appended to a GBA executable in the same way that an Allegro datafile was appended to a PC game executable. This avoids pathological compiler behavior, plus it lets artists work on assets without having to request a recompile. The UNIX tape archive (tar) format was rejected in favor of a custom format for two reasons: tar had far too much directory overhead (500 bytes per file), and linear search through a large archive was inefficient.
All values in the GBFS header and directory are little-endian because the GBA CPU is a little-endian ARM7. (It has been suggested, but not yet implemented, to make a big-endian variant of the format called "MDFS" for targeting Sega Genesis, Nintendo GameCube, and Wii.)
Each GBFS file begins with a header at least 24 bytes.
- 0x00-0x0F: The ASCII codes for "PinEightGBFS", followed by control characters 0x0D, 0x0A, 0x1A, and 0x0A. These control characters are inspired by PNG and serve to make
type somefile.gbfsin the DOS prompt do something sane.
- 0x10-0x13 (
uint32_t): Total length of archive, including header, directory, and file data. Used to find the next archive if more than one are appended.
- 0x14-0x15 (
uint16_t): Offset from start of file to start of directory, or length of header. Was intended for determining format version.
- 0x16-0x17 (
uint16_t): Number of files in archive.
Each entry in the directory is 32 bytes, and files are sorted with names in memcmp() order.
- 0x00-0x17: Filename in ASCII, padded with NUL values
- 0x18-0x1B (
uint32_t): Length of file in bytes
- 0x1C-0x1F (
uint32_t): Offset in bytes from start of GBFS file to start of file data
GBFS comes with a library that can locate an appended archive, search directory for a filename, and enumerate files in an archive. Locating an appended archive requires the executable to have been padded to a multiple of 256 bytes before appending archives. This way, the library can efficiently perform a linear search through the 32 MiB of GBA ROM address space to find the header. The archiver tool pads individual files to start on 16-byte boundaries so that memory copy can work efficiently.
In 2004, it became apparent that source-level debugging was incompatible with use of appended archives.
Also by this time, developers had become more comfortable with assembly language files.
So alternate methods of inserting archives into an executable were proposed.
One involved converting an archive to an array in an assembly language file (the
bin2s method), which was not quite as inefficient as doing so in C.
The other involved generating a large zero-filled assembly language file and later overwriting that space with archives.
The GBFS tools ended up included with devkitARM.