Difference between revisions of "Hey! Pikmin save file"

From Pikmin Technical Knowledge Base
Jump to navigation Jump to search
m
 
(2 intermediate revisions by 2 users not shown)
Line 3: Line 3:
  
 
== Format ==
 
== Format ==
The save data is split into several blocks that start with 4-byte magic words.
+
The save data is split into several blocks that start with 4-byte magic words. The blocks are ordered in the same order they are presented in this article.
  
 
=== SAVE ===
 
=== SAVE ===
Line 51: Line 51:
  
 
=== PBUF ===
 
=== PBUF ===
 +
Which log entries the player has unlocked, and which they have read.
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
 
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
 
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
Line 58: Line 59:
 
| <code>0x002C</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>PBUF</code>.
 
| <code>0x002C</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>PBUF</code>.
 
|-
 
|-
 +
| <code>0x0030</code> || <code>0x0004</code> || 4 bytes || Unknown || Messing with it can make the save file be recognized as empty in the title screen.
 +
|-
 +
| <code>0x0034</code> || <code>0x0008</code> || 36 bytes || Unlocked entries bitfield || The first 11 bits correspond to the Pikmin Logs category (blue pellet, red pellet, rock pellet, winged pellet, yellow pellet, Onion, Red Pikmin, Blue Pikmin, Yellow Pikmin, Rock Pikmin, Winged Pikmin). After that come the enemies, normal treasures, and amiibo treasures, but their order is all over the place.
 +
|-
 +
| <code>0x0058</code> || <code>0x002C</code> || 4 bytes || Read "Pikmin Logs" entries bitfield ||
 +
|-
 +
| <code>0x005C</code> || <code>0x0030</code> || 36 bytes || Read entries from the other categories bitfield ||
 
|}
 
|}
  
Line 132: Line 140:
 
! File || Block
 
! File || Block
 
|-
 
|-
| <code>0x0BF0</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>0xRINF</code>.
+
| <code>0x0BF0</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>RINF</code>.
 
|-
 
|-
 
|}
 
|}
Line 186: Line 194:
 
| <code>0x0CE0</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>DATE</code>.
 
| <code>0x0CE0</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>DATE</code>.
 
|-
 
|-
| <code>0x0CE4</code> || <code>0x0004</code> || 8 bytes || {{unknown|Unknown}} ||  
+
| <code>0x0CE4</code> || <code>0x0004</code> || 4 bytes || {{unknown|Unknown}} ||  
 +
|-
 +
| <code>0x0CE8</code> || <code>0x0008</code> || 4 bytes || Year || Human format. For instance, 2019 has the actual value "2019".
 
|-
 
|-
 
| <code>0x0CEC</code> || <code>0x000C</code> || 1 byte || Month || 1 to 12.
 
| <code>0x0CEC</code> || <code>0x000C</code> || 1 byte || Month || 1 to 12.
Line 232: Line 242:
  
 
== Checksum ==
 
== 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 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:
  
The following python code can recalculate the checksum if the data after the checksum is modified:
 
 
<pre>
 
<pre>
 
import struct
 
import struct

Latest revision as of 14:27, 11 September 2022

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.

Format[edit]

The save data is split into several blocks that start with 4-byte magic words. The blocks are ordered in the same order they are presented in this article.

SAVE[edit]

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[edit]

What "news" the player has seen. "News" are the cutscenes that take place inside the S.S. Dolphin II, where the ship explains something to Olimar.

Offset Length Field Notes
File Block
0x0010 0x0000 4 bytes Block magic word Always NEWS.
0x0014 0x0004 4 bytes Unknown
0x0018 0x0008 4 bytes Unknown news controller 1 0 when the player hasn't heard the news about 1-A yet, 1 when they have.
0x001C 0x000C 4 bytes Unknown news controller 2 0 when the player hasn't heard the news about 1-A yet, 8 when they have.

OPTI[edit]

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[edit]

Which log entries the player has unlocked, and which they have read.

Offset Length Field Notes
File Block
0x002C 0x0000 4 bytes Block magic word Always PBUF.
0x0030 0x0004 4 bytes Unknown Messing with it can make the save file be recognized as empty in the title screen.
0x0034 0x0008 36 bytes Unlocked entries bitfield The first 11 bits correspond to the Pikmin Logs category (blue pellet, red pellet, rock pellet, winged pellet, yellow pellet, Onion, Red Pikmin, Blue Pikmin, Yellow Pikmin, Rock Pikmin, Winged Pikmin). After that come the enemies, normal treasures, and amiibo treasures, but their order is all over the place.
0x0058 0x002C 4 bytes Read "Pikmin Logs" entries bitfield
0x005C 0x0030 36 bytes Read entries from the other categories bitfield

PARK[edit]

Offset Length Field Notes
File Block
0x0080 0x0000 4 bytes Block magic word Always PARK.

MINI[edit]

Offset Length Field Notes
File Block
0x04A4 0x0000 4 bytes Block magic word Always MINI.

CRNT[edit]

Offset Length Field Notes
File Block
0x0588 0x0000 4 bytes Block magic word Always CRNT.

GAME[edit]

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[edit]

Offset Length Field Notes
File Block
0x09E0 0x0000 4 bytes Block magic word Always EVNT.

HELP[edit]

Offset Length Field Notes
File Block
0x0AE8 0x0000 4 bytes Block magic word Always HELP.

RINF[edit]

Offset Length Field Notes
File Block
0x0BF0 0x0000 4 bytes Block magic word Always RINF.

RSLT[edit]

Offset Length Field Notes
File Block
0x0C2C 0x0000 4 bytes Block magic word Always RSLT.

SPER[edit]

Offset Length Field Notes
File Block
0x0C38 0x0000 4 bytes Block magic word Always SPER.

STRE[edit]

Offset Length Field Notes
File Block
0x0C48 0x0000 4 bytes Block magic word Always STRE.

PLRP[edit]

Offset Length Field Notes
File Block
0x0C90 0x0000 4 bytes Block magic word Always PLRP.

DATE[edit]

Time at which the save was made.

Offset Length Field Notes
File Block
0x0CE0 0x0000 4 bytes Block magic word Always DATE.
0x0CE4 0x0004 4 bytes Unknown[unsure]
0x0CE8 0x0008 4 bytes Year Human format. For instance, 2019 has the actual value "2019".
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[edit]

Offset Length Field Notes
File Block
0x0CFC 0x0000 4 bytes Block magic word Always TTDS.

WMAP[edit]

Offset Length Field Notes
File Block
0x0D08 0x0000 4 bytes Block magic word Always WMAP.

SAMI[edit]

Offset Length Field Notes
File Block
0x0D80 0x0000 4 bytes Block magic word Always SAMI.

Checksum[edit]

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)

Tools[edit]