Zelda 64 Filesystem
See also: Zelda 64 Filesystem/Hacks.
Ocarina of Time, Majora's Mask, and all of their seperate versions & ports use an identical filesystem. The filesystem helps the game differentiate between compressed and decompressed data, and keep the files in order. It also allows for quick editing should something be misplaced. The filesystem (rather the file offset table part) begins right after the ROM creator and build date. If you would like to see for yourself, you can find the filesystem at the following offsets:
Debug ROM: OoT 1.0 (U): MM (U): MM (J): 0x00012F40 0x00007400 0x0001A4D0 0x0001C0E0
If you, for some reason, have a different version, just search for "zelda@", and with any luck, you'll find it! An interesting note: the filename table refers to the file offset table as "dmadata". The file offset table also refers to itself, which is quite extraordinary.
File Offset Table
The file offset table begins exactly 0x30 bytes (48 bytes in decimal) from the creator/build date. The first entry of the file table is always (I have yet to find an exception) the locations 0x00000000 - 0x00001060, which is referred to as "makerom" in the filename table. This is not related to this article, but that file it the header for the N64 ROM. It contains region info, cart title, country, and boot code. The next few entries should be constant as well, but it could vary, for all I know.
One entry for a file is 16 bytes (or 0x10 bytes; one line in a hex editor). You can split this up into 2 parts to find out the two offset pairs, and then you can split those two pairs in half to find the start/end offset for said pair. Here is a tiny diagram:
Virtual Mapping Physical Mapping 00000000 00000000 00000000 00000000
When working with a compressed ROM, the "virtual mapping" will contain the decompressed offset to the file. IE, if all of the ROM's resources were decompressed and stuck together, that's where it would be. The "physical mapping" in these files will then either point to an uncompressed file or a Yaz0 block (basically a file compressed with the Yaz0 algorithm). To determine if a file hasn't been compressed, you can simply check if the second offset in the "physical mapping" is 00000000. Otherwise, it is compressed. For example:
<= COMPRESSED DATA => Offset 0x0001C1C0 in the debug ROM Virtual Mapping Physical Mapping 00A3A000 00A3EB80 009BA6B0 009BBA50 00A3F000 00A42300 009BBA50 009BD150 00A43000 00A4A780 009BD150 009C0310 <= DECOMPRESSED DATA => Offset 0x0001C1F0 in MM Jap Virtual Mapping Physical Mapping 00A4B000 00A4C310 009C0310 00000000 00A4C310 00A55660 009C1620 00000000 00A55660 00A68270 009CA970 00000000
As you can see in the decompressed data example, these three files are decompressed and mapped to a certain location, with their last pointer being set to 00000000. Since the file size of the file in the ROM is identical to the mapped one, the last pointer can be set to 00000000 without losing data. In short: the game calculates the size of the block via the virtual mapping (when dealing with decompressed data). In the compressed data example, however, the physical mapping points to a Yaz0 block in the ROM, which is then decompressed and mapped to the location in the first offset pair.
Since the debug ROM is in fact a debugger's ROM, the developers were nice enough to leave all of the filenames for OoT's resources intact. They can be found in the main code block or at offset 0xBE80. The filenames are separated by 1-4 null (0x00) bytes (for four-byte alignment), and they match up perfectly with the data referenced by the file offset table. The debug ROM is the only Zelda 64 game to have a complete file listing. Majora's Mask has one too (following a similar format), but it only lists the names of the scene files, and some of the scenes it refers to are removed.