Changes

Jump to navigation Jump to search
2,942 bytes added ,  17:42, 20 November 2019
→‎Extra data: fix save struct link
Line 1: Line 1:  
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.
 
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.
   −
== Main header ==
+
= Main header =
    
The header is 0x4000 bytes long.
 
The header is 0x4000 bytes long.
    
There are 2 headers stored at 0x0 and 0x4000, presumably for commit and rollback purposes.
 
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"
 
{| class="wikitable"
Line 64: Line 66:  
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.
 
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 ===
+
== AES CMAC header ==
 +
 
 +
This is internally referred to as MasterHeaderMac.
 +
 
 
{| class="wikitable"
 
{| class="wikitable"
 
|-
 
|-
Line 82: Line 87:  
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.
 
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 ===
+
== DISF ==
    
This section contains information about the structure of the save file.
 
This section contains information about the structure of the save file.
Line 98: Line 103:  
| 0x004
 
| 0x004
 
| 4
 
| 4
| Version? (Upper 2 bytes must be 0x0004)
+
| Version (Major version must be 4 or 5. Only system version 5.0.0+ can read version 5 save files)
 
|-
 
|-
 
| 0x008
 
| 0x008
Line 262: Line 267:  
| 0x160
 
| 0x160
 
| 8
 
| 8
| [5.0.0+] Unknown header offset A
+
| [5.0.0+] File allocation table IVFC master hash offset A
 
|-
 
|-
 
| 0x168
 
| 0x168
 
| 8
 
| 8
| [5.0.0+] Unknown header offset B
+
| [5.0.0+] File allocation table IVFC master hash offset B
 
|-
 
|-
 
| 0x170
 
| 0x170
 
| 8
 
| 8
| [5.0.0+] Unknown offset into duplex storage
+
| [5.0.0+] File allocation table IVFC level 1 virtual offset
 
|-
 
|-
 
| 0x178
 
| 0x178
 
| 8
 
| 8
| [5.0.0+] Size for the above data
+
| [5.0.0+] File allocation table IVFC level 1 size
 
|-
 
|-
 
| 0x180
 
| 0x180
 
| 8
 
| 8
| [5.0.0+] Unknown offset into duplex storage
+
| [5.0.0+] File allocation table IVFC level 2 virtual offset
 
|-
 
|-
 
| 0x188
 
| 0x188
 
| 8
 
| 8
| [5.0.0+] Size for the above data
+
| [5.0.0+] File allocation table IVFC level 2 size
 
|-
 
|-
 
| 0x200
 
| 0x200
Line 289: Line 294:  
|}
 
|}
   −
=== Duplex header ===
+
== Integrity verification header ==
 
  −
* Block sizes are stored as powers of 2
  −
 
  −
{| class="wikitable"
  −
|-
  −
! Start
  −
! Length
  −
! Description
  −
|-
  −
| 0x00
  −
| 4
  −
| Magic ("DPFS")
  −
|-
  −
| 0x04
  −
| 4
  −
| Version? (Upper 2 bytes must be 0x0001)
  −
|-
  −
| 0x08
  −
| 8
  −
| Master bitmap offset
  −
|-
  −
| 0x10
  −
| 8
  −
| Master bitmap size
  −
|-
  −
| 0x18
  −
| 4
  −
| Master bitmap block size power
  −
|-
  −
| 0x1C
  −
| 8
  −
| Level 1 offset
  −
|-
  −
| 0x24
  −
| 8
  −
| Level 1 size
  −
|-
  −
| 0x2C
  −
| 4
  −
| Level 1 block size power
  −
|-
  −
| 0x30
  −
| 8
  −
| Level 2 offset
  −
|-
  −
| 0x38
  −
| 8
  −
| Level 2 size
  −
|-
  −
| 0x40
  −
| 4
  −
| Level 2 block size power
  −
|-
  −
|}
  −
 
  −
=== Integrity verification header ===
      
* Offsets for levels 1-3 come from the metadata remap storage
 
* Offsets for levels 1-3 come from the metadata remap storage
Line 363: Line 312:  
| 0x04
 
| 0x04
 
| 4
 
| 4
| Version? (Upper 2 bytes must be 0x0002)
+
| Version (0.2.x.x)
 
|-
 
|-
 
| 0x08
 
| 0x08
Line 383: Line 332:  
|}
 
|}
   −
==== Level information ====
+
=== Level information ===
    
* 0x18 bytes long
 
* 0x18 bytes long
Line 412: Line 361:  
|}
 
|}
   −
=== Journal header ===
+
== Journal header ==
    
{| class="wikitable"
 
{| class="wikitable"
Line 426: Line 375:  
| 0x04
 
| 0x04
 
| 4
 
| 4
| Version? (Must be 0x10000 or less)
+
| Version (Must be 0.0.x.x or 0.1.0.0)
 
|-
 
|-
 
| 0x08
 
| 0x08
Line 439: Line 388:  
| 8
 
| 8
 
| Block size
 
| Block size
|-
  −
|
  −
|
  −
| The below fields are treated as a separate subheader
   
|-
 
|-
 
| 0x20
 
| 0x20
| 4
+
| 16
| Version? (Must be 0 or 1)
+
| Journal map header
|-
  −
| 0x24
  −
| 4
  −
| Main data block count
  −
|-
  −
| 0x28
  −
| 8
  −
| Journal block count
   
|-
 
|-
 
| 0x200
 
| 0x200
Line 462: Line 399:  
|}
 
|}
   −
=== Save FS header ===
+
=== Journal map header ===
 
  −
* Structure is different than 3DS.
      
{| class="wikitable"
 
{| class="wikitable"
Line 474: Line 409:  
| 0x00
 
| 0x00
 
| 4
 
| 4
| Magic ("SAVE")
+
| Version (Stored as a normal 32-bit integer. Must be 0 or 1)
 
|-
 
|-
 
| 0x04
 
| 0x04
 
| 4
 
| 4
| Version? (Upper 2 bytes must be 0x0006)
+
| Main data block count
 
|-
 
|-
 
| 0x08
 
| 0x08
| 8
  −
| Number of blocks. Does not change if save file is resized.
  −
|-
  −
| 0x10
  −
| 8
  −
| Block Size
  −
|-
  −
|
  −
|
  −
| The below fields are treated as a separate subheader
  −
|-
  −
| 0x18
  −
| 8
  −
| Block size
  −
|-
  −
| 0x20
  −
| 8
  −
| FAT offset
  −
|-
  −
| 0x28
   
| 4
 
| 4
| FAT entry count
+
| Journal block count
 
|-
 
|-
| 0x2C
+
| 0x0C
 
| 4
 
| 4
 
| Padding
 
| Padding
|-
  −
| 0x30
  −
| 8
  −
| Data offset
  −
|-
  −
| 0x38
  −
| 4
  −
| Data block count
  −
|-
  −
| 0x3C
  −
| 4
  −
| Padding
  −
|-
  −
| 0x40
  −
| 8
  −
| Directory table block index
  −
|-
  −
| 0x48
  −
| 8
  −
| File table block index
   
|-
 
|-
 
|}
 
|}
   −
=== Remap storage header ===
+
== Extra data ==
    
{| class="wikitable"
 
{| class="wikitable"
Line 539: Line 434:  
|-
 
|-
 
| 0x00
 
| 0x00
| 4
  −
| Magic ("RMAP")
  −
|-
  −
| 0x04
  −
| 4
  −
| Version? (Must be 0x10000 or less)
  −
|-
  −
| 0x08
  −
| 4
  −
| Number of remapping entries
  −
|-
  −
| 0x0C
  −
| 4
  −
| Number of remapping segments
  −
|-
  −
| 0x10
  −
| 4
  −
| Number of bits reserved for the segment index in virtual offsets
  −
|-
   
| 0x40
 
| 0x40
|
+
| [[Filesystem_services#SaveDataCreationInfo|SaveDataCreationInfo]]
| End
  −
|}
  −
 
  −
=== Extra data ===
  −
 
  −
{| class="wikitable"
  −
|-
  −
! Start
  −
! Length
  −
! Description
  −
|-
  −
| 0x00
  −
| 0x40
  −
| [[Filesystem_services#Save_Struct|Save Struct]]
   
|-
 
|-
 
| 0x40
 
| 0x40
Line 608: Line 470:  
|}
 
|}
   −
== Remap Storage ==
+
= 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.
 
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.
Line 624: Line 486:  
Segment index: 0x3 Offset: 0x000000000000100
 
Segment index: 0x3 Offset: 0x000000000000100
   −
=== Remapping Entry ===
+
== 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
 +
| Number of remapping segments
 +
|-
 +
| 0x10
 +
| 4
 +
| Number of bits reserved for the segment index in virtual offsets
 +
|-
 +
| 0x40
 +
|
 +
| End
 +
|}
 +
 
 +
== Remapping Entry ==
    
{| class="wikitable"
 
{| class="wikitable"
Line 654: Line 549:  
|}
 
|}
   −
== Duplex Storage ==
+
= Duplex Storage =
    
A Duplex Storage contains four separate elements: [[#Duplex header|a header]], a bitmap, and two identically-sized chunks of data.
 
A Duplex Storage contains four separate elements: [[#Duplex header|a header]], a bitmap, and two identically-sized chunks of data.
Line 660: Line 555:  
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.
 
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 ===
+
== 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.
 
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.
Line 666: Line 561:  
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.
 
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 ===
+
== 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.
 
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.
Line 674: Line 569:  
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.
 
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.
   −
== Files ==
+
== Duplex header ==
   −
=== Directory Table Entry ===
+
* Block sizes are stored as powers of 2
 
  −
* Index 0 is the start of a linked list that contains all invalid/inactive directories.
  −
* Index 1 is the start of a linked list that contains all valid/active directories.
  −
* Index 2 is the root directory.
      
{| class="wikitable"
 
{| class="wikitable"
Line 690: Line 581:  
| 0x00
 
| 0x00
 
| 4
 
| 4
| Parent directory node index
+
| Magic ("DPFS")
 
|-
 
|-
 
| 0x04
 
| 0x04
| 64
+
| 4
| Filename
+
| Version (0.1.x.x)
 
|-
 
|-
| 0x44
+
| 0x08
 +
| 8
 +
| Master bitmap offset
 +
|-
 +
| 0x10
 +
| 8
 +
| Master bitmap size
 +
|-
 +
| 0x18
 
| 4
 
| 4
| Next sibling directory node index
+
| Master bitmap block size power
 +
|-
 +
| 0x1C
 +
| 8
 +
| Level 1 offset
 +
|-
 +
| 0x24
 +
| 8
 +
| Level 1 size
 
|-
 
|-
| 0x48
+
| 0x2C
 
| 4
 
| 4
| First child directory node index
+
| Level 1 block size power
 
|-
 
|-
| 0x4c
+
| 0x30
 
| 8
 
| 8
| First child file node index
+
| Level 2 offset
 
|-
 
|-
| 0x54
+
| 0x38
 
| 8
 
| 8
| Unused?
+
| Level 2 size
 
|-
 
|-
| 0x5c
+
| 0x40
 
| 4
 
| 4
| Next directory node index in the chain of invalid or valid directories. If this is the first block in a list of length 0, this value will contain the total number of directory nodes.
+
| Level 2 block size power
 
|-
 
|-
 
|}
 
|}
   −
=== File Table Entry ===
+
= Save FS =
   −
* Index 0 is the start of a linked list that contains all invalid/inactive files.
+
== Save FS header ==
* Index 1 is the start of a linked list that contains all valid/active files.
+
 
 +
* Structure is different than 3DS.
    
{| class="wikitable"
 
{| class="wikitable"
Line 731: Line 639:  
| 0x00
 
| 0x00
 
| 4
 
| 4
| Parent directory node index
+
| Magic ("SAVE")
 
|-
 
|-
 
| 0x04
 
| 0x04
| 64
+
| 4
| Filename
+
| Version (0.6.x.x)
 +
|-
 +
| 0x08
 +
| 8
 +
| Number of blocks. Does not change if save file is resized.
 +
|-
 +
| 0x10
 +
| 8
 +
| Block Size
 +
|-
 +
| 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
 +
| Block size
 +
|-
 +
| 0x08
 +
| 8
 +
| FAT offset
 +
|-
 +
| 0x10
 +
| 4
 +
| FAT entry count
 +
|-
 +
| 0x14
 +
| 4
 +
| Padding
 +
|-
 +
| 0x18
 +
| 8
 +
| Data offset
 +
|-
 +
| 0x20
 +
| 4
 +
| Data block count
 +
|-
 +
| 0x24
 +
| 4
 +
| Padding
 +
|-
 +
| 0x28
 +
| 4
 +
| Directory table block index
 
|-
 
|-
| 0x44
+
| 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
 
| 4
| Next sibling file node index
+
| Next entry index. A value of 0 indicates the end of the list.
 +
|-
 +
| 0x04
 +
| sizeof(T)
 +
| Value of type T.
 
|-
 
|-
| 0x48
+
|}
 +
 
 +
=== Save File Info ===
 +
 
 +
Holds the information of a single file.
 +
 
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! Length
 +
! Description
 +
|-
 +
| 0x00
 
| 4
 
| 4
| Index of the block that the file starts at
+
| Starting block index.
 
|-
 
|-
| 0x4c
+
| 0x04
 
| 8
 
| 8
| File size in bytes
+
| File length in bytes.
 
|-
 
|-
| 0x54
+
| 0x0C
 
| 8
 
| 8
| Unused?
+
| 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.
 +
|-
 +
|}
 +
 
 +
== 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.
 +
|-
 +
|}
 +
 
 +
=== Save FS List Key ===
 +
 
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! Length
 +
! Description
 +
|-
 +
| 0x00
 +
| 4
 +
| 32-bit integer.
 +
|-
 +
| 0x04
 +
| 0x40
 +
| 0x40-byte string.
 +
|-
 +
|}
 +
 
 +
=== Save FS List Entry ===
 +
 
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! Length
 +
! Description
 +
|-
 +
| 0x00
 +
| 0x44
 +
| Key.
 +
|-
 +
| 0x44
 +
| sizeof(T)
 +
| Value.
 
|-
 
|-
| 0x5c
+
| 0x44 + sizeof(T)
 
| 4
 
| 4
| Next file node index in the chain of invalid or valid files. If this is the first block in a list of length 0, this value will contain the total number of file nodes.
+
| Next entry node index. A value of 0 indicates the end of the list.
 
|-
 
|-
 
|}
 
|}
77

edits

Navigation menu