User Tools

Site Tools


using_the_fnt_and_fat_to_locate_files

Many Nintendo DS games use the NitroROM filesystem as a way of storing and retrieving files. This is an explanation of how to locate a file in an NDS ROM using two parts of the filesystem, the FNT and the FAT. It also includes a worked example using Metroid Prime Hunters.

FNT

FNT stands for File Name Table. The FNT consists of a list of directories, and a list of entries, which themselves contain a list of sub-entries. All values stored are little-endian.

Finding the FNT

The location of the FNT can be found as four bytes in the header at location 0x40. The size of the FNT in bytes can be found as four bytes in the header at location 0x44.

Reading the FNT

As stated before, the FNT starts with a list of directories, then a list of entries containing the files or folders for each directory. The list of directories is always found first.

Directory and Entries Count

The number of directories is given as a 2-byte value found at FntOffset + 6. This is also part of the data for the first directory. The count is the same for the number of lists of entries

Directory Structure

The list of directories begins immediately at the beginning of the FNT. The structure of the directory is as follows:

  • Offset - 4 bytes - An offset from the beginning of the FNT for the first entry in this directory
  • FirstID - 2 bytes - The ID of the first file in the sub-table pointed to by the Offset. A directory entry will only ever point at the first file.
  • ParentID - 2 bytes - If this is the first directory in the table, then this value is the number of directories in the list. Else, it's the ID of the parent directory that this directory is in. This is how sub-folders work. Directory IDs exist in the range 0xF000-0xFFFF, where directory F000 is the root directory. This is only relevant for directories, not actual files.

Entry Structure

The entries are stored as lists. There are the same number of lists as there are directories - if there are 55 directories, you have 55 lists of entries. There is no count given for the number of entries in a list; instead, a special entry serves as a list terminator. These lists begin as soon as the directory list ends, and its structure is as follows:

  • Attributes - 1 byte - This attribute byte has three functions. The MSB indicates whether or not this entry is for a file or a directory. The MSB is 1 to indicate directory, else it's a file. The lower 7 bits is the length of the filename, meaning a filename can be at most 127 characters. Finally, if the Attributes byte is 0, this is a Null Entry, which indicates the end of this list of entries.
  • Filename (optional) - Variable number of bytes - The length of the filename is given from the lower 7 bits of the Attributes byte. If the Attributes byte was 0, then there is no filename.
  • DirectoryID (optional) - 2 bytes - If the MSB of the Attributes byte is 1, then these two bytes give the ID of this directory. Directories exist in the range of 0xF001-0xFFFF. If the MSB of the Attributes byte is 0, these two bytes are omitted.

FAT

FAT stands for File Allocation Table. The FAT stores sets of pointers into the ROM, indicating the start and end positions for files.

Finding the FAT

The location of the FAT can be found as four bytes in the header at location 0x48. The size of the FAT in bytes can be found as four bytes in the header at location 0x4C. The number of entries in the FAT is given by FATSize / 8.

Reading the FAT

Compared to the FNT, the FAT is very simple. Each entry in the FAT consists of two 4-byte absolute pointers to the ROM. The first pointer is the start position of the data, the second pointer is the end position of the data. The length of the data is simply End - Start.

Finding a file using the FNT and FAT

I'm going to be using Metroid Prime Hunters as an example. If you want to follow along, you'll likely need a USA Revision 1 version of the ROM (CRC32: 303E6FC4), and to open that in a hex editor. I personally use the 010 Editor, as it has a templating function. When you load a .nds file, it'll prompt to download an NDS template, which will automatically identify the various parts of the ROM, including the FNT, FAT, and all of the entries within. The downside is it's not free, and is on a trial. It may also be useful to grab a tool called Nitro Explorer, as this will let you view the file structure yourself.

Some useful information to know:

  • Our FNT starts at 0x163A00
  • Our FAT starts at 0x174000
  • We have 55 directories/sub-directories
  • The list of entries into those directories starts at 0x163BB8

The file we want to locate is called “bigeyeroom.arc”. A quick text search for this string locates the filename in the FNT at offset 0x163DFE. This is the fourth entry in the third list of entries - or in other words, the fourth file in the third directory. You can verify this by starting at the beginning of the list of entries, and stepping through all of the entries. You'll encounter 3 Null entries indicating the end of 3 lists of entries, and then 3 filenames before you reach “bigeyeroom.arc”.

If you're looking at this ROM in Nitro Explorer, you may notice that “bigeyeroom.arc” exists in the second directory called “archives”, despite being in the third list of entries. This is because the first list of entries is the root folder for the filesystem, with subsequent lists being the subfolders. Since we're looking at the raw hex, it's more straightforward to consider the root folder as the first directory.

Knowing that we're in the third directory, we can go to our list of directories and have a look at the third directory structure.

From this, we can see several things. First is the offset: 0x3DD. Taking our FNT offset of 0x163A00 and adding 0x3DD to it gives us 0x163DDD, which is the location of the first file entry in the entries table. We've just gone backwards from that. Secondly, we can see a parentID of 0xF000. Since this is a file, not a folder, we can ignore this. The actual parent folder ID is 0xF000 + 2; the 2 is because it's the third list of entries, but we start counting at 0. And finally, the firstID has a value of 0x13. This is not the ID of the file we're looking for, this is the ID of the first file in the directory. It's also an offset in the FAT table for that first file.

So how do we get the offset for our file? The directory structure gives us the ID of the first file in the directory. Subsequent files are found by simply incrementing that ID. We know that the file we're looking for was the fourth file in our list of entries, so we just add 3 to our first ID to get a value of 0x16, or 22 in decimal.

Then we jump to the FAT, specifically the 23rd entry in the FAT - IDs start at 0, not 1, so we need to add 1 to our ID. You can figure out this location by taking the FAT offset and adding (8 * 22) to it. 8 * 22 = 176, or 0xB0, giving us a final address in the FAT of 0x1740B0. There, we find two 4-byte values:

The data we're looking for starts at offset 0x2EFF600, and ends at 0x2F195D0, running for a length of 106,448 bytes.

From this image, we can see that we've found the file we're looking for. The .arc is an archive format, so the data needs to be decompressed in order to work with it.

using_the_fnt_and_fat_to_locate_files.txt · Last modified: 2024/08/29 19:54 by zero_one