Difference between revisions of "Hey! Pikmin save file"
Jump to navigation
Jump to search
(Added some values.) |
m |
||
Line 11: | Line 11: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x0000</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>SAVE</code>. |
|- | |- | ||
− | | <code> | + | | <code>0x000C</code> || <code>0x000C</code> || 4 bytes || Save data checksum || Calculated using [[#Checksum|this method]]. |
|} | |} | ||
Line 22: | Line 22: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x0010</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>NEWS</code>. |
|- | |- | ||
|} | |} | ||
Line 33: | Line 33: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x0020</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>OPTI</code>. |
|- | |- | ||
− | | <code> | + | | <code>0x0024</code> || <code>0x0004</code> || 4 bytes || {{unknown|Unknown}} || |
|- | |- | ||
− | | <code> | + | | <code>0x0028</code> || <code>0x0008</code> || 1 byte || Music volume || 0 to 3. |
|- | |- | ||
− | | <code> | + | | <code>0x0029</code> || <code>0x0009</code> || 1 byte || Sound effects volume || 0 to 3. |
|- | |- | ||
− | | <code> | + | | <code>0x002A</code> || <code>0x000A</code> || 2 bytes || {{unknown|Unknown}} || |
|} | |} | ||
Line 50: | Line 50: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x002C</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>PBUF</code>. |
|- | |- | ||
|} | |} | ||
Line 60: | Line 60: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x0080</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>PARK</code>. |
|- | |- | ||
|} | |} | ||
Line 70: | Line 70: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x04A4</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>MINI</code>. |
|- | |- | ||
|} | |} | ||
Line 80: | Line 80: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x0588</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>CRNT</code>. |
|- | |- | ||
|} | |} | ||
Line 90: | Line 90: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x09A8</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>GAME</code>. |
|- | |- | ||
− | | <code> | + | | <code>0x09AC</code> || <code>0x0004</code> || 4 bytes || {{unknown|Unknown}} || |
|- | |- | ||
− | | <code> | + | | <code>0x09B0</code> || <code>0x0008</code> || 4 bytes || Sparklium total || Values over 99999 will make the game assume it's just 99999 |
|- | |- | ||
− | | <code> | + | | <code>0x09B4</code> || <code>0x000C</code> || 44 bytes || {{unknown|Unknown}} || |
|- | |- | ||
|} | |} | ||
Line 106: | Line 106: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x09E0</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>EVNT</code>. |
|- | |- | ||
|} | |} | ||
Line 116: | Line 116: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x0AE8</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>HELP</code>. |
|- | |- | ||
|} | |} | ||
Line 126: | Line 126: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x0BF0</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>0xRINF</code>. |
|- | |- | ||
|} | |} | ||
Line 136: | Line 136: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x0C2C</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>RSLT</code>. |
|- | |- | ||
|} | |} | ||
Line 146: | Line 146: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x0C38</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>SPER</code>. |
|- | |- | ||
|} | |} | ||
Line 156: | Line 156: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x0C48</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>STRE</code>. |
|- | |- | ||
|} | |} | ||
Line 166: | Line 166: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x0C90</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>PLRP</code>. |
|- | |- | ||
|} | |} | ||
Line 178: | Line 178: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x0CE0</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>DATE</code>. |
|- | |- | ||
− | | <code> | + | | <code>0x0CE4</code> || <code>0x0004</code> || 8 bytes || {{unknown|Unknown}} || |
|- | |- | ||
− | | <code> | + | | <code>0x0CEC</code> || <code>0x000C</code> || 1 byte || Month || 1 to 12. |
|- | |- | ||
− | | <code> | + | | <code>0x0CED</code> || <code>0x000D</code> || 1 byte || Day || 1 to 31. |
|- | |- | ||
− | | <code> | + | | <code>0x0CEE</code> || <code>0x000E</code> || 1 byte || Hours || 0 to 23. |
|- | |- | ||
− | | <code> | + | | <code>0x0CEF</code> || <code>0x000F</code> || 1 byte || Minutes || 0 to 59. |
|- | |- | ||
− | | <code> | + | | <code>0x0CF0</code> || <code>0x0010</code> || 1 byte || Seconds || 0 to 59. |
|- | |- | ||
− | | <code> | + | | <code>0x0CF1</code> || <code>0x0011</code> || 11 bytes || {{unknown|Unknown}} || |
|} | |} | ||
Line 201: | Line 201: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x0CFC</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>TTDS</code>. |
|- | |- | ||
|} | |} | ||
Line 211: | Line 211: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x0D08</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>WMAP</code>. |
|- | |- | ||
|} | |} | ||
Line 221: | Line 221: | ||
! File || Block | ! File || Block | ||
|- | |- | ||
− | | <code> | + | | <code>0x0D80</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>SAMI</code>. |
|- | |- | ||
|} | |} | ||
Line 229: | Line 229: | ||
The following python code can recalculate the checksum if the data after the checksum is modified: | The following python code can recalculate the checksum if the data after the checksum is modified: | ||
− | + | <pre> | |
− | + | import struct | |
− | + | from zlib import crc32 | |
− | + | ||
− | + | filepath = "radish0.sav" # put the script in the same folder as the save file | |
− | + | with open(filepath, "rb") as f: | |
− | + | header = f.read(0xC) | |
− | + | chksum = struct.unpack("I", f.read(4))[0] | |
− | + | ||
− | + | data = f.read() | |
− | + | ||
− | + | calculated_chksum = crc32(data[0:0xd87]) | |
− | + | with open(filepath, "wb") as f: | |
− | + | f.write(header) | |
− | + | f.write(struct.pack("I", calculated_chksum)) | |
+ | f.write(data) | ||
+ | </pre> | ||
[[Category:File formats]] | [[Category:File formats]] | ||
[[Category:Hey! Pikmin]] | [[Category:Hey! Pikmin]] |
Revision as of 22:53, 29 December 2017
This article is a stub.
The file format for a Hey! Pikmin saved game. Note: this comes from analyses of the .sav
files generated by Citra.
Contents
Format
The save data is split into several blocks that start with 4-byte magic words.
SAVE
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x0000 |
0x0000 |
4 bytes | Block magic word | Always SAVE .
|
0x000C |
0x000C |
4 bytes | Save data checksum | Calculated using this method. |
NEWS
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x0010 |
0x0000 |
4 bytes | Block magic word | Always NEWS .
|
OPTI
The player's preferences in the options menu.
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x0020 |
0x0000 |
4 bytes | Block magic word | Always OPTI .
|
0x0024 |
0x0004 |
4 bytes | Unknown[unsure] | |
0x0028 |
0x0008 |
1 byte | Music volume | 0 to 3. |
0x0029 |
0x0009 |
1 byte | Sound effects volume | 0 to 3. |
0x002A |
0x000A |
2 bytes | Unknown[unsure] |
PBUF
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x002C |
0x0000 |
4 bytes | Block magic word | Always PBUF .
|
PARK
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x0080 |
0x0000 |
4 bytes | Block magic word | Always PARK .
|
MINI
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x04A4 |
0x0000 |
4 bytes | Block magic word | Always MINI .
|
CRNT
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x0588 |
0x0000 |
4 bytes | Block magic word | Always CRNT .
|
GAME
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x09A8 |
0x0000 |
4 bytes | Block magic word | Always GAME .
|
0x09AC |
0x0004 |
4 bytes | Unknown[unsure] | |
0x09B0 |
0x0008 |
4 bytes | Sparklium total | Values over 99999 will make the game assume it's just 99999 |
0x09B4 |
0x000C |
44 bytes | Unknown[unsure] |
EVNT
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x09E0 |
0x0000 |
4 bytes | Block magic word | Always EVNT .
|
HELP
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x0AE8 |
0x0000 |
4 bytes | Block magic word | Always HELP .
|
RINF
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x0BF0 |
0x0000 |
4 bytes | Block magic word | Always 0xRINF .
|
RSLT
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x0C2C |
0x0000 |
4 bytes | Block magic word | Always RSLT .
|
SPER
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x0C38 |
0x0000 |
4 bytes | Block magic word | Always SPER .
|
STRE
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x0C48 |
0x0000 |
4 bytes | Block magic word | Always STRE .
|
PLRP
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x0C90 |
0x0000 |
4 bytes | Block magic word | Always PLRP .
|
DATE
Time at which the save was made.
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x0CE0 |
0x0000 |
4 bytes | Block magic word | Always DATE .
|
0x0CE4 |
0x0004 |
8 bytes | Unknown[unsure] | |
0x0CEC |
0x000C |
1 byte | Month | 1 to 12. |
0x0CED |
0x000D |
1 byte | Day | 1 to 31. |
0x0CEE |
0x000E |
1 byte | Hours | 0 to 23. |
0x0CEF |
0x000F |
1 byte | Minutes | 0 to 59. |
0x0CF0 |
0x0010 |
1 byte | Seconds | 0 to 59. |
0x0CF1 |
0x0011 |
11 bytes | Unknown[unsure] |
TTDS
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x0CFC |
0x0000 |
4 bytes | Block magic word | Always TTDS .
|
WMAP
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x0D08 |
0x0000 |
4 bytes | Block magic word | Always WMAP .
|
SAMI
Offset | Length | Field | Notes | |
---|---|---|---|---|
File | Block | |||
0x0D80 |
0x0000 |
4 bytes | Block magic word | Always SAMI .
|
Checksum
The checksum seems to be a CRC32 of the 0xd87 bytes that follow after the checksum (i.e. just one byte short of the entire remaining data), with crc32 as implemented by e.g. zlib.
The following python code can recalculate the checksum if the data after the checksum is modified:
import struct from zlib import crc32 filepath = "radish0.sav" # put the script in the same folder as the save file with open(filepath, "rb") as f: header = f.read(0xC) chksum = struct.unpack("I", f.read(4))[0] data = f.read() calculated_chksum = crc32(data[0:0xd87]) with open(filepath, "wb") as f: f.write(header) f.write(struct.pack("I", calculated_chksum)) f.write(data)