In memory of Ben “bushing” Byer, who passed away on Monday, February 8th, 2016.

Difference between revisions of "Savegame Files"

From WiiBrew
Jump to navigation Jump to search
(Let's start this one too)
 
 
(29 intermediate revisions by 14 users not shown)
Line 1: Line 1:
(Feel free to fix or enhance this page)
+
= Format =
  
== Format ==
+
A savegame consists of a [[#Header|Header]], followed by a [[#Bk Header|Bk Header]] and a set of files contained in a [[#Files|files]] section, and finally a [[Certificate chain|certificate chain]]. The savgames are signed while being copied to the SD card using the Wii's private NG key.
  
=== Header ===
+
== Header ==
  
The Header is 0xF0C0 bytes long.
+
The Header is 0xF0C0 bytes long. It is encrypted (AES128-CBC) using the "sd-key" and the "sd-iv" initialization vector.
  
{| style="border-collapse: collapse; padding: 0.2em 0.2em 0.2em 0.2em;"
+
The plaintext header contents are described in the following table.
|- style="background-color: #ddd;"
+
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #cdc;" | '''Start'''
+
It's divided in two parts: The "main header" and the "banner"
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ccd;" | '''End'''
+
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ccc;" | '''Length'''
+
=== Main header ===
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dcc;" | '''Description'''
+
{| class="wikitable"
|- style="background-color: #ddd;"
+
|-  
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x0000
+
! Start
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x0007
+
! End
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 8
+
! Length
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | Savegame ID
+
! Description
|- style="background-color: #ddd;"
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x0008
+
| 0x0000
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x000B
+
| 0x0007
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4
+
| 8
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | size of header (0x72a0 or 0xf0a0)
+
| Savegame ID
|- style="background-color: #ddd;"
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x000C
+
| 0x0008
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x000D
+
| 0x000B
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 2
+
| 4
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | ?? unknown
+
| size of banner (0x72A0 or 0xF0A0, also seen 0xBAA0)
|- style="background-color: #ddd;"
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x000E
+
| 0x000C
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x001D
+
| 0x000C
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 16
+
| 1
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | md5 of plaintext header with md5 blanker applied
+
| permissions
|- style="background-color: #ddd;"
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x001E
+
| 0x000D
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x001F
+
| 0x000D
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 2
+
| 1
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | ?? unknown
+
| ?? unknown
|- style="background-color: #ddd;"
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x0020
+
| 0x000E
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x003F
+
| 0x001D
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 16
+
| 16
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | ?? unknown
+
| md5 of plaintext header with md5 blanker applied
|- style="background-color: #ddd;"
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x0040
+
| 0x001E
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x00BF
+
| 0x001F
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 128
+
| 2
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | title (?? in utf16)
+
| ?? unknown
|- style="background-color: #ddd;"
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x00C0
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x60BF
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 24576
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | banner (192x64)
 
|- style="background-color: #ddd;"
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x60C0
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x72BF
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4608
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | icon0 (48x48)
 
|- style="background-color: #ddd;"
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x72C0
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x84BF
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4608
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | icon1 (optional, present if size of header is 0xf0a0)
 
|- style="background-color: #ddd;"
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x84C0
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x96BF
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4608
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | icon2 (optional, present if size of header is 0xf0a0)
 
|- style="background-color: #ddd;"
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x96C0
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0xA8BF
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4608
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | icon3 (optional, present if size of header is 0xf0a0)
 
|- style="background-color: #ddd;"
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0xA8C0
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0xBABF
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4608
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | icon4 (optional, present if size of header is 0xf0a0)
 
|- style="background-color: #ddd;"
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0xBAC0
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0xCCBF
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4608
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | icon5 (optional, present if size of header is 0xf0a0)
 
|- style="background-color: #ddd;"
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0xCCC0
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0xDEBF
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4608
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | icon6 (optional, present if size of header is 0xf0a0)
 
|- style="background-color: #ddd;"
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0xDEC0
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0xF0BF
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4608
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | icon7 (optional, present if size of header is 0xf0a0)
 
 
|}
 
|}
  
  
=== Bk Header ===
+
=== Banner ===
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! End
 +
! Length
 +
! Description
 +
|-
 +
| 0x0020
 +
| 0x0023
 +
| 4
 +
| magic ('WIBN') (Wii Banner?)
 +
|-
 +
| 0x0024
 +
| 0x0027
 +
| 4
 +
| Flags - (Maskable) if set to 0x000001 the save cannot be copied from NAND via normal means, 0x000010 means to "Bounce" between 0 and the last frame. Setting it to 0x000000 makes the animation loop through all frames resetting to 0 on the final frame.
 +
|-
 +
| 0x0028
 +
| 0x002A
 +
| 2
 +
| AnimSpeed - Set per frame frameDelay*((animSpeed >> (2*nFrame))&3); 0 being no animation 1-3 being progressively slower, this is a little strange, but if the first two frames are set to 0, don't animate the icon.
 +
|-
 +
| 0x002B
 +
| 0x003F
 +
| 22
 +
| Reserved
 +
|-
 +
| 0x0040
 +
| 0x007F
 +
| 64
 +
| Game title (big endian Unicode)
 +
|-
 +
| 0x0080
 +
| 0x00BF
 +
| 64
 +
| Game subtitle (big endian Unicode)
 +
|-
 +
| 0x00C0
 +
| 0x60BF
 +
| 24576
 +
| banner (192x64) - RGB5A3 GX texture format
 +
|-
 +
| 0x60C0
 +
| 0x72BF
 +
| 4608
 +
| icon0 (48x48) - RGB5A3 GX texture format
 +
|-
 +
| 0x72C0
 +
| 0x84BF
 +
| 4608
 +
| icon1 (optional, present if size of header is 0xF0C0)
 +
|-
 +
| 0x84C0
 +
| 0x96BF
 +
| 4608
 +
| icon2 (optional, present if size of header is 0xF0C0)
 +
|-
 +
| 0x96C0
 +
| 0xA8BF
 +
| 4608
 +
| icon3 (optional, present if size of header is 0xF0C0)
 +
|-
 +
| 0xA8C0
 +
| 0xBABF
 +
| 4608
 +
| icon4 (optional, present if size of header is 0xF0C0)
 +
|-
 +
| 0xBAC0
 +
| 0xCCBF
 +
| 4608
 +
| icon5 (optional, present if size of header is 0xF0C0)
 +
|-
 +
| 0xCCC0
 +
| 0xDEBF
 +
| 4608
 +
| icon6 (optional, present if size of header is 0xF0C0)
 +
|-
 +
| 0xDEC0
 +
| 0xF0BF
 +
| 4608
 +
| icon7 (optional, present if size of header is 0xF0C0)
 +
|}
  
The Bk Header is 0x80 bytes long.
+
== Bk ("BacKup") Header ==
  
{| style="border-collapse: collapse; padding: 0.2em 0.2em 0.2em 0.2em;"
+
The Bk Header is 0x70 bytes long (plus 0x10 bytes of padding/aligning). It is not encrypted.
|- style="background-color: #ddd;"
+
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #cdc;" | '''Start'''
+
{| class="wikitable"
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ccd;" | '''End'''
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ccc;" | '''Length'''
+
! Start
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dcc;" | '''Description'''
+
! End
|- style="background-color: #ddd;"
+
! Length
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x000
+
! Description
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x003
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4
+
| 0x000
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | magic1 (0x00000070)
+
| 0x003
|- style="background-color: #ddd;"
+
| 4
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x004
+
| Size of the header (0x00000070)
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x005
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 2
+
| 0x004
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | magic2 ('Bk')
+
| 0x005
|- style="background-color: #ddd;"
+
| 2
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x006
+
| magic ('Bk')
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x007
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 2
+
| 0x006
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | magic3 (0x0001)
+
| 0x007
|- style="background-color: #ddd;"
+
| 2
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x008
+
| magic2 or version (0x0001)
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x00B
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4
+
| 0x008
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | NG id
+
| 0x00B
|- style="background-color: #ddd;"
+
| 4
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x00C
+
| NG id
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x00F
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4
+
| 0x00C
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | number of files
+
| 0x00F
|- style="background-color: #ddd;"
+
| 4
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x010
+
| number of files
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x013
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4
+
| 0x010
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | size of files
+
| 0x013
|- style="background-color: #ddd;"
+
| 4
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x014
+
| size of files
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x017
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4
+
| 0x014
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | ?? unknown
+
| 0x017
|- style="background-color: #ddd;"
+
| 4
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x018
+
| ?? unknown
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x01B
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4
+
| 0x018
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | ?? unknown
+
| 0x01B
|- style="background-color: #ddd;"
+
| 4
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x01C
+
| ?? unknown
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x01F
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4
+
| 0x01C
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | total size
+
| 0x01F
|- style="background-color: #ddd;"
+
| 4
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x020
+
| total size
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x05F
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 64
+
| 0x020
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | ?? unknown
+
| 0x05F
|- style="background-color: #ddd;"
+
| 64
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x060
+
| ?? unknown
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x063
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4
+
| 0x060
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | ?? unknown
+
| 0x063
|- style="background-color: #ddd;"
+
| 4
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x064
+
| ?? unknown
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x067
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4
+
| 0x064
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | Game ID (ex. 'RMGP' for Super Mario Galaxy)
+
| 0x067
|- style="background-color: #ddd;"
+
| 4
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x068
+
| Game ID (ex. 'RMGP' for Super Mario Galaxy)
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x06B
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4
+
| 0x068
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | ?? unknown
+
| 0x06D
|- style="background-color: #ddd;"
+
| 6
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x06C
+
| Mac address of the wii
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x06F
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4
+
| 0x06E
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | ?? unknown
+
| 0x06F
|- style="background-color: #ddd;"
+
| 2
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x070
+
| ?? unknown
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x07F
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 16
+
| 0x070
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | null padded
+
| 0x07F
 +
| 16
 +
| null padded
 
|}
 
|}
 
  
 
== Files ==
 
== Files ==
  
The savegame consists of several files up to the number specified in the Bk Header.
+
A savegame consists of several files up to the number specified in the [[#Bk Header|Bk Header]].
Files are stored immediatelly after the Bk Header using the format described in the next sections.
+
Each file is described using a [[#File Header|File Header]] and a [[#File Data|File Data]] section. Files are stored immediately after the [[#Bk Header|Bk Header]] using the format described in the next sections.
  
 
=== File Header ===
 
=== File Header ===
  
The file header is 0x80 bytes long.
+
The file header is 0x80 bytes long. It is not encrypted.
 +
 
 +
{| class="wikitable"
 +
|-
 +
! Start
 +
! End
 +
! Length
 +
! Description
 +
|-
 +
| 0x000
 +
| 0x003
 +
| 4
 +
| magic1 (0x03adf17e)
 +
|-
 +
| 0x004
 +
| 0x007
 +
| 4
 +
| size of file
 +
|-
 +
| 0x008
 +
| 0x008
 +
| 1
 +
| permissions
 +
|-
 +
| 0x009
 +
| 0x009
 +
| 1
 +
| attributes
 +
|-
 +
| 0x00A
 +
| 0x00A
 +
| 1
 +
| type (1=file, 2=directory)
 +
|-
 +
| 0x00B
 +
| variable
 +
| variable
 +
| name (null terminated)
 +
|-
 +
| ...
 +
| ...
 +
| ...
 +
| ...
 +
|-
 +
| 0x050
 +
| 0x05F
 +
| 16
 +
| IV for file data decryption
 +
|-
 +
| 0x060
 +
| 0x07F
 +
| ...
 +
| ?? unknown
 +
|}
 +
 
 +
=== File Data ===
 +
 
 +
File data comes after the [[#File Header|File Header]]. Each file actually occupies the length specified in the file header rounded up to the next 64 byte boundary. Data is encrypted (AES128-CBC) using the "sd-key" and the initialization vector specified in the File Header.
 +
 
 +
=== Parsing savegames ===
  
{| style="border-collapse: collapse; padding: 0.2em 0.2em 0.2em 0.2em;"
+
Savegames can be decrypted and unpacked with segher's tachtig and created with his twintig.
|- style="background-color: #ddd;"
+
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #cdc;" | '''Start'''
+
=== Plaintext certificate chain area ===
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ccd;" | '''End'''
+
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ccc;" | '''Length'''
+
For more information about each certificate layout, please refer to [[Certificate chain|this]] page.
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dcc;" | '''Description'''
+
 
|- style="background-color: #ddd;"
+
{| class="wikitable"
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x000
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x003
+
! Start
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4
+
! End
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | magic1 (0x03adf17e)
+
! Length
|- style="background-color: #ddd;"
+
! Description
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x004
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x007
+
| 0x000
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 4
+
| 0x17F
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | size of file
+
| 0x180
|- style="background-color: #ddd;"
+
| Copy of the console-specific device certificate (also known as <code>NG</code> certificate), returned by [[:/dev/es|ES_GetDeviceCert]].
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x008
+
|-
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x008
+
| 0x180
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 1
+
| 0x2FF
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | permissions
+
| 0x180
|- style="background-color: #ddd;"
+
| Application-specific certificate (also known as <code>AP</code> certificate), dynamically generated during the <code>data.bin</code> file creation.
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x009
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x009
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 1
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | attributes
 
|- style="background-color: #ddd;"
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x00A
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x00A
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 1
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | type (1=file, 2=directory)
 
|- style="background-color: #ddd;"
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x00B
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | variable
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | variable
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | name (null tereminated)
 
|- style="background-color: #ddd;"
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ded;" | 0x050
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #dde;" | 0x05F
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #ddd;" | 16
 
| style="border: 1px solid #ccc; padding: 0.2em; background-color: #edd;" | IV for file data decryption
 
 
|}
 
|}
  
=== File data ===
+
Both certificates hold a trimmed ECDSA signature and a trimmed 0x3C-byte long ECC public key. Both values must be padded with two leading <code>\x00</code> before each coordinate in order to be able to use them as part of crypto functions.
 +
 
 +
The certificate name/identity from the <code>AP</code> certificate is always set to <code>AP0000000100000002</code>, because it's always generated by the System Menu.
 +
 
 +
The ECC public key from the <code>AP</code> certificate is nothing more than an ECC shared secret generated using the random ECC private key from this very same certificate.
 +
 
 +
Finally, the ECDSA signature from the <code>AP</code> certificate is issued by the <code>NG</code> certificate, using the console-specific ECC private key (stored inside the [[Hardware/OTP|OTP]]).
 +
 
 +
== See Also ==
 +
 
 +
* [[FE100]]
 +
* [[Wii Savegame Parser]]
  
File data comes after the File Header. Each file actually occupies the length specified in the file header rounded up to the next 64 byte boundary. File data is encrypted using the "sd-key" and the initialization vector specified in the File Header.
+
[[Category:File formats]]

Latest revision as of 05:43, 28 November 2024

Format

A savegame consists of a Header, followed by a Bk Header and a set of files contained in a files section, and finally a certificate chain. The savgames are signed while being copied to the SD card using the Wii's private NG key.

Header

The Header is 0xF0C0 bytes long. It is encrypted (AES128-CBC) using the "sd-key" and the "sd-iv" initialization vector.

The plaintext header contents are described in the following table.

It's divided in two parts: The "main header" and the "banner"

Main header

Start End Length Description
0x0000 0x0007 8 Savegame ID
0x0008 0x000B 4 size of banner (0x72A0 or 0xF0A0, also seen 0xBAA0)
0x000C 0x000C 1 permissions
0x000D 0x000D 1 ?? unknown
0x000E 0x001D 16 md5 of plaintext header with md5 blanker applied
0x001E 0x001F 2 ?? unknown


Start End Length Description
0x0020 0x0023 4 magic ('WIBN') (Wii Banner?)
0x0024 0x0027 4 Flags - (Maskable) if set to 0x000001 the save cannot be copied from NAND via normal means, 0x000010 means to "Bounce" between 0 and the last frame. Setting it to 0x000000 makes the animation loop through all frames resetting to 0 on the final frame.
0x0028 0x002A 2 AnimSpeed - Set per frame frameDelay*((animSpeed >> (2*nFrame))&3); 0 being no animation 1-3 being progressively slower, this is a little strange, but if the first two frames are set to 0, don't animate the icon.
0x002B 0x003F 22 Reserved
0x0040 0x007F 64 Game title (big endian Unicode)
0x0080 0x00BF 64 Game subtitle (big endian Unicode)
0x00C0 0x60BF 24576 banner (192x64) - RGB5A3 GX texture format
0x60C0 0x72BF 4608 icon0 (48x48) - RGB5A3 GX texture format
0x72C0 0x84BF 4608 icon1 (optional, present if size of header is 0xF0C0)
0x84C0 0x96BF 4608 icon2 (optional, present if size of header is 0xF0C0)
0x96C0 0xA8BF 4608 icon3 (optional, present if size of header is 0xF0C0)
0xA8C0 0xBABF 4608 icon4 (optional, present if size of header is 0xF0C0)
0xBAC0 0xCCBF 4608 icon5 (optional, present if size of header is 0xF0C0)
0xCCC0 0xDEBF 4608 icon6 (optional, present if size of header is 0xF0C0)
0xDEC0 0xF0BF 4608 icon7 (optional, present if size of header is 0xF0C0)

Bk ("BacKup") Header

The Bk Header is 0x70 bytes long (plus 0x10 bytes of padding/aligning). It is not encrypted.

Start End Length Description
0x000 0x003 4 Size of the header (0x00000070)
0x004 0x005 2 magic ('Bk')
0x006 0x007 2 magic2 or version (0x0001)
0x008 0x00B 4 NG id
0x00C 0x00F 4 number of files
0x010 0x013 4 size of files
0x014 0x017 4 ?? unknown
0x018 0x01B 4 ?? unknown
0x01C 0x01F 4 total size
0x020 0x05F 64 ?? unknown
0x060 0x063 4 ?? unknown
0x064 0x067 4 Game ID (ex. 'RMGP' for Super Mario Galaxy)
0x068 0x06D 6 Mac address of the wii
0x06E 0x06F 2 ?? unknown
0x070 0x07F 16 null padded

Files

A savegame consists of several files up to the number specified in the Bk Header. Each file is described using a File Header and a File Data section. Files are stored immediately after the Bk Header using the format described in the next sections.

File Header

The file header is 0x80 bytes long. It is not encrypted.

Start End Length Description
0x000 0x003 4 magic1 (0x03adf17e)
0x004 0x007 4 size of file
0x008 0x008 1 permissions
0x009 0x009 1 attributes
0x00A 0x00A 1 type (1=file, 2=directory)
0x00B variable variable name (null terminated)
... ... ... ...
0x050 0x05F 16 IV for file data decryption
0x060 0x07F ... ?? unknown

File Data

File data comes after the File Header. Each file actually occupies the length specified in the file header rounded up to the next 64 byte boundary. Data is encrypted (AES128-CBC) using the "sd-key" and the initialization vector specified in the File Header.

Parsing savegames

Savegames can be decrypted and unpacked with segher's tachtig and created with his twintig.

Plaintext certificate chain area

For more information about each certificate layout, please refer to this page.

Start End Length Description
0x000 0x17F 0x180 Copy of the console-specific device certificate (also known as NG certificate), returned by ES_GetDeviceCert.
0x180 0x2FF 0x180 Application-specific certificate (also known as AP certificate), dynamically generated during the data.bin file creation.

Both certificates hold a trimmed ECDSA signature and a trimmed 0x3C-byte long ECC public key. Both values must be padded with two leading \x00 before each coordinate in order to be able to use them as part of crypto functions.

The certificate name/identity from the AP certificate is always set to AP0000000100000002, because it's always generated by the System Menu.

The ECC public key from the AP certificate is nothing more than an ECC shared secret generated using the random ECC private key from this very same certificate.

Finally, the ECDSA signature from the AP certificate is issued by the NG certificate, using the console-specific ECC private key (stored inside the OTP).

See Also