Editing Hey! Pikmin save file

Jump to navigation Jump to search

Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.

The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then save the changes below to finish undoing the edit.

Latest revision Your text
Line 1: Line 1:
 
{{stub}}
 
{{stub}}
The file format for a ''Hey! Pikmin'' [[Save files|saved game]]. Note: this comes from analyses of the <code>.sav</code> files generated by Citra.
+
The file format for a ''Hey! Pikmin'' saved game. Note: this comes from analyses of the <code>.sav</code> files generated by Citra.
  
 
== Format ==
 
== Format ==
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.
+
The save data is split into several blocks that start with 4-byte magic words.
  
 
=== SAVE ===
 
=== SAVE ===
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
 
|-
 
|-
! File || Block
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>SAVE</code>.
 
|-
 
|-
| <code>0x0000</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>SAVE</code>.
+
| <code>000C</code> || 4 bytes || Save data checksum || {{unsure|Calculated using an unknown algorithm.}}
|-
 
| <code>0x000C</code> || <code>0x000C</code> || 4 bytes || Save data checksum || Calculated using [[#Checksum|this method]].
 
 
|}
 
|}
  
 
=== NEWS ===
 
=== NEWS ===
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.
 
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
|-
 
! File || Block
 
|-
 
| <code>0x0010</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>NEWS</code>.
 
|-
 
| <code>0x0014</code> || <code>0x0004</code> || 4 bytes || Unknown ||
 
 
|-
 
|-
| <code>0x0018</code> || <code>0x0008</code> || 4 bytes || Unknown news controller 1 || 0 when the player hasn't heard the news about 1-A yet, 1 when they have.
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>NEWS</code>.
 
|-
 
|-
| <code>0x001C</code> || <code>0x000C</code> || 4 bytes || Unknown news controller 2 || 0 when the player hasn't heard the news about 1-A yet, 8 when they have.
 
 
|}
 
|}
  
 
=== 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
+
! Offset || Length || Field || Notes
 
|-
 
|-
! File || Block
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>OPTI</code>.
 
|-
 
|-
| <code>0x0020</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>OPTI</code>.
+
| <code>0028</code> || 1 byte || Music volume || <code>01</code>, <code>02</code>, or <code>03</code>.
 
|-
 
|-
| <code>0x0024</code> || <code>0x0004</code> || 4 bytes || {{unknown|Unknown}} ||
 
|-
 
| <code>0x0028</code> || <code>0x0008</code> || 1 byte || Music volume || 0 to 3.
 
|-
 
| <code>0x0029</code> || <code>0x0009</code> || 1 byte || Sound effects volume || 0 to 3.
 
|-
 
| <code>0x002A</code> || <code>0x000A</code> || 2 bytes || {{unknown|Unknown}} ||
 
 
|}
 
|}
  
 
=== 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
+
! Offset || Length || Field || Notes
|-
 
! File || Block
 
|-
 
| <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>0000</code> || 4 bytes || Block magic word || Always <code>PBUF</code>.
 
|-
 
|-
| <code>0x005C</code> || <code>0x0030</code> || 36 bytes || Read entries from the other categories bitfield ||
 
 
|}
 
|}
  
 
=== PARK ===
 
=== PARK ===
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
|-
 
! File || Block
 
 
|-
 
|-
| <code>0x0080</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>PARK</code>.
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>PARK</code>.
 
|-
 
|-
 
|}
 
|}
Line 80: Line 50:
 
=== MINI ===
 
=== MINI ===
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
 
|-
 
|-
! File || Block
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>MINI</code>.
|-
 
| <code>0x04A4</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>MINI</code>.
 
 
|-
 
|-
 
|}
 
|}
Line 90: Line 58:
 
=== CRNT ===
 
=== CRNT ===
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
|-
 
! File || Block
 
 
|-
 
|-
| <code>0x0588</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>CRNT</code>.
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>CRNT</code>.
 
|-
 
|-
 
|}
 
|}
Line 100: Line 66:
 
=== GAME ===
 
=== GAME ===
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
|-
 
! File || Block
 
 
|-
 
|-
| <code>0x09A8</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>GAME</code>.
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>GAME</code>.
|-
 
| <code>0x09AC</code> || <code>0x0004</code> || 4 bytes || {{unknown|Unknown}} ||
 
|-
 
| <code>0x09B0</code> || <code>0x0008</code> || 4 bytes || Sparklium total || Values over 99999 will make the game assume it's just 99999
 
|-
 
| <code>0x09B4</code> || <code>0x000C</code> || 44 bytes || {{unknown|Unknown}} ||
 
 
|-
 
|-
 
|}
 
|}
Line 116: Line 74:
 
=== EVNT ===
 
=== EVNT ===
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
 
|-
 
|-
! File || Block
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>EVNT</code>.
|-
 
| <code>0x09E0</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>EVNT</code>.
 
 
|-
 
|-
 
|}
 
|}
Line 126: Line 82:
 
=== HELP ===
 
=== HELP ===
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
 
|-
 
|-
! File || Block
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>HELP</code>.
|-
 
| <code>0x0AE8</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>HELP</code>.
 
 
|-
 
|-
 
|}
 
|}
Line 136: Line 90:
 
=== RINF ===
 
=== RINF ===
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
 
|-
 
|-
! File || Block
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>RINF</code>.
|-
 
| <code>0x0BF0</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>RINF</code>.
 
 
|-
 
|-
 
|}
 
|}
Line 146: Line 98:
 
=== RSLT ===
 
=== RSLT ===
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
 
|-
 
|-
! File || Block
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>RSLT</code>.
|-
 
| <code>0x0C2C</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>RSLT</code>.
 
 
|-
 
|-
 
|}
 
|}
Line 156: Line 106:
 
=== SPER ===
 
=== SPER ===
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
 
|-
 
|-
! File || Block
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>SPER</code>.
|-
 
| <code>0x0C38</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>SPER</code>.
 
 
|-
 
|-
 
|}
 
|}
Line 166: Line 114:
 
=== STRE ===
 
=== STRE ===
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
 
|-
 
|-
! File || Block
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>STRE</code>.
|-
 
| <code>0x0C48</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>STRE</code>.
 
 
|-
 
|-
 
|}
 
|}
Line 176: Line 122:
 
=== PLRP ===
 
=== PLRP ===
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
|-
 
! File || Block
 
 
|-
 
|-
| <code>0x0C90</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>PLRP</code>.
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>PLRP</code>.
 
|-
 
|-
 
|}
 
|}
  
 
=== 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
+
! Offset || Length || Field || Notes
 
|-
 
|-
! File || Block
+
| <code>0000</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> || 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>0x0CED</code> || <code>0x000D</code> || 1 byte || Day || 1 to 31.
 
|-
 
| <code>0x0CEE</code> || <code>0x000E</code> || 1 byte || Hours || 0 to 23.
 
|-
 
| <code>0x0CEF</code> || <code>0x000F</code> || 1 byte || Minutes || 0 to 59.
 
|-
 
| <code>0x0CF0</code> || <code>0x0010</code> || 1 byte || Seconds || 0 to 59.
 
|-
 
| <code>0x0CF1</code> || <code>0x0011</code> || 11 bytes || {{unknown|Unknown}} ||
 
 
|}
 
|}
  
 
=== TTDS ===
 
=== TTDS ===
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
 
|-
 
|-
! File || Block
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>TTDS</code>.
|-
 
| <code>0x0CFC</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>TTDS</code>.
 
 
|-
 
|-
 
|}
 
|}
Line 223: Line 146:
 
=== WMAP ===
 
=== WMAP ===
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
 
|-
 
|-
! File || Block
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>WMAP</code>.
|-
 
| <code>0x0D08</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>WMAP</code>.
 
 
|-
 
|-
 
|}
 
|}
Line 233: Line 154:
 
=== SAMI ===
 
=== SAMI ===
 
{| class="wikitable sortable"
 
{| class="wikitable sortable"
! colspan="2" | Offset || rowspan="2" | Length || rowspan="2" | Field || rowspan="2" | Notes
+
! Offset || Length || Field || Notes
 
|-
 
|-
! File || Block
+
| <code>0000</code> || 4 bytes || Block magic word || Always <code>SAMI</code>.
|-
 
| <code>0x0D80</code> || <code>0x0000</code> || 4 bytes || Block magic word || Always <code>SAMI</code>.
 
 
|-
 
|-
 
|}
 
|}
 
== 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:
 
 
<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>
 
 
== Tools ==
 
* [https://github.com/Espyo/Misc_Pikmin_tools/tree/master/hp_save_editor Hey! Pikmin save editor]
 
  
 
[[Category:File formats]]
 
[[Category:File formats]]
 
[[Category:Hey! Pikmin]]
 
[[Category:Hey! Pikmin]]

Please note that all contributions to Pikmin Technical Knowledge Base are considered to be released under the Creative Commons Attribution-ShareAlike (see Pikmin Technical Knowledge Base:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!

To edit this page, please answer the question that appears below (more info):

Cancel Editing help (opens in new window)

Templates used on this page: