Changes

Jump to navigation Jump to search
11,583 bytes added ,  21:03, 28 April 2020
→‎Extra data: prior save struct link fix pointed to wrong struct
Line 1: Line 1: −
This page describes the format of savegames contained in NAND.
+
This page describes the format of save files contained in NAND. These files are stored as completely unencrypted, plaintext data. Save files are not cleared upon creation, resulting in possible garbage data in unused portions of the container.
   −
=== AES CMAC header ===
+
= Main header =
 +
 
 +
The header is 0x4000 bytes long.
 +
 
 +
There are 2 headers stored at 0x0 and 0x4000, presumably for commit and rollback purposes.
 +
 
 +
Decimal versions are separated as Major, Minor, Micro, and Bugfix with each using one byte. e.g. version 3.4.5.6 would be 0x03040506.
 +
 
 +
{| class="wikitable"
 +
|-
 +
! Image offset
 +
! Length
 +
! Description
 +
|-
 +
| 0x000
 +
| 0x100
 +
| AES-CMAC header
 +
|-
 +
| 0x100
 +
| 0x200
 +
| DISF header
 +
|-
 +
| 0x300
 +
| 0x44
 +
| Duplex header
 +
|-
 +
| 0x344
 +
| 0xC4
 +
| Integrity verification header
 +
|-
 +
| 0x408
 +
| 0x200
 +
| Journal header
 +
|-
 +
| 0x608
 +
| 0x48
 +
| Save header
 +
|-
 +
| 0x650
 +
| 0x40
 +
| Main data remap header
 +
|-
 +
| 0x690
 +
| 0x40
 +
| Meta data remap header
 +
|-
 +
| 0x6D0
 +
| 0x8
 +
| Unknown
 +
|-
 +
| 0x6D8
 +
| 0x200
 +
| Extra data A
 +
|-
 +
| 0x8D8
 +
| 0x200
 +
| Extra data B
 +
|-
 +
| 0xAD8
 +
| 0x3528
 +
| Additional storage
 +
|}
 +
 
 +
The additional storage at the end of the header is used to store any extra header data. This data's structure is determined by offsets stored in the main part of the header.
 +
 
 +
== AES CMAC header ==
 +
 
 +
This is internally referred to as MasterHeaderMac.
    
{| class="wikitable"
 
{| class="wikitable"
Line 11: Line 78:  
| 0x00
 
| 0x00
 
| 0x10
 
| 0x10
| AES-CMAC
+
| AES-CMAC over DISF header (size 0x200)
 
|-
 
|-
 
| 0x10
 
| 0x10
Line 18: Line 85:  
|}
 
|}
   −
==== DISF ====
+
The final CMAC key used for this is generated using GenerateAesKek with a kek source and the device key, along with and LoadAesKey and a set key seed.
 +
 
 +
== DISF ==
   −
* This is located @ 0x100 in the image, following the CMAC header.
+
This section contains information about the structure of the save file.
    
{| class="wikitable"
 
{| class="wikitable"
Line 34: Line 103:  
| 0x004
 
| 0x004
 
| 4
 
| 4
| Magic Number (0x40000)
+
| Version (Major version must be 4 or 5. Only system version 5.0.0+ can read version 5 save files)
 
|-
 
|-
 
| 0x008
 
| 0x008
 
| 32
 
| 32
| Hash
+
| Hash of start of DPFS to end of 0x4000 block (0x300-0x3FFF)
 
|-
 
|-
 
| 0x028
 
| 0x028
 
| 8
 
| 8
| Primary Partition Table Offset
+
| Main data remap mapping table offset
 
|-
 
|-
 
| 0x030
 
| 0x030
 
| 8
 
| 8
| Primary Partition Table Size
+
| Main data remap mapping table size
 
|-
 
|-
 
| 0x038
 
| 0x038
 
| 8
 
| 8
| Secondary Partition Table Offset
+
| Meta data remap mapping table offset
 
|-
 
|-
 
| 0x040
 
| 0x040
 
| 8
 
| 8
| Secondary Partition Table Size
+
| Meta data remap mapping table size
 
|-
 
|-
 
| 0x048
 
| 0x048
 
| 8
 
| 8
| Save Partition Offset
+
| Main data remap offset
 
|-
 
|-
 
| 0x050
 
| 0x050
 
| 8
 
| 8
| Save Partition Size
+
| Main data remap size
 +
|-
 +
| 0x058
 +
| 8
 +
| Duplex level 1 virtual offset A
 +
|-
 +
| 0x060
 +
| 8
 +
| Duplex level 1 virtual offset B
 +
|-
 +
| 0x068
 +
| 8
 +
| Duplex level 1 size
 +
|-
 +
| 0x070
 +
| 8
 +
| Duplex level 2 virtual offset A
 +
|-
 +
| 0x078
 +
| 8
 +
| Duplex level 2 virtual offset B
 +
|-
 +
| 0x080
 +
| 8
 +
| Duplex level 2 size
 +
|-
 +
| 0x088
 +
| 8
 +
| Journal storage virtual offset
 +
|-
 +
| 0x090
 +
| 8
 +
| Journal storage data size A
 +
|-
 +
| 0x098
 +
| 8
 +
| Journal storage data size B
 +
|-
 +
| 0x0A0
 +
| 8
 +
| Journal storage journal size
 +
|-
 +
| 0x0A8
 +
| 8
 +
| Duplex master bitmap offset A
 +
|-
 +
| 0x0B0
 +
| 8
 +
| Duplex master bitmap offset B
 +
|-
 +
| 0x0B8
 +
| 8
 +
| Duplex master bitmap size
 +
|-
 +
| 0x0C0
 +
| 8
 +
| IVFC master hash offset A
 +
|-
 +
| 0x0C8
 +
| 8
 +
| IVFC master hash offset B
 +
|-
 +
| 0x0D0
 +
| 8
 +
| IVFC master hash size
 +
|-
 +
| 0x0D8
 +
| 8
 +
| Journal block table virtual offset
 +
|-
 +
| 0x0E0
 +
| 8
 +
| Journal block table size
 +
|-
 +
| 0x0E8
 +
| 8
 +
| Virtual offset of bitmap of modified physical journal blocks
 +
|-
 +
| 0x0F0
 +
| 8
 +
| Size of bitmap of modified physical journal blocks
 +
|-
 +
| 0x0F8
 +
| 8
 +
| Virtual offset of bitmap of modified virtual journal blocks
 +
|-
 +
| 0x100
 +
| 8
 +
| Size of bitmap of modified virtual journal blocks
 +
|-
 +
| 0x108
 +
| 8
 +
| Virtual offset of bitmap of free journal blocks
 +
|-
 +
| 0x110
 +
| 8
 +
| Size of bitmap of free journal blocks
 
|-
 
|-
| 0x258
+
| 0x118
 +
| 8
 +
| IVFC level 1 virtual offset
 +
|-
 +
| 0x120
 +
| 8
 +
| IVFC level 1 size
 +
|-
 +
| 0x128
 +
| 8
 +
| IVFC level 2 virtual offset
 +
|-
 +
| 0x130
 +
| 8
 +
| IVFC level 2 size
 +
|-
 +
| 0x138
 +
| 8
 +
| IVFC level 3 virtual offset
 +
|-
 +
| 0x140
 +
| 8
 +
| IVFC level 3 size
 +
|-
 +
| 0x148
 +
| 8
 +
| File allocation table virtual offset
 +
|-
 +
| 0x150
 +
| 8
 +
| File allocation table size
 +
|-
 +
| 0x158
 +
| 1
 +
| Index of the active duplex master bitmap
 +
|-
 +
| 0x160
 +
| 8
 +
| [5.0.0+] File allocation table IVFC master hash offset A
 +
|-
 +
| 0x168
 +
| 8
 +
| [5.0.0+] File allocation table IVFC master hash offset B
 +
|-
 +
| 0x170
 +
| 8
 +
| [5.0.0+] File allocation table IVFC level 1 virtual offset
 +
|-
 +
| 0x178
 +
| 8
 +
| [5.0.0+] File allocation table IVFC level 1 size
 +
|-
 +
| 0x180
 +
| 8
 +
| [5.0.0+] File allocation table IVFC level 2 virtual offset
 +
|-
 +
| 0x188
 +
| 8
 +
| [5.0.0+] File allocation table IVFC level 2 size
 +
|-
 +
| 0x200
 
|  
 
|  
 
| End
 
| End
 
|}
 
|}
   −
==== DPFS ====
+
== Integrity verification header ==
   −
* This is located @ 0x200 in the image, following DISF.
+
* Offsets for levels 1-3 come from the metadata remap storage
* Block sizes are log2
+
* Offsets for level 4 comes from the main data remap storage
 +
* This is the same header used in NCA files
    
{| class="wikitable"
 
{| class="wikitable"
Line 82: Line 308:  
| 0x00
 
| 0x00
 
| 4
 
| 4
| Magic ("DPFS")
+
| Magic ("IVFC")
 
|-
 
|-
 
| 0x04
 
| 0x04
 
| 4
 
| 4
| Magic Number (0x10000)
+
| Version (0.2.x.x)
 
|-
 
|-
 
| 0x08
 
| 0x08
| 8
+
| 4
| Offset 0
+
| Master hash size
 +
|-
 +
| 0xC
 +
| 4
 +
| Number of levels (Unused in save files)
 
|-
 
|-
 
| 0x10
 
| 0x10
| 8
+
| 0x18*6
| Size 0
+
| Level information for up to 6 levels
 
|-
 
|-
| 0x18
+
| 0xA0
| 8
+
| 32
| Block Size 0
+
| Salt seed
 
|-
 
|-
| 0x20
+
|}
| 8
+
 
| Offset 1
+
=== Level information ===
 +
 
 +
* 0x18 bytes long
 +
* Block sizes are stored as powers of 2
 +
 
 +
{| class="wikitable"
 
|-
 
|-
| 0x28
+
! Start
| 8
+
! Length
| Size 1
+
! Description
 
|-
 
|-
| 0x30
+
| 0x00
 
| 8
 
| 8
| Block Size 1
+
| Offset
 
|-
 
|-
| 0x38
+
| 0x08
 
| 8
 
| 8
| Offset 2
+
| Size
 
|-
 
|-
| 0x40
+
| 0x10
| 8
+
| 4
| Size 2
+
| Block size power
 
|-
 
|-
| 0x48
+
| 0x14
| 8
+
| 4
| Block Size 2
+
| Reserved
 
|-
 
|-
 
|}
 
|}
   −
==== IVFC ====
+
== Journal header ==
 
  −
* Generally follows DPFS, similar to 3DS.
      
{| class="wikitable"
 
{| class="wikitable"
Line 138: Line 371:  
| 0x00
 
| 0x00
 
| 4
 
| 4
| Magic ("IVFC")
+
| Magic ("JNGL")
 
|-
 
|-
 
| 0x04
 
| 0x04
 
| 4
 
| 4
| Magic Number (0x20000)
+
| Version (Must be 0.0.x.x or 0.1.0.0)
 
|-
 
|-
 
| 0x08
 
| 0x08
 
| 8
 
| 8
| Master hash size?
+
| Total size (Incl. journal)
 
|-
 
|-
 
| 0x10
 
| 0x10
 
| 8
 
| 8
| Level 1 offset
+
| Journal size
 
|-
 
|-
 
| 0x18
 
| 0x18
 
| 8
 
| 8
| Level 1 size
+
| Block size
 
|-
 
|-
 
| 0x20
 
| 0x20
 +
| 16
 +
| Journal map header
 +
|-
 +
| 0x200
 +
|
 +
| End
 +
|-
 +
|}
 +
 +
=== Journal map header ===
 +
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! Length
 +
! Description
 +
|-
 +
| 0x00
 
| 4
 
| 4
| Level 1 block size, in log2
+
| Version (Stored as a normal 32-bit integer. Must be 0 or 1)
 
|-
 
|-
| 0x24
+
| 0x04
 +
| 4
 +
| Main data block count
 +
|-
 +
| 0x08
 
| 4
 
| 4
| Reserved
+
| Journal block count
 
|-
 
|-
| 0x28
+
| 0x0C
| 8
+
| 4
| Level 2 offset
+
| Padding
 
|-
 
|-
| 0x30
+
|}
| 8
+
 
| Level 2 size
+
== Extra data ==
 +
 
 +
{| class="wikitable"
 
|-
 
|-
| 0x38
+
! Start
| 4
+
! Length
| Level 2 block size, in log2.
+
! Description
 
|-
 
|-
| 0x3C
+
| 0x00
| 4
+
| 0x40
| Reserved
+
| [[Filesystem_services#SaveDataAttribute|SaveDataAttribute]]
 
|-
 
|-
 
| 0x40
 
| 0x40
 
| 8
 
| 8
| Level 3 offset
+
| Save owner ID
 
|-
 
|-
 
| 0x48
 
| 0x48
 
| 8
 
| 8
| Level 3 size
+
| Timestamp
 
|-
 
|-
 
| 0x50
 
| 0x50
 
| 4
 
| 4
| Level 3 block size, in log2.
+
| Flags?
 
|-
 
|-
 
| 0x54
 
| 0x54
 
| 4
 
| 4
| Reserved
+
| Unused?
 
|-
 
|-
 
| 0x58
 
| 0x58
 
| 8
 
| 8
| Level 4 offset
+
| Size of usable save data
 
|-
 
|-
 
| 0x60
 
| 0x60
 
| 8
 
| 8
| Level 4 size
+
| Journal size
 
|-
 
|-
 
| 0x68
 
| 0x68
 +
| 8
 +
| Commit ID
 +
|-
 +
| 0x200
 +
|
 +
| End
 +
|}
 +
 +
= Remap Storage =
 +
 +
Remap Storage is used to remap segments of data from virtual offsets to physical offsets. This allows extending the save file without having to relocate existing data.
 +
 +
Each Remap Storage has three components: [[#Remap storage header|a header]], a remapping table, and the main data storage.
 +
 +
A remap storage can contain a varying number of segments, each representing a chunk of contiguous virtual storage. A segment can be composed of one or more entries. Each of these entries are mapped from their virtual locations to their physical locations by entries in the remapping table. A physical offset corresponds to that offset in the main data storage.
 +
 +
When a segment is extended a new remapping entry is appended to the physical storage, allowing expansion without relocating the existing entries.
 +
 +
Each virtual offset has two parts, a segment index and an offset. The size of these sections is controlled by the remap header.
 +
 +
Example: 0x3000000000000100<br />
 +
If 4 bits were reserved for the segment index, the offset would be split like this, representing offset 0x100 of segment 3.<br />
 +
Segment index: 0x3 Offset: 0x000000000000100
 +
 +
== Remap storage header ==
 +
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! Length
 +
! Description
 +
|-
 +
| 0x00
 +
| 4
 +
| Magic ("RMAP")
 +
|-
 +
| 0x04
 +
| 4
 +
| Version (Must be 0.0.x.x or 0.1.x.x)
 +
|-
 +
| 0x08
 +
| 4
 +
| Number of remapping entries
 +
|-
 +
| 0x0C
 
| 4
 
| 4
| Level 4 block size, in log2.
+
| Number of remapping segments
 
|-
 
|-
| 0x6C
+
| 0x10
 
| 4
 
| 4
| Reserved
+
| Number of bits reserved for the segment index in virtual offsets
 +
|-
 +
| 0x40
 +
|
 +
| End
 +
|}
 +
 
 +
== Remapping Entry ==
 +
 
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! Length
 +
! Description
 +
|-
 +
| 0x00
 +
| 8
 +
| Virtual offset
 +
|-
 +
| 0x08
 +
| 8
 +
| Physical offset
 +
|-
 +
| 0x10
 +
| 8
 +
| Size
 
|-
 
|-
| 0x70
+
| 0x18
| 48
+
| 4
| Unknown, reserved?
+
| Alignment
 
|-
 
|-
| 0xA0
+
| 0x1c
| 32
+
| 4
| Hash
+
| Padding?
 
|-
 
|-
 
|}
 
|}
   −
==== JNGL ====
+
= Duplex Storage =
 +
 
 +
A Duplex Storage contains four separate elements: [[#Duplex header|a header]], a bitmap, and two identically-sized chunks of data.
 +
 
 +
As hinted by the name, a Duplex Storage contains two main chunks of data. To store X bytes, two chunks of data each with size X are required.
 +
 
 +
== Bitmap ==
 +
 
 +
This main data storage is split into blocks of the size indicated in the duplex header. The bitmap contains as many bits as the main data has blocks. If the main data is 0x40000 bytes long with a block size of 0x4000 bytes, the bitmap would contain 0x10 bits.
 +
 
 +
The bitmap controls which data chunk is active for each block. e.g. If bit 3 of the bitmap is a 0 then block 3 of data chunk 0 is active and block 3 of data chunk 1 is inactive. This means that when data from block 3 is read, the data from chunk 0 will be returned and the data from chunk 1 will be completely ignored.
 +
 
 +
== Hierarchical Duplex Storage ==
 +
 
 +
Multiple Duplex Storages can be chained together to gain various benefits. With a Hierarchical Duplex Storage, the bitmap for the main data is stored inside another Duplex Storage.
 +
 
 +
The bitmap for this second Duplex Storage is stored in a special Duplex Storage. The data of this top level contains a master bitmap that is typically 0x40 bytes long. A bit in the save file header controls which master bitmap is active.
 +
 
 +
This allows for atomic operations on the Hierarchical Duplex Storage. When writing to the storage, data will be written to the inactive blocks and inactive bitmaps. When the data is committed the bit in the save file header is flipped, changing which master bitmap is active.
   −
* Generally follows IVFC
+
== Duplex header ==
 +
 
 +
* Block sizes are stored as powers of 2
    
{| class="wikitable"
 
{| class="wikitable"
Line 234: Line 581:  
| 0x00
 
| 0x00
 
| 4
 
| 4
| Magic ("JNGL")
+
| Magic ("DPFS")
 
|-
 
|-
 
| 0x04
 
| 0x04
 
| 4
 
| 4
| Magic Number (0x10000)
+
| Version (0.1.x.x)
 
|-
 
|-
 
| 0x08
 
| 0x08
 
| 8
 
| 8
| Savedata Size
+
| Master bitmap offset
 
|-
 
|-
 
| 0x10
 
| 0x10
 
| 8
 
| 8
| Unknown, Size
+
| Master bitmap size
 
|-
 
|-
 
| 0x18
 
| 0x18
 +
| 4
 +
| Master bitmap block size power
 +
|-
 +
| 0x1C
 
| 8
 
| 8
| Savedata Blocksize?
+
| Level 1 offset
 
|-
 
|-
| 0x20
+
| 0x24
 +
| 8
 +
| Level 1 size
 +
|-
 +
| 0x2C
 
| 4
 
| 4
| Unknown
+
| Level 1 block size power
 
|-
 
|-
| 0x24
+
| 0x30
| 4
+
| 8
| Unknown
+
| Level 2 offset
 
|-
 
|-
| 0x28
+
| 0x38
 
| 8
 
| 8
| Unknown
+
| Level 2 size
 
|-
 
|-
| 0x30
+
| 0x40
| 464
+
| 4
| Padding?
+
| Level 2 block size power
 
|-
 
|-
 
|}
 
|}
   −
==== SAVE ====
+
= Save FS =
 +
 
 +
== Save FS header ==
   −
* Generally follows JNGL, structure is different from 3DS.
+
* Structure is different than 3DS.
    
{| class="wikitable"
 
{| class="wikitable"
Line 286: Line 643:  
| 0x04
 
| 0x04
 
| 4
 
| 4
| Magic Number (0x60000)
+
| Version (0.6.x.x)
 
|-
 
|-
 
| 0x08
 
| 0x08
 
| 8
 
| 8
| Unknown, number
+
| Number of blocks. Does not change if save file is resized.
 
|-
 
|-
 
| 0x10
 
| 0x10
 
| 8
 
| 8
| Unknown, block size
+
| Block Size
 
|-
 
|-
 
| 0x18
 
| 0x18
 +
| 0x30
 +
| FAT header
 +
|-
 +
|}
 +
 +
== File allocation table ==
 +
 +
The savedata FS uses an allocation table to keep track of block allocation. This FAT contains doubly-linked lists of the blocks allocated to each file. Each entry in the FAT is 8 bytes in size.
 +
 +
FAT entry 0 is reserved for the list of free blocks. Because of this, the FAT entry for block n is found at FAT index n+1. The indexes stored in FAT entries refer the index of the next/previous FAT entry in the chain, not the index of the next/previous block.
 +
 +
The FAT header is internally called AllocationTableControlArea. The FAT itself is called AllocationTableMeta. The actual save FS data is called AllocationTableData.
 +
 +
=== File allocation table header ===
 +
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! Length
 +
! Description
 +
|-
 +
| 0x00
 
| 8
 
| 8
| Unknown, block size
+
| Block size
 +
|-
 +
| 0x08
 +
| 8
 +
| FAT offset
 +
|-
 +
| 0x10
 +
| 4
 +
| FAT entry count
 +
|-
 +
| 0x14
 +
| 4
 +
| Padding
 +
|-
 +
| 0x18
 +
| 8
 +
| Data offset
 
|-
 
|-
 
| 0x20
 
| 0x20
 +
| 4
 +
| Data block count
 +
|-
 +
| 0x24
 +
| 4
 +
| Padding
 +
|-
 +
| 0x28
 +
| 4
 +
| Directory table block index
 +
|-
 +
| 0x2C
 +
| 4
 +
| File table block index
 +
|-
 +
|}
 +
 +
=== File allocation table entry ===
 +
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! Length
 +
! Description
 +
|-
 +
| 0
 +
| 4 (High bit)
 +
| Set if entry is the first entry in the list.
 +
|-
 +
| 0
 +
| 4 (Lower 31 bits)
 +
| Previous entry index. First entry in list if 0.
 +
|-
 +
| 4
 +
| 4 (High bit)
 +
| Set if the allocation segment has multiple blocks.
 +
|-
 +
| 4
 +
| 4 (Lower 31 bits)
 +
| Next entry index. Last entry in list if 0.
 +
|-
 +
|}
 +
 +
If the allocation segment has multiple blocks, the first entry will be followed by a range descriptor entry. The last entry in the segment will contain a duplicate of this entry.
 +
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! Length
 +
! Description
 +
|-
 +
| 0
 +
| 4 (High bit)
 +
| Always set.
 +
|-
 +
| 0
 +
| 4 (Lower 31 bits)
 +
| First entry in this segment.
 +
|-
 +
| 4
 +
| 4 (High bit)
 +
| Never set.
 +
|-
 +
| 4
 +
| 4 (Lower 31 bits)
 +
| Last entry in this segment.
 +
|-
 +
|}
 +
 +
== Save File Table ==
 +
 +
The save file table is similar to the RomFS file table, except the save file table uses linked lists instead of dictionaries.
 +
 +
The table contains a list of directory entries and a list of file entries. Their respective types are:<br />
 +
'''SaveFsList<SaveFileTableEntry<SaveDirectoryInfo>>'''<br />
 +
'''SaveFsList<SaveFileTableEntry<SaveFileInfo>>'''
 +
 +
=== Save File Table Entry ===
 +
 +
SaveFileTableEntry<class T>
 +
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! Length
 +
! Description
 +
|-
 +
| 0x00
 +
| 4
 +
| Next entry index. A value of 0 indicates the end of the list.
 +
|-
 +
| 0x04
 +
| sizeof(T)
 +
| Value of type T.
 +
|-
 +
|}
 +
 +
=== Save File Info ===
 +
 +
Holds the information of a single file.
 +
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! Length
 +
! Description
 +
|-
 +
| 0x00
 +
| 4
 +
| Starting block index.
 +
|-
 +
| 0x04
 
| 8
 
| 8
| Unknown
+
| File length in bytes.
 +
|-
 +
| 0x0C
 +
| 8
 +
| Reserved.
 +
|-
 +
|}
 +
 
 +
=== Save Directory Info ===
 +
 
 +
Holds the information of a single directory.
 +
 
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! Length
 +
! Description
 +
|-
 +
| 0x00
 +
| 4
 +
| First child directory index. 0 if none.
 +
|-
 +
| 0x04
 +
| 4
 +
| First child file index. 0 if none.
 +
|-
 +
| 0x08
 +
| 0xC
 +
| Reserved.
 
|-
 
|-
 
|}
 
|}
   −
==== RMAP ====
+
== Save FS List ==
 +
 
 +
SaveFsList<class T>
 +
 
 +
This is a linked list that is used internally by '''Save File Table''' as a key-value store. Integer/string pairs are used as keys. The list is represented as a single array so that it can be easily stored and read from a file. Entry indexes 0 and 1 are reserved.
 +
 
 +
Index 0 is the start of a list containing all free entries. When an item in the list is removed, the entry it was using is added to this list for future reuse.
 +
 
 +
Index 1 is the start of a list containing all currently used entries.
 +
 
 +
The first 8 bytes of the list are used as follows. Indexes 0 and 1 are included in these counts.
 +
 
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! Length
 +
! Description
 +
|-
 +
| 0x00
 +
| 4
 +
| The size of the list. Freed entries that have not been reused are included in the count.
 +
|-
 +
| 0x04
 +
| 4
 +
| The current capacity of the list based on the number of bytes allocated.
 +
|-
 +
|}
   −
* There are generally two RMAP blocks in sequence.
+
=== Save FS List Key ===
    
{| class="wikitable"
 
{| class="wikitable"
Line 318: Line 879:  
| 0x00
 
| 0x00
 
| 4
 
| 4
| Magic ("RMAP")
+
| 32-bit integer.
 
|-
 
|-
 
| 0x04
 
| 0x04
 +
| 0x40
 +
| 0x40-byte string.
 +
|-
 +
|}
 +
 +
=== Save FS List Entry ===
 +
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! Length
 +
! Description
 +
|-
 +
| 0x00
 +
| 0x44
 +
| Key.
 +
|-
 +
| 0x44
 +
| sizeof(T)
 +
| Value.
 +
|-
 +
| 0x44 + sizeof(T)
 
| 4
 
| 4
| Magic Number (0x10000)
+
| Next entry node index. A value of 0 indicates the end of the list.
 
|-
 
|-
 
|}
 
|}
77

edits

Navigation menu