> ## 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.

# Save System

> Flash sector layout, SaveBlock structures, and save/load mechanics.

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:

```c theme={null}
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       |

<Note>
  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.
</Note>

## 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.

```c theme={null}
// Save Slot 1: sectors  0-13
// Save Slot 2: sectors 14-27
#define NUM_SECTORS_PER_SLOT 14
```

## Save types

```c theme={null}
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:

```c theme={null}
bool8 LinkFullSave_Init(void);
bool8 LinkFullSave_WriteSector(void);
bool8 LinkFullSave_ReplaceLastSector(void);
bool8 LinkFullSave_SetLastSectorSignature(void);
void  Task_LinkFullSave(u8 taskId);
```

<Warning>
  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.
</Warning>
