Documentation Index
Fetch the complete documentation index at: https://mintlify.com/pret/pokeemerald/llms.txt
Use this file to discover all available pages before exploring further.
pokeemerald saves to a 128 KB flash chip organized as 32 sectors of 4 KB each. The save system rotates between two save slots for corruption protection and stores trainer data, bag contents, Pokémon party, PC storage, Hall of Fame records, and more.
Flash layout
The 32 sectors are divided into fixed-purpose regions. Sector numbers are defined in include/save.h.
| Sector(s) | Constant | Contents |
|---|
| 0 | SECTOR_ID_SAVEBLOCK2 | SaveBlock2 — trainer info, options, Pokédex |
| 1–4 | SECTOR_ID_SAVEBLOCK1_START – SECTOR_ID_SAVEBLOCK1_END | SaveBlock1 — current map, bag, money, etc. |
| 5–13 | SECTOR_ID_PKMN_STORAGE_START – SECTOR_ID_PKMN_STORAGE_END | PC Pokémon storage (boxes 1–9) |
| 14–27 | (Save Slot 2 mirror of 0–13) | Alternating save slot for corruption recovery |
| 28–29 | SECTOR_ID_HOF_1 / SECTOR_ID_HOF_2 | Hall of Fame records |
| 30 | SECTOR_ID_TRAINER_HILL | Trainer Hill data |
| 31 | SECTOR_ID_RECORDED_BATTLE | Recorded Battle data |
SECTORS_COUNT = 32; NUM_SECTORS_PER_SLOT = 14.
Sector structure
Each 4 KB sector is split into a data region and a footer:
struct SaveSector
{
u8 data[SECTOR_DATA_SIZE]; // 3968 bytes of save data
u8 unused[SECTOR_FOOTER_SIZE - 12]; // unused footer padding
u16 id; // sector identifier
u16 checksum; // XOR checksum of data
u32 signature; // must equal SECTOR_SIGNATURE
u32 counter; // incremented on each write
}; // total: SECTOR_SIZE (0x1000 = 4096 bytes)
Key constants from include/save.h:
| Constant | Value | Meaning |
|---|
SECTOR_DATA_SIZE | 3968 | Usable data bytes per sector |
SECTOR_FOOTER_SIZE | 128 | Footer size in bytes |
SECTOR_SIGNATURE | 0x8012025 | Magic number that marks a valid sector |
NUM_SAVE_SLOTS | 2 | Number of alternating save slots |
The game uses approximately 96% of available flash capacity. SaveBlock1 has roughly 84 bytes of headroom and SaveBlock2 has roughly 120 bytes before you must reorganize the sector layout.
Dual-slot rotation
NUM_SAVE_SLOTS = 2. Sectors 0–13 are Save Slot 1 and sectors 14–27 are Save Slot 2. On every normal save the game writes to the slot with the lower counter value. If a write is interrupted the other slot still holds a valid copy, preventing total save loss.
// Save Slot 1: sectors 0-13
// Save Slot 2: sectors 14-27
#define NUM_SECTORS_PER_SLOT 14
Save types
enum
{
SAVE_NORMAL, // standard in-game save
SAVE_LINK, // Link / Battle Frontier save
SAVE_EREADER, // deprecated in Emerald
SAVE_HALL_OF_FAME, // writes HOF sectors only
SAVE_OVERWRITE_DIFFERENT_FILE, // overwrites without slot check
SAVE_HALL_OF_FAME_ERASE_BEFORE // unused
};
Save status codes
| Constant | Value | Meaning |
|---|
SAVE_STATUS_EMPTY | 0 | No save data found |
SAVE_STATUS_OK | 1 | Save loaded successfully |
SAVE_STATUS_CORRUPT | 2 | Checksum or signature mismatch |
SAVE_STATUS_NO_FLASH | 4 | Flash chip not detected |
SAVE_STATUS_ERROR | 0xFF | Unrecoverable error |
The current status is stored in gSaveFileStatus (extern u16 gSaveFileStatus).
Key functions
| Function | Signature | Description |
|---|
TrySavingData | u8 TrySavingData(u8 saveType) | Attempts a full save; returns SAVE_STATUS_* |
LoadGameSave | u8 LoadGameSave(u8 saveType) | Reads save data from flash into RAM |
HandleSavingData | u8 HandleSavingData(u8 saveType) | Lower-level save dispatcher |
WriteSaveBlock2 | bool8 WriteSaveBlock2(void) | Writes only the SaveBlock2 sector |
WriteSaveBlock1Sector | bool8 WriteSaveBlock1Sector(void) | Writes one sector of SaveBlock1 incrementally |
ClearSaveData | void ClearSaveData(void) | Erases all save sectors |
TryReadSpecialSaveSector | u32 TryReadSpecialSaveSector(u8 sector, u8 *dst) | Reads HOF / Trainer Hill / Recorded Battle sectors |
TryWriteSpecialSaveSector | u32 TryWriteSpecialSaveSector(u8 sector, u8 *src) | Writes to a special sector |
Link save
For link-cable trades and the Battle Frontier, SaveBlock1 is written incrementally across multiple frames using a task:
bool8 LinkFullSave_Init(void);
bool8 LinkFullSave_WriteSector(void);
bool8 LinkFullSave_ReplaceLastSector(void);
bool8 LinkFullSave_SetLastSectorSignature(void);
void Task_LinkFullSave(u8 taskId);
Do not expand SaveBlock1 or SaveBlock2 beyond their headroom without updating gRamSaveSectorLocations and auditing every sector’s size field. Exceeding SECTOR_DATA_SIZE (3968 bytes) per sector silently truncates data.