Ocarina of time
Ocarina of time saves are still a tad unknown however thanks to spinout i have this:
Contents
Info
The Ocarina of Time save file consists of a simple 32 byte long header, and six 5200 byte long game files, in the order 1,2,3/1,2,3. The header can be freely edited. The individual game files cannot however, as the game will reject them if they've been modified, unless the checksum is manually recalculated.
File Header
Offset | Data Type | Length | Usage | Additional Info |
0x00 | Sound Options | |||
0x01 | Z-Target Options | 0 for Switch, 1 for Hold | ||
0x07 | String | 5 | Contains the text "ZELDA" | |
0x0C | uint32_t | 4 | 0xABABABAB if debug, 0x00000000 if 1.0U+ (may be different for J/E) |
Game Data
After the file header, there are 6 different game files
RAM Offsets
The save data format seems to match up to a portion of ram in game. Therefore, it's possible to determine what the values stored in the save file do by peeking into the RAM in-game. You can also use this table, along with the following table, to generate cheat codes. Simply take the appropriate offset for your game (ex. 1.0 U, 11A5D0), take an offset from the following table (ex. Respawn Point, 0x0002), and add them together (11A5D2). Then prefix it with either 80 (modify 1 byte) or 81 (modify 2 bytes), and assign a value to it (ex. 8111A5D2 0000)
Save Data Relative to Ram | |||||
Debug E | 1.0U | 1.1U | 1.2U | 1.0E | 1.1E |
15E660 | 11A5D0 | 11A790 | 11AC80 | 1183D0 | 118410 |
Checksum
Each segment of game data has a 16 bit checksum at offset 0x1352. It is generated by adding all previous 0x9A9 shorts together (overflowing to 0 when 0xFFFF is reached).
Game Data Format
Offset | Data Type | Length | Usage | Additional Info |
0x0002 | uint16_t | 2 | Entrance index | Stores the entrance Link starts/respawns at. See Debug ROM: Exit List for a listing of all values. If not inside a dungeon when loading a save from file, this value defaults back to either 00BB (Deku Tree) or 0053 (Temple of Time) |
0x0004 | uint32_t | 4 | Age Modifier | 0 = Adult Link, 1 = Child Link |
0x001C | String | 6 | Unknown | Contains the string "ZELDAZ". If different, the save will be considered corrupt even if the checksum is valid |
0x0022 | Short | 2 | Death Counter | |
0x0024 | String | 8 | Player Name | If the player name is less than 8 characters, the remaining char values will be DF. Charsets vary by language. |
0x002C | uint32_t | 4 | Heart containers | 0x10 is equivalent to 1 heart container |
0x0030? | ??? | 1-2? | Disk Drive Only flag | Setting to 1 will flag the save as a Disk Drive only file. The file cannot be accessed normally (but can be forced), and will crash on copy/erase attempt on a release build |
0x0032 | uint32_t | 4 | Health | 0x10 is equivalent to 1 full heart |
0x0036 | uint32_t | 4 | Rupees | |
0x003A | uint16_t | 2 | Some clock? | Increments every cycle, unless the game is paused. Resets whenever a new map is loaded |
0x0E64 | 32? | Farore's Wind Warp | If modifying values in-game, the warp point must be unloaded for the new values to take effect | |
0x0E64 | Long | X Coordinate | ||
0x0E68 | Long | Y Coordinate | ||
0x0E6B | Long | Z Coordinate | ||
0x0E72 | Short | Y-Axis Rotation | Direction that Link Faces on returning | |
0x0E7A | uint16_t | Entrance Index | Determines which scene Link is transported to. See Zelda 64 Scene Listings. | |
0x0E7F | Map Number | Determines which map of the scene to load | ||
0x0E83 | Warp Point Set | 1 = Warp point set, 0 = unset. | ||
0x1352 | uint16_t | 2 | Checksum | Checksum of previous 0x9A9 shorts (0x1352 bytes) |
C structs/functions
Beware of endianess! Big endian (N64 native) assumed.
#define LINK_ADULT 0 #define LINK_CHILD 1 typedef struct { uint32_t respawn_exit_n; /* 0x0004 */ uint32_t age; /* 0x0008 */ uint8_t __pad_00[0x014];/* 0x001C */ char str[5]; /* 0x0021 "ZELDA" in created file */ uint8_t __pad_01[0x001];/* 0x0022 */ uint16_t death_count; /* 0x0024 */ uint8_t name[8]; /* 0x002C Special char encoding */ uint8_t __pad_02[0x006];/* 0x0032 */ uint32_t rupee_count; /* 0x0036 */ uint8_t __pad_03[0x002];/* 0x0038 */ uint16_t scene_count; /* 0x003A May be uint32_t */ uint8_t __pad_04[0xE2A];/* 0x0E64 */ struct { float x, y, z; /* 0x0E70 */ uint16_t y_rot; /* 0x0E72 */ uint8_t __pad_00[0x8]; /* 0x0E7A */ uint16_t scene_no; /* 0x0E7C */ uint32_t map_no; /* 0x0E80 */ uint32_t isset; /* 0x0E84 */ } farore_warp; uint8_t __pad_05[0x4CE];/* 0x1352 */ uint16_t chksum; /* 0x1354 */ uint8_t __pad_06[0x0FC];/* 0x1540 */ } z_save_file; typedef struct { uint8_t sound_opt; uint8_t ztarget_opt; char tag[5]; /* "ZELDA" (not null terminated) */ z_save_file files[3]; } z_save; void calc_chksum( z_save_file *f ) { int16_t *data = (int16_t*)(f); int sum = 0; int i; f->chksum = 0; for( i = 0; i < 0x9A9; i++ ) { sum += data[i]; sum &= 0xFFFF; } f->chksum = (uint16_t)sum; }