The game finds every model block in dt_na.dat through a table in main.dol: open the file by name, seek to an offset, read a number of bytes. Nothing inside dt_na.dat says where a model lives, so relocating a model (hammerspace) only means writing the block somewhere new and repointing its DOL entry. All values are big-endian. The constants below are for the US version.
A flat array of uint32 virtual addresses at DOL file offsets 0x69C828–0x69CAD8, one per directory. Convert an address to a DOL file offset by subtracting 0x80003F00. The index into this table is the chunk number (ChunkNumber in a .sluggie; also the number in front of each export folder name).
Each directory is an array of 48-byte entries (12 × uint32). The index into it is the file index (FileIndex in a .sluggie). The array ends at the first entry whose word 0 is not the dt_na.dat name pointer, or where the next directory's entries begin. Directories are stored back to back, so a walk that checks only the name pointer runs on into the following directories. It then meets the same physical entry again under other chunk numbers: for example, (76,197), (77,182) … (88,17) and (89,2) are all the entry at DOL offset 0x688A28. Each entry has three language slots of four words each, and every slot has the same layout:
| Offset | Type | Description |
|---|---|---|
| 0x0 | uint32 | File name pointer. Always 0x8067F658, the RAM address of the string "dt_na.dat" |
| 0x4 | uint32 | Length of the block in bytes |
| 0x8 | uint32 | Byte offset of the block inside dt_na.dat |
| 0xc | uint32 | Allocation size. Equal to the length in every entry the tools write |
| Slot | Entry offset |
|---|---|
English (en) |
0x00 |
Spanish (sp) |
0x10 |
French (fr) |
0x20 |
In every character directory, all three slots point at the same data. 118 entries do have diverging slots, all in non-character chunks (9, 10, 16, 119-123, 128, 138 and 145-148; surveyed in the input DOL on 2026-09-27). The tools read the en slot and write all three, which is correct for every character entry.
fileCount:u32, then fileCount u32 member offsets relative to the archive start (0 = empty slot), then the members in ascending order on 32-byte boundaries. A member's length is the distance to the next populated offset, or to the archive end for the last one. Members can be models or ANM blocks. Multi-part characters (stadium bosses, map objects) use this. Some equipment entries (bats, for example) also have a short prefix in front of the model. In a .sluggie, ModelOffset/ModelLength give the member itself, which then differs from the entry's offset/length.--untangle exports split on purpose. They always move alone; see Unused characters.BASE_SIZE (715,046,144) in the extended dt_na.dat, on a 32-byte boundary.dt_na.dat size field in fst.bin (FST entry 1, size at byte 0x14) to match the extended file.The block a patch replaces is zero-filled, but only when no entry in the output main.dol still points into it (in any language slot). Otherwise it is left in place with a warning, so that one route's patch never blanks another route's live bytes.
Unpatching restores the entry from the untouched input main.dol, restores the original bytes from the input dt_na.dat, and zero-fills the hammerspace range. Unused-character entries are the exception (below). The implementation is SluggiesTools/Hammerspace/HammerspaceHelper.py (readDolEntry, patchDolEntry, findSharedEntries, liveRoutesInto, removeModelFromHammerspace) and ArchiveContainer.py.
Zero-filled blocks are reused only when a later block fits into them, and dt_na.dat never shrinks by itself. start.py --defrag-hammerspace [--dry-run] (the GUI's Maintenance tab, Defragment) packs hammerspace again. The implementation is SluggiesTools/Hammerspace/Defrag.py.
fst.bin gets the new file size.BASE_SIZE, each keeping its alignment modulo 32, so nothing moves up. The file ends 128 bytes after the last block. Non-zero bytes that no route covers are dropped.main.dol, fst.bin and the dt_na.dat tail are backed up to 3_Output_Dat/_defrag_backup. Every route is hashed before the move and checked after it, and any failure restores the backup. A backup left by an interrupted run is restored by the next run.BASE_SIZE can shrink. Vanilla ranges zero-filled by patches stay where they are.dt_na.dat, main.dol and fst.bin to the game at the same time.First run on a real output (2026-10-13): 94 extents, 24.80 MB live of 25.40 MB, 93 moved, 629,568 bytes saved. All 8,460 routes read the same bytes as before, and only the 273 offset words of moved routes changed in main.dol.
The six unused characters have no model data of their own in the stock game. Every file entry of theirs points at a block that a playable character also uses (input DOL, 2026-09-27):
| Unused chunk | Shares with | Files |
|---|---|---|
| 89 Unused Yoshi A | 24 Yoshi, 90 | 0-4, 6-14 |
| 90 Unused Yoshi B | 24 Yoshi, 89 | 0-4, 6-14 |
| 91 Unused Toad | 50 Purple Toad | 0-4, 6-14 |
| 92 Unused Pianta | 39 Pianta | 0-4, 6-14 |
| 93 Unused Kritter | 76 Kritter | 0-14 |
| 94 Unused Koopa | 30 Koopa | 0-4, 6-14 |
File layout of a character directory: 0 high-poly model, 1 L_ model, 2 bat, 3 left glove, 4 right glove, 5 placeholder, 6-14 ANMs. File 5 of about 65 character directories, including 89-92 and 94, is one shared 64-byte block at 0x4B33140. It is an archive header with count 1 and a slot at +0x20, and is all zeros after that.
File 5 survey of the character directories (2026-10-05): only Peach (dir 22, a second bat, 94103648_bat.gpl, 10,688 bytes) and Wario (dir 28, chall_g.gpl, 26,208 bytes) hold a real block. 30 directories (76-79, 83, 93 and the Miis' 95-118) hold a 32-byte all-zero block, the others the shared 64-byte block. Peach's second bat appears during her Star Swing (user observation, Dolphin, 2026-10-05), so file 5 seems tied to that animation. Whether the load is keyed on the file's content or on a per-character setting, what Wario's block does, and whether another character uses a block placed in its file 5 are not known. Bats and gloves (files 2-4) are one-bone models inside a 32-byte archive container, and are widely shared between directories: one vanilla bat block serves up to 13 routes.
--untangle exports (export.py, clone_unused_dirs_to_hammerspace_for_untangle) copy every entry of chunks 89-94 into hammerspace and repoint only those entries. The playable owner keeps the vanilla location. The list of chunks lives in SluggiesTools/Hammerspace/UntanglePolicy.py.
.sluggie records the resulting texture names..sluggie offsets point into hammerspace. All offsets in a .sluggie are absolute. So when building, the patcher rebases them onto the vanilla block, and it keeps the archive prefix of entries such as the bat. The copy's own address therefore does not matter, even after it has been zeroed.dt_na.dat plus the .sluggie (UntangledTextures.py replays the untangler's flip sequence until each texture's Dolphin name matches the recorded TextureFileName). This reproduces all 89 intact export copies byte for byte.start.py --resplit-unused (StartTools menu [6]). A patch of such an entry needs no repair first, because it moves only that entry.Confirmed in Dolphin on Unused Yoshi A (2026-09-28/29). In each case green Yoshi and Unused Yoshi B stayed vanilla throughout:
L_;