Difference between revisions of "Hey! Pikmin save file"

From Pikmin Technical Knowledge Base
Jump to navigation Jump to search
(→‎Checksum: add info on the checksum algorithm)
(Added some values.)
Line 27: Line 27:
  
 
=== OPTI ===
 
=== OPTI ===
 +
The player's preferences in the options menu.
 
{| 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 34: Line 35:
 
| <code>0020</code> || <code>0000</code> || 4 bytes || Block magic word || Always <code>OPTI</code>.
 
| <code>0020</code> || <code>0000</code> || 4 bytes || Block magic word || Always <code>OPTI</code>.
 
|-
 
|-
| <code>0028</code> || <code>0008</code> || 1 byte || Music volume || <code>01</code>, <code>02</code>, or <code>03</code>.
+
| <code>0024</code> || <code>0004</code> || 4 bytes || {{unknown|Unknown}} ||  
 
|-
 
|-
 +
| <code>0028</code> || <code>0008</code> || 1 byte || Music volume || <code>00</code> to <code>03</code>.
 +
|-
 +
| <code>0029</code> || <code>0009</code> || 1 byte || Sound effects volume || <code>00</code> to <code>03</code>.
 +
|-
 +
| <code>002A</code> || <code>000A</code> || 2 bytes || {{unknown|Unknown}} ||
 
|}
 
|}
  
Line 85: Line 91:
 
|-
 
|-
 
| <code>09A8</code> || <code>0000</code> || 4 bytes || Block magic word || Always <code>GAME</code>.
 
| <code>09A8</code> || <code>0000</code> || 4 bytes || Block magic word || Always <code>GAME</code>.
 +
|-
 +
| <code>09AC</code> || <code>0004</code> || 4 bytes || {{unknown|Unknown}} ||
 +
|-
 +
| <code>09B0</code> || <code>0008</code> || 4 bytes || Sparklium total || Values over 99999 will make the game assume it's just 99999
 +
|-
 +
| <code>09B4</code> || <code>000C</code> || 44 bytes || {{unknown|Unknown}} ||
 
|-
 
|-
 
|}
 
|}
Line 159: Line 171:
  
 
=== DATE ===
 
=== DATE ===
 +
Time at which the save was made.
 +
 
{| 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 166: Line 180:
 
| <code>0CE0</code> || <code>0000</code> || 4 bytes || Block magic word || Always <code>DATE</code>.
 
| <code>0CE0</code> || <code>0000</code> || 4 bytes || Block magic word || Always <code>DATE</code>.
 
|-
 
|-
 +
| <code>0CE4</code> || <code>0004</code> || 8 bytes || {{unknown|Unknown}} ||
 +
|-
 +
| <code>0CEC</code> || <code>000C</code> || 1 byte || Month || 1 to 12.
 +
|-
 +
| <code>0CED</code> || <code>000D</code> || 1 byte || Day || 1 to 31.
 +
|-
 +
| <code>0CEE</code> || <code>000E</code> || 1 byte || Hours || 0 to 23.
 +
|-
 +
| <code>0CEF</code> || <code>000F</code> || 1 byte || Minutes || 0 to 59.
 +
|-
 +
| <code>0CF0</code> || <code>0010</code> || 1 byte || Seconds || 0 to 59.
 +
|-
 +
| <code>0CF1</code> || <code>0011</code> || 11 bytes || {{unknown|Unknown}} ||
 
|}
 
|}
  

Revision as of 23:47, 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.

Format

The save data is split into several blocks that start with 4-byte magic words.

SAVE

Offset Length Field Notes
File Block
0000 0000 4 bytes Block magic word Always SAVE.
000C 000C 4 bytes Save data checksum Calculated using an unknown algorithm.[unsure]

NEWS

Offset Length Field Notes
File Block
0010 0000 4 bytes Block magic word Always NEWS.

OPTI

The player's preferences in the options menu.

Offset Length Field Notes
File Block
0020 0000 4 bytes Block magic word Always OPTI.
0024 0004 4 bytes Unknown[unsure]
0028 0008 1 byte Music volume 00 to 03.
0029 0009 1 byte Sound effects volume 00 to 03.
002A 000A 2 bytes Unknown[unsure]

PBUF

Offset Length Field Notes
File Block
002C 0000 4 bytes Block magic word Always PBUF.

PARK

Offset Length Field Notes
File Block
0080 0000 4 bytes Block magic word Always PARK.

MINI

Offset Length Field Notes
File Block
04A4 0000 4 bytes Block magic word Always MINI.

CRNT

Offset Length Field Notes
File Block
0588 0000 4 bytes Block magic word Always CRNT.

GAME

Offset Length Field Notes
File Block
09A8 0000 4 bytes Block magic word Always GAME.
09AC 0004 4 bytes Unknown[unsure]
09B0 0008 4 bytes Sparklium total Values over 99999 will make the game assume it's just 99999
09B4 000C 44 bytes Unknown[unsure]

EVNT

Offset Length Field Notes
File Block
09E0 0000 4 bytes Block magic word Always EVNT.

HELP

Offset Length Field Notes
File Block
0AE8 0000 4 bytes Block magic word Always HELP.

RINF

Offset Length Field Notes
File Block
0BF0 0000 4 bytes Block magic word Always RINF.

RSLT

Offset Length Field Notes
File Block
0C2C 0000 4 bytes Block magic word Always RSLT.

SPER

Offset Length Field Notes
File Block
0C38 0000 4 bytes Block magic word Always SPER.

STRE

Offset Length Field Notes
File Block
0C48 0000 4 bytes Block magic word Always STRE.

PLRP

Offset Length Field Notes
File Block
0C90 0000 4 bytes Block magic word Always PLRP.

DATE

Time at which the save was made.

Offset Length Field Notes
File Block
0CE0 0000 4 bytes Block magic word Always DATE.
0CE4 0004 8 bytes Unknown[unsure]
0CEC 000C 1 byte Month 1 to 12.
0CED 000D 1 byte Day 1 to 31.
0CEE 000E 1 byte Hours 0 to 23.
0CEF 000F 1 byte Minutes 0 to 59.
0CF0 0010 1 byte Seconds 0 to 59.
0CF1 0011 11 bytes Unknown[unsure]

TTDS

Offset Length Field Notes
File Block
0CFC 0000 4 bytes Block magic word Always TTDS.

WMAP

Offset Length Field Notes
File Block
0D08 0000 4 bytes Block magic word Always WMAP.

SAMI

Offset Length Field Notes
File Block
0D80 0000 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)