The Gen 1 save file format: Pokémon Red, Blue & Yellow

Where everything lives in a 32 KiB Generation 1 battery save — offsets, checksums, the Pokémon struct, PC boxes and the text encoding. Verified against the pret disassemblies and against real cartridge dumps.

A Generation 1 save is 32 KiB of battery-backed SRAM, and almost all of it is empty. The parts that matter sit in three regions, guarded by three kinds of checksum, and once you know where they are the file stops being opaque.

Everything below is verified against the pret disassemblies of Red, Blue and Yellow — ram/sram.asm and ram/wram.asm — and then against real dumps of French and English cartridges. The tables are generated from the same constants this site’s own parser uses to read your uploads, so they cannot quietly drift out of date: if the parser changes, the page changes with it.

The file

A Gen 1 cartridge has 32 KiB of SRAM, arranged as four banks of $2000 bytes. A save file is a straight linear dump of all four, in order, so an offset in the file is an offset into SRAM with no translation needed.

Three of those banks are used:

A file that is exactly 32,768 bytes is the normal case. Files larger than that are common and usually still fine: some cartridge dumpers read an MBC’s maximum addressable SRAM rather than the size in the cartridge header, which for Yellow’s MBC5 means 128 KiB with the real save sitting in the first 32 KiB. Trailing bytes past the first $8000 can be discarded.

Where everything lives

Addresses are absolute offsets into the save file. The symbol column gives the name the region goes by in the disassembly, which is what you will want if you are cross-referencing against pret.

Address Bytes Symbol Field
0x2598 11 wPlayerName Player name, terminated
0x25A3 19 wPokedexOwned Pokédex owned flags, one bit per species
0x25B6 19 wPokedexSeen Pokédex seen flags, one bit per species
0x25C9 42 wNumBagItems Bag: item count, then (item id, quantity) pairs
0x25F3 3 wPlayerMoney Money, binary-coded decimal
0x2602 1 wObtainedBadges Badges, one bit each in gym order
0x2605 2 wPlayerID Trainer ID, 16-bit, fixed at New Game
0x260A 1 wCurMap Current map id
0x260D 1 wYCoord Player Y position, in walking steps
0x260E 1 wXCoord Player X position, in walking steps
0x27E6 102 wNumBoxItems PC item storage: count, then (item id, quantity) pairs
0x284C 1 wCurrentBoxNum Selected box, in the low 7 bits, counting from 0
0x2852 32 wToggleableObjectFlags Overworld object flags, one bit per ground item or missable sprite
0x299C 14 wObtainedHiddenItemsFlags Hidden item flags, one bit per hidden spot
0x29F3 320 wEventFlags Story event flags — every trainer beaten, every one-off done
0x2CED 5 wPlayTimeHours Play time: hours, maxed flag, minutes, seconds, frames
0x2F2C 404 wPartyCount Party: count, species list, 6 structs, trainer names, nicknames
0x30C0 1122 wBoxCount The selected PC box, live copy
0x3523 1 wMainDataCheckSum Main checksum over 0x2598-0x3522

The main checksum

Bank 1 is guarded by a single byte at 0x3523. It is the one’s complement of the sum of every byte from 0x2598 to 0x3522 inclusive, truncated to eight bits:

sum = 0
for address in 0x2598..0x3522:
    sum = (sum + byte[address]) & 0xFF
checksum = (~sum) & 0xFF

The game refuses a save whose checksum does not match, and so should you. It is also the reason you cannot edit a save by changing one byte in a hex editor and walking away: every edit inside that range has to be followed by recomputing this byte.

Note what the range covers. It starts at the player’s name and ends just before the checksum itself, which means it includes the party and the currently selected PC box — but not the other eleven boxes, which live in their own banks with their own checksums.

PC boxes

The twelve boxes are split across two banks: boxes 1 to 6 start at 0x4000, boxes 7 to 12 at 0x6000. Each box occupies 0x462 bytes, laid out exactly like the party region but with room for twenty Pokémon instead of six.

Each bank carries its own checksum, computed the same way as the main one, over the bank’s six boxes and stored in the byte immediately after them — at 0x4000 + 6 × 0x462 for the first bank, and the equivalent for the second. On a save where the player has never opened the PC, these banks are uninitialised and their checksums will not match; treat that as “no boxes here” rather than as a corrupt file.

There is one trap worth knowing about. The box you currently have selected exists twice: once in its bank, and once at 0x30C0 inside bank 1. The copy in bank 1 is the live one. The game writes the bank copy back when you switch boxes, so on a save taken mid-session the two can disagree, and the bank copy is the stale one. Read the active box — the number is in the low seven bits of 0x284C, plus one — from 0x30C0, and read the other eleven from their banks.

How a Pokémon is stored

Each box region and the party region share a layout: a count byte, then a list of species indexes closed by a 0xFF terminator, then the structs, then the original trainers’ names, then the nicknames. Each name field is eleven bytes, filled or not.

The struct itself is 33 bytes in a box and 44 in the party. The first 33 bytes are identical; the party’s extra eleven hold the current HP, status, level and the five computed stats, all of which the game recalculates anyway. Bytes 5 and 6 are the species’ two type bytes, copied from its base stats — the values the Gen 1 type chart is indexed by.

Offset Bytes Field
+0x00 1 Species, as an internal index
+0x03 1 Level, shared copy — what a boxed Pokémon is restored from
+0x08 4 Move ids, one byte each
+0x0E 3 Experience, 24-bit
+0x11 10 Stat experience: HP, Attack, Defense, Speed, Special, 16-bit each
+0x1B 2 DVs, four nibbles: Attack, Defense, Speed, Special
+0x21 1 Level, party only — the one the game trusts

Two details catch people out.

The species byte is not the Pokédex number. Gen 1 stores an internal index, an ordering left over from development that has no relation to the national dex. Mew is 0x15, Bulbasaur is 0x99. You need a lookup table in both directions; the disassembly’s constants/pokemon_constants.asm has it.

There are two level bytes. One sits at +0x03 inside the shared 33 bytes, and the party struct has another at +0x21. The one at +0x21 is the one the game trusts for a Pokémon in your party; the one at +0x03 is what a boxed Pokémon is restored from. Write both.

DVs and stat experience

Determinant values — the Gen 1 ancestor of IVs — are packed as four nibbles across two bytes at +0x1B: Attack and Defense in the first, Speed and Special in the second, high nibble then low. Each runs 0 to 15.

There is no HP DV stored anywhere. It is derived from the least significant bit of the other four:

hp_dv = ((atk & 1) << 3) | ((def & 1) << 2) | ((spd & 1) << 1) | (spc & 1)

Which is why a Pokémon with all four DVs at 15 also has a perfect HP DV, and why you cannot tune HP independently.

Stat experience — what later generations replaced with EVs — is five 16-bit big-endian values at +0x11, in the order HP, Attack, Defense, Speed, Special. Each runs to 65,535 and each contributes floor(ceil(sqrt(ev)) / 4) to the computed stat.

Text

Gen 1 does not use ASCII. Characters are a custom encoding from constants/charmap.asm:

The rest are punctuation and the game’s own glyphs: 0xE1 and 0xE2 are the two halves of the Pk/Mn ligature, 0xEF and 0xF5 are the male and female symbols, and there are two distinct codes that both render as a full stop.

A name field is eleven bytes with a terminator, so ten characters is the practical maximum, and the bytes after the terminator are whatever was there before — do not assume they are zeroed.

Non-English cartridges use the same layout. A French, German or Spanish save has its regions in exactly the same places; only the text differs, because those ROMs assign accented characters to codes the English charmap does not use. Species are stored numerically, so a French save can be read by an English-language tool without any translation at all. Only names come out wrong, and only if they contain an accent.

Yellow is the same too: diffing sram.asm between the Red/Blue and Yellow disassemblies produces nothing. A parser written for Red reads a Yellow save without changes.

What the save does not contain

There is no field identifying which game wrote it. Nothing in a Gen 1 save says Red, Blue or Yellow. This surprises people, and it is worth stating plainly because a good deal of effort can be lost looking for it.

The usual suggestion is to infer the version from the starter, but the starter is not stored as such — only the Pokémon themselves are, and by the time a save is interesting the original starter may have been traded away, evolved, or boxed behind eleven other Pokémon. Inference from the dex flags fares no better: a save early enough to be distinctive is a save with almost nothing in it.

If you need to tell two saves apart, use the trainer’s name together with the 16-bit trainer ID at 0x2605. The ID is fixed when the game is first started and never changes afterwards, which makes the pair a reliable identity for a cartridge across every save it ever produces. That is what this site uses to recognise which cartridge an upload belongs to.

Odds and ends

Money is three bytes of binary-coded decimal at 0x25F3, so 0x00 0x99 0x99 is ₽9,999 and the maximum the game will display is ₽999,999.

Badges are a single bitfield at 0x2602, one bit per badge, in gym order: Boulder, Cascade, Thunder, Rainbow, Soul, Marsh, Volcano, Earth. Counting the set bits gives the badge count the game shows on your trainer card.

The Pokédex is two bitfields of nineteen bytes each — owned at 0x25A3, seen at 0x25B6. Bit n of byte m is dex number m × 8 + n + 1, least significant bit first. Nineteen bytes gives 152 bits for 151 species, so the top bit of the last byte is unused. A Pokémon can be seen without being owned, but the reverse is meaningless, and the game will happily print a diploma for a save where every owned bit is set regardless of how it got that way — which is the whole reason all 151 on one cartridge is possible at all, given that no single version can catch them.

Play time is five bytes at 0x2CED: hours, a flag marking the counter as maxed out, then minutes, seconds and frames.

Your position is the map id at 0x260A and coordinates at 0x260D and 0x260E, counted in sixteen-pixel walking steps from the top-left of the current map rather than in pixels.