libfat

A FAT library for the Nintendo GBA and DS

This is the successor to GBA NDS FAT. It features better reentrancy support, cleaner source code and is built as a proper library.

Installation

libfat is packaged as part of DevkitARM. Download the DevkitPro Updater and run it to start the installation. You will need to install as a minimum DevkitARM r20, libnds and libfat. I also suggest you install the GBA and NDS examples.

Using libfat in a project

The following assumes that you are using one of the example NDS templates.

The first step is adding libfat to the list of libraries. In the ARM9 Makefile (or the top level one if there is no specific ARM9 Makefile), look for the list of libraries. It should look like:

LIBS	:= -lnds9

Change this to:

LIBS	:= -lfat -lnds9

The order is important! Make sure that -lfat comes before -lnds9. The include directory will already be set, so you don't need to worry about this.

Next, you'll need to include fat.h in your main source file. Near the top of the file, insert the line:

#include <fat.h>

Before you can use any file functions, you'll need to initialise the library. You should only do this once within the execution of the program. Somewhere within the program's startup code, insert the line:

fatInitDefault();

This will initialise libfat and set the number of cached sectors to the default (8 on the DS, 2 on the GBA). It will also set libfat as the default stdio file device. If you prefer to use your own settings, use the line:

fatInit(cache_size, set_as_default);

In this case, cache_size is the number of sectors to store in the cache at any one time and if set_as_default is true it then libfat will be the default stdio file device.

Both fatInit and fatInitDefault return true if the library was successfully initialised and false otherwise.

That is all that's needed to get libfat running within your project.

Using files

Through the magic of DevkitARM and libfat, files can be openned like on any other POSIX system. Most of the functions in the stdio.h header should work. You do not need to worry about the cache. It will be taken care of, as long as you remember to close any open files before turning off the power.

Paths are separated by a forward slash -- / . Directories are specified by their path. The directory itself can be refered to as . and the parent as ... The root directory is simply / or /..

When libfat is not the default stdio device, you will need to refer to files and directories with a device specifier. This is done by using fat: before a path. You can also use a single digit in the device specifier, such as fat0: or fat1:. It is possible to use both slots at once on a DS. This is done by specifying the slot with a number. The full list of device specifiers is:

To open a file called graphics.bin for reading within a directory called app_data located on the card in Slot-2, you would use:

FILE* test = fopen ("fat2:/app_data/graphics.bin", "rb");

If libfat is set as default, and you don't mind which slot the card is in, you could use:

FILE* test = fopen ("/app_data/graphics.bin", "rb");

If you are already in app_data (after a chdir("/app_data"), for example), you could use:

FILE* test = fopen ("graphics.bin", "rb");

Using directories

Directory functions do not follow POSIX standards. Custom functions are used for various reasons.

To add directory support to a project, you'll need to include <sys/dir.h>


To open a directory, use:

DIR_ITER* dp = diropen ("/directory/path/");

diropen returns NULL and sets errno on failure.


To iterate through a directory, use:

dirnext (dp, filename, &filestat);

dp is a previously openned directory, filename will be filled with the filename of the next file or directory, and filestat will be filled with the file's stats. If filestat is NULL, it won't be filled.

To start at the begining of a directory again, use:

dirreset (dp);

To close a directory, use:

dirclose (dp);

dirnext, dirreset, and dirclose all return 0 on success. If they fail for any reason, they will return -1 and set errno. dirnext will set errno to ENOENT if there are no file or directory names left in the directory.


The directory functions mkdir and chdir work as normal.

A quick file listing demo:

#include <dir.h>
#include <unistd.h>

struct stat st;
char filename[MAXPATHLEN]; // always guaranteed to be enough to hold a filename
DIR_ITER* dir;

dir = diropen ("/"); 

if (dir == NULL) {
	iprintf ("Unable to open the directory.\n");
} else {
	while (dirnext(dir, filename, &st) == 0) {
		// st.st_mode & S_IFDIR indicates a directory
		iprintf ("%s: %s\n", (st.st_mode & S_IFDIR ? " DIR" : "FILE"), filename);
	}
}

Other Things

stat

The stat information of a file includes some useful information.

The st_ino field contains the start cluster number of the file, which can be passed to NDSMP.

The st_dev field contains a unique identifier for the inserted device. For example, for a file openned on the GBAMP CF, the st_dev has a value of 0x4643504D. This is 'MPCF' if placed in a string.