# What Must Persist

Lecture ID: lecture_w20_d01
Week 20, Day 1

## Objectives
- Use Pokemon Showdown as a secondary comparison for battle data hooks, Dex/mods, simulator turns, and regression testing without replacing FireRed as the main architecture source.
- Connect week 20 material to the overall course plan.

## Transcript
Good afternoon. Let’s pick up from where week nineteen left us, because today’s question only makes sense if you keep the whole architecture in view. Last week, you designed minimal clone versions of map loading, script execution, party data, battle turns, item use, and save persistence. That was not just a grab bag of systems. It was the moment where the course moved from “how do these systems run?” to “what state must survive when the machine turns off?”

So today I want to slow down on that word: persist. Not “what exists in memory.” Not “what is useful during one callback.” Not “what a menu task happens to be editing this frame.” What must persist?

If I write that on the board, I want you to hear it as an architectural question, not as a serialization question. Serialization is the technique. Persistence is the promise. The promise is: when the player saves in Vermilion City, turns off the Game Boy Advance, comes back tomorrow, and selects Continue, the world is still their world. Their party is their party. The bag reflects what they used, bought, received, and lost. Story flags still block or unlock scripts. Variables still remember partial progression. The player’s map position is restored coherently. And the save loader can distinguish “this is a valid game state” from “this is junk, stale data, or a torn write.”

That is why this week sits in the Persistence, Link, and System Integration module. We are no longer studying one subsystem in isolation. Save data is the crossing point where overworld, scripts, party, inventory, UI, battle aftermath, progression, and hardware storage all have to agree.

Now, because you are advanced at this point in the course, I am not going to define a save file as “a file with data in it” and then spend half an hour on basic structs. You are past that. The more important idea is ownership. Who owns the truth for a piece of state? Which state is temporary? Which state is derived and should be recomputed? Which state is authoritative enough that losing it would corrupt the player’s journey?

In FireRed, the source focus for this week gives us a clean path into that question. The load and save entry flow is in pokefirered/src/load_save.c, especially chunk_0755fbe98f753bb7. The validation support is also in pokefirered/src/load_save.c, chunk_3efebaad2e0f31e0. The continuation flow is in pokefirered/src/load_save.c, chunk_d615017937c86ab6. Then the lower-level save system is in pokefirered/src/save.c, chunk_ab80cd8f4fa87d77, and the sector flow is in pokefirered/src/save.c, chunk_852fbc242f85. The packet also names pokefirered/src/agb_flash.c as the flash persistence boundary, and I want you to treat that file differently from the higher-level save files. load_save.c is asking, “What game state do we initialize, validate, and restore?” save.c is asking, “How do we package and move save blocks through the save medium?” agb_flash.c is closer to, “What does the hardware let us read and write?”

That layering matters.

Let me make a first board sketch. Imagine three horizontal bands. At the top: game ownership. Player, party, bag, flags, variables, map position, PC storage, options, Pokédex, quest log, maybe daycare, depending on the game’s feature set. In the middle: save schema. Save blocks, offsets, checksums, version metadata, sector IDs, signatures, counters. At the bottom: flash operations. Read boundary, write boundary, erase behavior, sector granularity, failure cases.

Now, pause there. If you confuse those bands, your design gets brittle. If a bag menu directly knows about flash sectors, you have leaked hardware concerns into UI. If a flash driver knows what a Gym Badge flag means, you have leaked narrative semantics into storage. FireRed’s architecture, like many GBA-era games, is not “modern clean” in the textbook sense, but it still gives us a very useful separation: gameplay state is organized into save blocks, save routines validate and rotate storage, and flash code performs device-level persistence.

Let’s connect this back to prior weeks. When we studied menus as task-backed state machines, we looked at callback slots, RunTasks, task IDs, and screen-specific state. You saw how a party menu, summary screen, or move relearner flow can have temporary UI state. That temporary state is not automatically save state. The summary screen reads party data, and the move replacement screen mutates party data, but the cursor position inside a confirmation prompt generally does not become part of the permanent world. That distinction is the kind of judgment I expect you to bring into this week’s lab.

Same with item-use dispatch. We already covered field-use versus battle-use items, healing, key item behavior, and script variables and flags as blockers, so I will not re-teach that. But carry the architecture forward. A Potion animation is temporary. The resulting HP change is persistent if the party is saved afterward. A key item use may be temporary as an input event, but the flag it sets or the variable it advances may be persistent. A menu callback is transient. The item quantity in the bag is persistent. The unlock flag for a scripted event is persistent.

That is the mental filter for today.

Now let’s talk about what must persist in a creature RPG specifically. First, the player identity and profile. Name, gender if represented, trainer ID, secret ID, play time, options. Some of these are visible, some support mechanics, some are used by systems like ownership and trading. If you lose the trainer ID, you do not just lose a number; you can break ownership checks, obedience-like mechanics in other games, and trade identity semantics.

Second, location and overworld state. Not just “map ID.” You need the map group and map number, position, facing direction or enough player avatar state to resume plausibly, current warp context, and sometimes last heal location or respawn location. Earlier in the course, when we traced maps from headers to layouts to object events, the map was not merely a background image. It was a registry entry, a layout, an event-object population, scripts, connections, wild encounter tables, metatile behavior, and callbacks. Save state does not need to duplicate all static map data, because that belongs in ROM data tables. But save state must remember where in that static world the player currently is, and which dynamic changes have happened.

Third, event flags and variables. These are the backbone of progression. A rival battle completed flag, an item picked up flag, a door unlocked flag, a story variable that says which phase an NPC should be in. If you think of FireRed’s world as a large script-driven machine, flags and vars are its persistent control registers. They tell static scripts which branch the player’s world is currently on.

Fourth, party data. This is where the course’s creature-model work returns. Species, personality, level, experience, moves, PP, EVs, IVs, status, friendship, held item, met data, nickname, OT information, current HP, and any other game-specific fields that make an individual creature an individual. Static species data is not persisted per creature except by reference; individual creature state is persisted. That is the distinction between “Bulbasaur as a species definition” and “this particular Bulbasaur that the player received, trained, named, damaged, taught moves to, and maybe traded.”

Fifth, inventory. Bag pockets, item IDs, quantities, key item ownership, Poké Balls, TMs and HMs depending on generation rules, berries, money, coins, registered item. We covered item behavior recently, so again, do not re-teach dispatch. Just notice that dispatch depends on persistent inventory being truthful. The game can only decide whether an item can unlock a field behavior if the bag state and flags are coherent.

Sixth, storage systems beyond the immediate party. PC boxes, daycare, mail, Pokédex, battle records, quest log, options, possibly link-related metadata. The daycare is a useful example because the packet points us to pokefirered/src/daycare.c, chunk_119931105f546346, where daycare logic sits among Pokémon data, mail data, storage, event data, scripts, overworld, and other systems. That include neighborhood is not accidental. Daycare state is not just a menu list. It touches party ownership, creature growth, egg generation, map/script interactions, and UI. If a daycare system exists in your clone, its persistent state needs a clear owner.

Let’s do a check for understanding.

Suppose a player opens the party summary screen, moves from page one to page two, looks at a move, cancels, uses a Rare Candy from the bag, learns a new move, replaces Tackle, then saves. Which parts must persist?

Take ten seconds.

The page number of the summary screen? Usually no. The fact that the Pokémon gained a level? Yes. The new move list? Yes. The consumed item quantity? Yes. The exact UI path by which the player got there? Usually no. The party menu task’s internal cursor state? No, unless your game explicitly resumes inside menus, which FireRed is not architected around as a normal save promise. The script flag for a tutorial saying “the player has learned to use the bag,” if one exists? Yes, if it affects future behavior.

That is the level of discrimination I want.

Now, FireRed’s save organization gives us a historically specific version of this. The source focus names save block ownership as the first topic, and that is the right order. Save blocks are not just chunks for convenience. They express an architectural decision: group state by ownership, volatility, and restore semantics. When you see load_save.c entry flow in pokefirered/src/load_save.c, chunk_0755fbe98f753bb7, you should read it as a doorway between runtime initialization and persistent world restoration. When you see validation support in chunk_3efebaad2e0f31e0, you should read it as the guardrail that prevents arbitrary flash contents from becoming trusted game state. And when you move into pokefirered/src/save.c, chunk_ab80cd8f4fa87d77 and chunk_852fbc242f85, you are looking at the packaging and sector-level flow that makes the promise robust enough for real hardware.

Let’s linger on validation. In many student projects, the save loader is just “open file, deserialize JSON, assign globals.” That is fine for a prototype, but it is not enough for a creature RPG architecture course. A real save system needs to ask: Is there a save? Does it have the expected signature? Are its sectors complete? Do its checksums match? Which copy is newest? Is the version compatible? If one sector is bad, can we recover from an alternate copy? If the save is absent or invalid, how do we initialize a new game state without leaving globals half-old and half-new?

The reason is simple: persistence is a failure boundary. Normal RAM state can be trusted because your own code constructed it this frame or this session. Save state is different. It may be old. It may be partially written. It may belong to an earlier version of the schema. It may be corrupted. It may be uninitialized flash. So the load path must be more suspicious than normal gameplay code.

Here is a concrete example. Imagine your clone has three save blocks. SaveBlock1 contains world state: flags, variables, map position, object event persistence, maybe some game stats. SaveBlock2 contains player profile and party. SaveBlock3 contains storage boxes and checksum metadata. That exact grouping is not my claim about FireRed’s full structure; I’m using it as a simplified lab schema. Now imagine the player saves, and the program writes block one successfully, starts writing block two, and power is lost. On next boot, what should happen?

A naive loader might read block one from the newest save attempt and block two from the older save attempt. That may produce a world where a script thinks the player received a starter, but the party does not contain it. Or a bag quantity says the player spent money and bought items, but the money value is from before the purchase. Or a map position says the player is in a post-event room, but flags say the event has not happened. You have created an impossible state.

So a sector-based save system needs a notion of coherent save set. Checksums per sector help detect local damage. Save indices or counters help identify which full save attempt is newest. Validation must assemble a complete candidate and reject incoherent mixtures. This is why the source focus separates load/save validation support in pokefirered/src/load_save.c, chunk_3efebaad2e0f31e0, from sector flow in pokefirered/src/save.c, chunk_852fbc242f85. The higher layer cares about whether the loaded game is valid. The lower layer cares about reading and writing the sectors that make that possible.

Now let me bring Pokémon Showdown in as a secondary comparison, carefully. Showdown is not our replacement architecture. It is not a GBA overworld engine. It does not answer how FireRed stores save blocks in flash. But it is useful as a contrast because Showdown is extremely explicit about battle data hooks, Dex/mods, simulator turns, and regression testing.

Look at the packet’s example from pokemon-showdown/data/abilities.ts, chunk_da22cf969179258c. The excerpt describes ability ratings and ability data as structured battle knowledge. And the packet’s pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f, shows battle calculation logic with hooks like damage callbacks, immunity checks, fixed damage handling, level-based damage, and other move-resolution behavior. That is a very different persistence problem from FireRed’s save blocks. Showdown’s battle simulator needs deterministic, inspectable, testable turn resolution. Its Dex and mods system lets data and behavior vary across generations or formats. Regression tests can assert that a battle interaction still resolves correctly after code changes.

But what persists there? In a server simulator, a battle log, replay, or serialized battle state may matter. In a cartridge RPG, the durable promise is broader and more embodied: player world, party, bag, flags, variables, location, storage. FireRed’s battle engine may use many temporary battle structures during a fight. Those structures do not necessarily become save data. After battle, the persistent consequences are things like gained experience, changed HP, status, consumed items, money changes, trainer defeated flags, Pokédex seen/caught updates, and story variables. The simulator turn itself is a transient process unless the game supports mid-battle save, which FireRed generally does not promise as the normal architecture.

So Showdown helps us ask a sharper question: when is data a rule, when is it a hook, when is it runtime state, and when is it persistent player state?

Let me put that on the board.

Species base stats are rules. Move effect callbacks are rules. Ability behavior is rules. A turn queue is runtime state. A damage roll during one attack is runtime state. The fact that the player’s Charmeleon is level 28 with specific moves and current HP is persistent player state. The fact that a trainer on Route Whatever has already been defeated is persistent world state. The test fixture that proves a move interaction works is regression evidence, not game save data.

That comparison is especially important because modern students often see Showdown’s clean data directories and want to turn a cartridge RPG into only a data-driven simulator. Resist that simplification. FireRed is a whole RPG runtime. It has maps, scripts, callbacks, tasks, link boundaries, save initialization, flash constraints, UI flows, and data tables. Showdown is valuable, but in this course it is a secondary comparison for particular ideas: battle data hooks, Dex/mods, simulator turns, regression testing. FireRed remains the main architecture source.

Now, let’s walk through the first half of the save lifecycle at a high level.

The game starts. Earlier in the course we studied AgbMain and main callback slots. That startup shell eventually needs to decide whether it is initializing a new game path, continuing from save, or entering some special mode. The load-save entry code in pokefirered/src/load_save.c, chunk_0755fbe98f753bb7, belongs to that transitional zone. It is not “gameplay” in the sense of walking around a map, but it determines what gameplay state will exist when the player does walk around the map.

A load path should usually do several things in a disciplined order. First, clear or initialize runtime memory regions so old RAM garbage cannot masquerade as valid state. Second, ask the save system whether valid persistent data exists. Third, validate the data before trusting it. Fourth, copy or map the restored save blocks into the runtime globals expected by the rest of the engine. Fifth, initialize derived runtime systems from the restored state: map objects, callbacks, palettes, script context, party menu assumptions, and so on. Sixth, route the player into the correct continuation flow.

Notice the distinction between saved state and derived state. If the player saved standing on a map, you persist the location and progression. You do not persist every decompressed tile buffer and every OAM sprite entry as permanent truth. Those can be rebuilt. This is one of the most common mistakes in save design: persisting caches. If you persist too little, you lose the player’s state. If you persist too much, you freeze implementation details into your save schema and make future changes painful.

Let’s use the Pokédex as an example. The static species names, base stats, cries, sprite pointers, and learnsets belong in ROM or data files. The player’s seen and caught flags are persistent. The current scroll position in the Pokédex UI is probably temporary. A cached alphabetical list can be rebuilt. If your save file stores the cached list, then changing sorting code or adding forms can create strange compatibility problems. Again: persist truth, not convenience.

The packet gives us a useful reminder of cross-module interactions in pokefirered/src/pokemon_summary_screen.c, chunk_e8e17a42bbf17d46. That file includes Pokémon data, summary screen headers, help system, tasks, menus, link, overworld, item constants, battle main, moves, party menu, and battle constants. A summary screen is a reader and sometimes a route into mutation flows, but it is not the owner of all the state it displays. When the player views a Pokémon, the screen reads persistent party data into a UI presentation. The party data must persist; the presentation state usually must not.

Let me ask another check for understanding. Why would it be dangerous to persist “current battle damage calculation locals” after every turn, but not persist “current HP after battle”?

Think it through.

The battle damage calculation locals are meaningful only inside a specific execution context. They depend on the current move, source, target, random number sequence, side conditions, temporary modifiers, and the battle engine’s internal phase. Persisting them as normal RPG save state would tie the save schema to a volatile procedure. Current HP, by contrast, is part of the creature’s durable condition after the battle system has resolved its consequences. So we persist the consequence, not the internal scratchwork.

That same principle appears if we compare to Showdown. In pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f, the excerpt shows a damage path that checks immunities, OHKO moves, damage callbacks, level damage, and fixed damage. Those are excellent battle hooks and simulator behaviors. They are exactly the sort of thing you regression test. But if you were building a FireRed-like cartridge RPG, you would not store “we were halfway through damageCallback” in the normal save block. You would store the stable state after the battle or after a supported save point.

Now, one more architectural connection before we move deeper into FireRed’s save blocks. Earlier, we repeatedly used the trace “data table to runtime globals to callback or task or script to visible behavior.” Today we add “to persisted state and back again.” The loop becomes: static data defines the world; runtime systems instantiate and mutate the current session; save blocks capture the durable subset; load validation restores that subset; initialization rebuilds runtime systems; callbacks, tasks, scripts, and maps continue producing visible behavior.

That loop is the week twenty version of the whole course.

In week one, when we decomposed the RPG into runtime shell, overworld maps, event scripting, creature data, battle, inventory, UI, save, and link systems, save was just one box in the diagram. By week twenty, save is no longer one box. It is the integration test for the boxes. If your party system has no stable representation, saving exposes that. If your flags are scattered in UI code, saving exposes that. If your map loader cannot reconstruct a map from a small location record and static data, saving exposes that. If your battle rewards mutate five systems without a consistent commit point, saving exposes that.

This is why the lab asks you to design a save schema with save blocks for player, party, bag, flags, variables, location, and checksum or version metadata, then simulate load validation and recovery. That lab is not about copying FireRed’s exact layout byte-for-byte. It is about proving that you understand the architecture well enough to decide what belongs in durable state, how it is grouped, how it is validated, and how the runtime is reconstructed.

Let’s now begin classifying state more rigorously, because this will save you trouble in the workshop.

I want you to imagine every piece of data in the game has to pass through one of five doors.

Door one: static rule data. Species definitions, move definitions, item definitions, map layouts, script bytecode, base stats, type charts, ability rules. These are not saved in the player’s save file because they ship with the game. Showdown’s Dex and mods are a modern, highly explicit version of this category. Its data files and modded scripts can redefine rules by generation or format. FireRed’s equivalent is more ROM-and-C-source oriented, but the category is the same: rule data is not per-player persistence.

Door two: persistent player/world state. Party, bag, money, flags, variables, location, storage, Pokédex progress, options, trainer identity. This is what we save.

Door three: derived runtime state. Decompressed graphics, loaded map block buffers, object sprites currently instantiated from saved/map state, menu windows, text buffers, task lists, callback pointers. Some of these are critical while the game is running, but they can be rebuilt after load.

Door four: temporary transactional state. A battle turn in progress, a menu confirmation branch, a script command halfway through execution, a fade transition, a cursor movement, a pending animation. This usually should not persist unless the game explicitly supports saving inside that transaction.

Door five: hardware or storage metadata. Sector IDs, checksums, save counters, signatures, flash chip behavior, read/write boundaries. This is not player-world meaning, but it is essential to keeping player-world meaning safe.

And I want you to notice something subtle. Door five metadata must persist, but it is not gameplay state. A checksum is not part of the story world. A save counter is not part of the player fantasy. But without it, the game cannot safely recover the story world. That is why save systems contain both semantic state and structural metadata.

We will continue from there into FireRed’s save block ownership and the exact recovery logic, but keep this classification on your page, because for the rest of today I am going to keep asking: which door does this data belong behind?

So, let’s keep that question alive: which door does this data belong behind? And now I want to shift from classification to ownership. Because once you say, “this must persist,” the next architectural question is not immediately, “how do I write it to flash?” The next question is, “who owns it?”

I’m going to write that on the board: persistence follows ownership. Not convenience. Not whatever file happened to need the value last. Ownership.

This is where FireRed is very instructive, because it does not treat the save file as one giant undifferentiated memory dump. It has save blocks. Those save blocks are not just storage containers; they are ownership boundaries. They say, in effect, “these fields belong to the long-lived player-world state,” “these fields belong to party or item state,” “these fields belong to event progression,” and so on. The exact entry and continuation flow we are focusing on this week is in `pokefirered/src/load_save.c`, especially `chunk_0755fbe98f753bb7` for the load/save entry flow, `chunk_3efebaad2e0f31e0` for validation support, and `chunk_d615017937c86ab6` for continuation logic. The complementary save-sector machinery is in `pokefirered/src/save.c`, with `chunk_ab80cd8f4fa87d77` for the broader save system and `chunk_852fbc242f85cc74` for sector flow.

Now, pause there. If you are building a clone, you might be tempted to say, “Fine, I’ll just serialize the whole game state object.” That can work in a small prototype. But in an architecture like FireRed’s, that phrasing hides too much. What is “the whole game state”? Does it include the current text box cursor? Does it include the currently selected party menu row? Does it include a temporary battle animation variable? Does it include the last random value consumed during a wild encounter check? Does it include the temporary callback that will run after a fade completes? Some of those are real state. Some are runtime scaffolding. Some are derivable. Some are dangerous to persist because they point into a moment that cannot safely be resumed after reset.

Here is a concrete example. Imagine the player is standing outside a gym, has six Pokémon in party, three badges, a bag with Potions and a Bicycle, and has already cleared a particular story flag. Those things must survive power-off. The player’s map group and map number, position, facing direction, party structs, inventory quantities, badges, flags, and variables belong behind our first four doors. But if the player opened the bag menu and the cursor is on slot four, do we need that exact menu cursor after reboot? Usually no. We need the bag contents, not the transient UI posture. That distinction is why last week’s menu-task state machines matter as prior foundation. We are not re-teaching menu callbacks today, but remember the principle: a menu task is often a temporary navigation machine wrapped around persistent data. The persistent thing is the item quantity or learned move or party mutation. The task-local cursor is usually disposable.

Let me check understanding here. If I ask, “Should the fact that the player owns HM01 Cut persist?” yes. If I ask, “Should the exact task ID of the party menu task persist?” no. If I ask, “Should the fact that a script flag says a tree has been cut or a door has been unlocked persist?” yes, if that flag represents world progression. If I ask, “Should the currently executing C callback pointer persist?” almost certainly no. Good. That is the level of discrimination I want.

Now, FireRed’s save blocks also teach a second principle: loading is not merely reading. Loading is reconstruction. That may sound like a small vocabulary point, but it is a major architectural point. When the cartridge starts, the game does not simply pour bytes from flash and continue as though nothing happened. It validates. It identifies usable sectors. It checks metadata. It initializes globals. It restores enough semantic state to rebuild the world. Then the runtime systems resume through normal callbacks, scripts, maps, and tasks.

On the board, I’m going to draw two columns. Left column: saved bytes. Right column: live runtime. Between them I’m drawing an arrow labeled “load initialization.” That arrow is not free. It contains policy.

For example, if a save sector has a checksum mismatch, what should happen? If two save copies exist, which one is newer? If the player’s party block is readable but the event flags block is not, is partial recovery allowed, or is the save rejected? If the flash read operation fails at the hardware boundary, what user-facing state should the game enter? These are not serialization trivia. These are game design decisions made concrete in code.

That is why `pokefirered/src/save.c` matters so much this week. The sector flow in `chunk_852fbc242f85cc74` is not just a low-level implementation detail. It embodies the idea that persistent state is segmented, labeled, checked, and recovered. And the flash layer, named in this week’s packet as `pokefirered/src/agb_flash.c`, reminds us that beneath the pleasant phrase “save the game” there is a physical storage device with boundaries. You do not write abstract intentions to flash. You write bytes into constrained regions, and you have to respect erase and write behavior.

Now, let’s connect this back to the course arc. In week 1, we said FireRed is not one big loop with gameplay sprinkled inside. It is a stack of cooperating state machines and data tables. We traced runtime shell, overworld, scripts, creature data, battle, inventory, UI, save, and link. By week 19, you had already designed minimal clone versions of map loading, script execution, party data, battle turns, item use, and save persistence. So at this point, the expected sophistication is higher. I do not want you merely saying, “Save the player data.” I want you saying, “The player profile belongs to one ownership region; party data belongs to another; event flags and variables are semantic progression state; transient menu and callback state should be reconstructed or discarded; storage metadata is required for validation but is not part of the world model.”

That is the difference between a feature checklist and an architecture.

Let’s bring in Pokémon Showdown briefly, because it gives us a useful contrast, but not a replacement. FireRed remains the main architecture source for this course because we are studying a complete cartridge RPG: overworld, scripts, flash persistence, party, bag, flags, callbacks, maps, and hardware constraints. Pokémon Showdown is useful when we compare battle data hooks, Dex and mod layering, simulator turns, and regression testing. It is not useful as a one-to-one model of saving a Game Boy Advance overworld state, because that is not its primary problem.

In Showdown, a lot of battle behavior is expressed as data plus hooks. For example, the packet points us to `pokemon-showdown/data/mods/gen2stadium2/scripts.ts`, `chunk_3480e574205d5d0f`, where damage computation can branch through move damage callbacks, fixed damage, level-based damage, immunity checks, and related mechanics. That is an excellent secondary comparison for battle extensibility. A move or ability can carry behavior hooks that the simulator invokes during a deterministic battle turn. Similarly, `pokemon-showdown/data/abilities.ts`, `chunk_da22cf969179258c`, shows ability records as structured battle data with ratings and effect-oriented metadata. That kind of architecture is highly testable because the simulator can run turns under controlled inputs.

But notice the persistence lesson. In Showdown, a battle replay or test fixture might preserve a sequence of choices, teams, formats, and expected outcomes. The simulator state matters, but it is usually in service of deterministic simulation and regression testing. In FireRed, the save file must preserve a player’s lived campaign over weeks or months. That includes where the avatar is, which scripts have fired, what items were consumed, which Pokémon have what stats and moves, what flags and variables encode progression, and whether the save data itself is valid. So Showdown helps us ask, “Where are the hooks? How do mods layer rules? How do deterministic turns get tested?” FireRed asks, “What world must exist again after power loss?”

Here is a useful way to compare them. FireRed persistence is player-continuity persistence. Showdown simulation is rules-continuity and test-continuity persistence. Both care about correctness. They just protect different meanings.

Let’s make that concrete. Suppose a Charizard uses a move in a battle. In Showdown, the important architectural path might be: Dex lookup, format or mod adjustment, move hook, simulator event ordering, test expectation. If a regression test fails, you want to know whether the damage callback or immunity check changed behavior. In FireRed, if the player saves after the battle, the important persistence path is different: did the party Pokémon’s HP change, did PP change, did experience or level change, did an item get consumed, did a story flag get set after the trainer battle, did the map state update, and did the save sector commit safely? Battle logic produces mutations, but persistence must capture the durable mutations, not the whole temporary battle engine.

That distinction is subtle and exam-worthy. The battle engine may contain many variables during the turn. Turn order, selected move, temporary stat stage calculations, animation state, text windows, AI scratch memory. Most of that should not become save data. But the resulting party changes, item changes, money changes, trainer defeated flag, and location state may need to persist. So when you design your lab schema this week, do not create a giant “battle_runtime” save block just because battles are important. Ask what battle outcomes cross back into the campaign state.

Now I want to return to load validation. The phrase sounds dry, but it is where your save system earns trust. In a cartridge RPG, a corrupted save is catastrophic. And even in a modern clone, a bad save migration or invalid JSON field can ruin a player’s campaign. Validation is the line between “we found stored bytes” and “we accept this as game state.”

In FireRed terms, validation support in `pokefirered/src/load_save.c`, `chunk_3efebaad2e0f31e0`, belongs to door five from our classification: structural metadata and recovery. Checksums, sector IDs, counters, and signatures are not story facts. They are the scaffolding that lets the story facts survive. Then, once accepted, the continuation logic in `chunk_d615017937c86ab6` participates in restoring the runtime path. Again, loading is reconstruction.

Let me give you a small clone-design example. Suppose your save schema has blocks named player, party, bag, flags, vars, location, and metadata. The player block has name, trainer ID, gender, money, badges. The party block has species, level, experience, moves, PP, friendship, status, held item. The bag block has pockets and item quantities. The flags block has bitsets for events. The vars block has script variables. The location block has map ID, warp, x, y, facing. The metadata block has version, checksum, timestamp or save index, and maybe a schema identifier.

Now imagine the party checksum passes, but the flags checksum fails. What do you do? Advanced answer: you do not answer from vibes. You define the recovery policy. Maybe the whole save is invalid because flags are required to avoid impossible progression. Maybe you keep two complete save slots and choose the newest fully valid one. Maybe you allow partial recovery only in a debug build. FireRed’s sector-based approach pushes you to think in these terms, because sectors can be validated and selected instead of blindly trusted.

Check for understanding: why is “just load whatever parses” not enough? Because parsing only proves that bytes can be shaped into structures. It does not prove those structures are coherent, current, complete, or safe. A bag quantity of 999 may parse. A map ID that points nowhere may parse. A party count of six with only four valid party entries may parse. A script flag combination that says the player both has and has not completed a required event may parse. Validation has to include structural integrity and, where necessary, semantic plausibility.

Now, you do not need to overdo semantic validation. Be careful here. A save system should not reimplement the entire game as a validator. But it should protect invariants that would crash or permanently trap the player. Valid map identifiers. Valid party count. Item IDs in range. Checksums correct. Version recognized. Required blocks present. Sector metadata coherent. That level of validation is practical.

Let’s connect this to earlier item lectures without reopening those lectures. When we studied item-use dispatch, field-use versus battle-use items, and key items as scripted unlock inputs, we cared about how an item action found the correct behavior path. Today we care about what remains after that behavior path is done. If the player uses a Potion, the dispatch path is not persisted. The resulting HP change and item quantity are. If the player uses a key item that sets a script flag, the temporary field-use callback is not the durable thing. The flag and possibly changed map state are. If the player views a summary screen, the summary screen reads party data, but the summary screen UI state is not what saves the Pokémon. The party struct is.

That is why prior systems now converge. Runtime shell gives us callbacks. Tasks give us per-frame UI and interaction state. Scripts give us progression mutations. Creature and item systems give us structured semantic data. Save systems decide what crosses the power boundary.

I’m going to put another phrase on the board: power boundary. This is one of the most useful phrases for today. A save file is the state that crosses the power boundary. If the console turns off, what must cross from before power-off to after power-on? Not what was convenient to have in RAM. Not what was on screen. What must cross?

Let’s test that with a few cases.

The player is halfway through a text box. Must that exact text box cross the power boundary? Usually no, unless the game explicitly supports suspend-anywhere with mid-dialogue resume. FireRed-style saving is more controlled.

The player has defeated a trainer. Must that cross? Yes, because otherwise the world reverts incorrectly.

The player has selected “Fight” in a battle menu but the turn has not resolved. Must that cross? In FireRed’s normal model, no, because saving is not generally inside that transaction.

The player’s Pokémon gained a level after battle. Must that cross if the game allows saving after the battle? Yes.

The flash sector checksum? Must that cross? Yes, structurally, although it is not gameplay meaning.

The currently installed callback? No, because startup will establish callbacks again as part of load initialization.

That last point brings us back to the runtime shell. Earlier in the course, we looked at `pokefirered/src/main.c`, including `chunk_b35676322b169f85` for `AgbMain` responsibilities and `chunk_6cfd90f6d0136799` for `SetMainCallback2`. We also looked at task allocation and per-frame dispatch in `pokefirered/src/task.c`, including `chunk_cb0bb91a147bfb59` for `CreateTask` and `chunk_3ffebc0341225936` for `RunTasks`. Those are live runtime mechanisms. Save data should not be a fossilized callback stack. It should be enough semantic state for those mechanisms to rebuild the appropriate live state.

Now, there are exceptions in some games. A modern emulator save state, for example, saves almost everything: CPU registers, memory, graphics state, audio state. That is a different product. It is a machine snapshot, not a game-designed save. FireRed’s save system is game semantic persistence. Keep that distinction sharp. Your lab is designing a game save schema, not an emulator save state.

Let’s bring the flash boundary into view. The source packet names `pokefirered/src/agb_flash.c` as the flash persistence file for this week. Even without turning today into a hardware lecture, the architectural lesson is clear: storage devices impose shape. There are read/write boundaries, sector boundaries, erase behavior, and failure cases. In a desktop clone, the equivalent might be filesystem writes, temp files, atomic renames, JSON or binary formats, and backups. In a server-backed game, it might be database transactions, schema migrations, and conflict resolution. The FireRed version is flash sectors and validation metadata. Different storage, same principle: durable writes need protocol.

So when you write “SaveGame()” in your design, I want you to feel slightly suspicious of that name. It is too smooth. A better mental expansion is: gather owned persistent state, serialize by block, attach structural metadata, write within storage constraints, verify or prepare recovery, and on load choose a valid coherent version, then initialize runtime systems from it.

That is the architecture hiding behind a menu option that says “Save.”

Now, before we move forward, I want to do a short recap. We have five categories from the previous segment: player identity and campaign progress; creature, party, and collection state; inventory and resources; world progression and script state; and storage metadata. We have added ownership: save blocks are boundaries that say who is responsible for durable meaning. We have added reconstruction: loading is not reading; loading is validation plus runtime initialization. We have added the power boundary: persist only what must survive power-off, not every live variable. And we have compared Showdown carefully: useful for battle hooks, Dex and mods, simulator turns, and regression testing, but not a substitute for FireRed’s cartridge RPG persistence model.

Now let’s push one level deeper into ownership conflicts, because this is where advanced designs often go wrong. Suppose a Pokémon is holding an item. Does that item belong to the party block or the bag block? The answer depends on the model, but the invariant must be clear. In FireRed-like games, a held item is part of the Pokémon’s data while held. It is not simultaneously in the bag quantity. If your save schema stores party held items and bag quantities separately, you must avoid double-counting. Persistence will preserve your model, including its mistakes.

Or consider a story flag that unlocks a map object. Does the map object persist, or does the flag persist? Usually the flag persists, and the map object is reconstructed when the map loads. This is an important pattern. Persist causes, not derived effects, unless recomputation is impossible or too expensive. If a cutscene sets a flag saying an NPC has moved away, then the map loading logic can hide or reposition the NPC. You do not necessarily need to persist every spawned object’s current visibility as independent truth. But if an object has genuinely dynamic state that is not derivable from flags, then you may need a world-state block for it.

This connects directly to the overworld and event scripting architecture we studied earlier. The script system mutates flags and variables. The map system reads those flags and variables to decide what the player sees and can do. Persistence belongs at the level of durable script facts, not at every momentary rendering consequence.

Here’s a check: if a door opens because variable 0x4050 equals 3, what should the save file store? The variable. Not necessarily “door_open = true,” unless that is your canonical state. If you store both, you now have two truths that can disagree. And when persistent truths disagree, loading becomes a trial.

Now, for the lab, I want you to simulate this explicitly. You are not just drawing a schema. You are going to corrupt or invalidate pieces and see what the loader does. Give your schema a version. Give each block a checksum. Give the whole save a save index or generation counter. Then test these cases: valid save, old version, bad checksum in one block, invalid location, invalid party count, and mismatched metadata. The point is not to build FireRed exactly. The point is to demonstrate that you understand why FireRed’s `load_save.c` and `save.c` are organized around entry flow, validation support, continuation, save system, and sector flow.

And when you write your explanation, do not say, “The checksum makes it safe.” A checksum does not make data safe. A checksum detects certain corruption. The recovery policy is what decides how the program behaves after detection. That distinction matters.

Let me say that again because it is one of those sentences that separates a serious engineer from a casual one: detection is not recovery. A checksum detects. A backup slot, sector selection rule, default initialization path, or user-facing error screen participates in recovery.

Now, tying back to Pokémon Showdown one more time, regression testing plays a related role. A Showdown battle test does not prevent a mechanic from being wrong. It detects that behavior changed relative to expectation. Then the maintainers decide whether the change is a bug fix or a regression. In the same way, FireRed-style save validation detects whether stored data can be trusted. Then load policy decides whether to accept it, fall back, initialize, or reject. Detection and policy are separate layers in both worlds.

That comparison should help you think more cleanly. Showdown’s Dex and mod system allows battle rules to vary by generation or format. FireRed’s save system allows campaign state to survive across hardware sessions. Both require boundaries. In Showdown, boundaries are often ruleset and data-layer boundaries. In FireRed, the boundaries this week are save blocks, sectors, and ownership domains.

As we continue, keep your page organized around the question from the end of the last segment: which door does this data belong behind? But add two more questions under it. Who owns this data? And can the live runtime reconstruct this from something more canonical?

Those three questions will carry us into the next part of the lecture, where we will look more closely at restoration: not just what gets written, but how world, party, bag, flags, and variables become a playable game again after the load path accepts the save.

Now, let us take those three questions and make them operational. If I ask, “which door does this data belong behind,” I am asking for layout. If I ask, “who owns this data,” I am asking for authority. And if I ask, “can the live runtime reconstruct this from something more canonical,” I am asking whether persistence is actually necessary, or whether persistence would just duplicate a value that should be derived.

So, on the board, I am going to draw three columns. First column: persistent source of truth. Second column: runtime cache or projection. Third column: disposable frame state. The mistake that beginners make, and frankly the mistake that many intermediate engineers make when they first design save systems, is that they store everything that seems important in the moment. The advanced version of the problem is subtler. You are not asking, “is this value important?” You are asking, “is this value the authoritative continuing identity of the campaign?”

Take the player’s location. That is persistent source of truth. If the player saved in a Pokemon Center, after power-off and reboot, the game needs to place the avatar in the correct map, at the correct coordinates, with the correct facing direction, or at least with a valid continuation rule that resolves to a safe location. The current decompressed metatile graphics for that map, however, are not persistent source of truth. They are runtime projection. You can rebuild them from the map header, map layout, tileset resources, and location. We spent earlier weeks tracing map header fields and layout registry references, so this should feel familiar. The save should not be a photograph of VRAM. The save should contain the minimum campaign state needed to reconstruct the world through the normal loading path.

Now compare that with party data. A party Pokemon is not just a visual object. It is identity-bearing state. Species, experience, moves, PP, held item, friendship, status, current HP, personality-related fields, and other per-mon fields are part of what the player owns. The summary screen that we discussed recently reads party data as a consumer; it is not the owner. That distinction matters. In pokefirered/src/pokemon_summary_screen.c, chunk_e8e17a42bbf17d46, you can see from the include surface how the summary screen sits at an intersection of pokemon.h, item constants, move constants, party menu, battle headers, and UI helpers. That source file is a reader and presenter of persistent creature state. It needs the party state to be restored correctly, but it should not define what persistence means.

Pause there. Check yourself. If a summary screen cursor is on the third Pokemon when the player powers off, do we need to persist that cursor? Usually, no. That is disposable UI state. If the player’s third Pokemon has one HP and poison, do we need to persist that? Yes. That is campaign state. Same screen, same visible moment, different persistence category.

This is why save block ownership is more than a C struct layout problem. It is an architectural rule about which system is allowed to decide continuity. The bag system owns item quantities and pockets. The event system owns flags and variables. The party system owns creature identity and creature mutable state. The overworld owns the continuation point, but even there, we have to be careful, because the overworld also has many transient values. A camera scroll offset during a fade is not the same kind of data as the map group and map number. One is a momentary presentation coordinate; the other is part of re-entering the campaign.

Now, in FireRed, the load and save path gives us a very concrete version of this abstraction. The week’s source focus in pokefirered/src/load_save.c, especially chunk_0755fbe98f753bb7 for load and save entry flow, chunk_3efebaad2e0f31e0 for validation support, and chunk_d615017937c86ab6 for continuation, is where you should read the system as an entry sequence, not merely as serialization utilities. The game is deciding whether the flash contents represent a coherent prior campaign. It is not simply copying bytes and hoping.

And this is where sector validation enters. Let me put a phrase on the board: “a save is accepted, not assumed.” That sounds obvious, but it changes the design. Flash storage has boundaries. It has write and read behavior that differs from ordinary RAM. In the source packet, pokefirered/src/agb_flash.c is identified as the flash persistence file, and for our purposes today the important architectural point is that persistence crosses a hardware boundary. The save code cannot behave as if it is mutating a normal heap object. FireRed’s save path must package state into sectors, write it through the flash interface, and later read and validate those sectors before exposing the restored data to the rest of the game.

So what must validation prove? Not that every story condition is pleasant. Not that the player is in a good strategic position. Validation needs to prove enough structural trust to let the runtime interpret the bytes. Is this sector one of ours? Is the save index or counter coherent? Is the checksum acceptable? Are the expected pieces present? Does the version or signature match what this game knows how to load? The precise implementation details live in pokefirered/src/save.c, and the research packet points us to chunk_ab80cd8f4fa87d77 for the save system and chunk_852fbc242f85 for the save sector flow. When you read those chunks, do not read them only as low-level storage code. Read them as a defensive boundary between raw flash and live game globals.

Now let us make this concrete. Suppose a student in workshop designs a save schema with a player block, party block, bag block, flags block, variables block, location block, and metadata block. Good start. But now I ask: what happens if the bag block validates and the party block does not? What happens if the metadata claims one version but the sector checksum fails? What happens if the location points to a map that no longer exists in your modified build? This is where an advanced design stops being a list of fields and becomes a recovery policy.

For FireRed as shipped, we are studying a fixed program and a fixed data universe, so version migration is not the dominant concern in the same way it would be for a live service game. But in your clone architecture, and especially in your lab this week, you should include version metadata because it forces you to name the compatibility boundary. If you change the party struct later, will older saves load? If you add a new pocket to the bag, what is the default value? If you reorder flags, can you still interpret old progression? These are not afterthoughts. They are consequences of making campaign state durable.

Now let us bring Pokemon Showdown back in as a secondary comparison, carefully. Showdown is not our model for cartridge persistence. It is useful because it gives us another mature architecture where boundaries matter. In Showdown, battle behavior is often expressed through data hooks and simulator callbacks. For example, pokemon-showdown/data/abilities.ts, chunk_da22cf969179258c, shows ability data annotated not only with behavior but with metagame-facing ratings and classifications. In pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f, battle damage logic is routed through move properties and callbacks such as fixed damage, level damage, damage callbacks, immunity checks, and base power handling. That is a different persistence problem, but the same boundary discipline appears. Some data is declarative. Some behavior is hooked. Some state belongs to an active battle turn and should not leak into global identity.

Imagine saving Showdown’s active simulator turn in the middle of resolving damage. That is not normally the goal. A simulator test may reconstruct a battle from teams, ruleset, choices, and deterministic assumptions. The durable artifact for regression testing is often the input sequence and expected outcome, not a raw heap dump of every temporary variable halfway through a move callback. That is the comparison I want you to keep. FireRed needs to persist campaign identity across power cycles. Showdown’s testable battle architecture often persists or records battle conditions and choices so a turn can be replayed or checked. Different domain, same question: can the runtime reconstruct this from something more canonical?

Let us check understanding with a quick classification. I will say a value; you decide whether it is persistent source of truth, runtime projection, or disposable frame state. The number of Potions in the player’s bag. Persistent source of truth. The currently highlighted item row in the bag menu. Usually disposable UI state. The fact that a particular NPC gift has already been received. Persistent, usually a flag. The text window tilemap currently drawn on screen. Runtime projection, maybe disposable depending on how the UI path rebuilds it, but not campaign identity. The player’s current party order. Persistent. The battle animation frame index during Tackle. Disposable. The fact that the player is in an active battle? Now that one is interesting. In FireRed’s normal save model, saving is constrained; you do not generally persist arbitrary mid-battle execution. So the architecture avoids needing to serialize the battle engine at every possible instruction boundary. That design choice dramatically reduces what “must persist.”

And this is one of the deeper lessons of the week. Persistence is shaped not only by what the game contains, but by where the game permits saving. If you allow saving anywhere, including during a move animation, during a script command, during a link negotiation, or halfway through a trade, your persistence model becomes far more difficult. You either need transactional snapshots of many systems, or you need a continuation mechanism that can unwind to a safe point. FireRed’s architecture narrows the problem by controlling save entry flow. That is why pokefirered/src/load_save.c chunk_0755fbe98f753bb7 matters as an entry flow source, and not just as a utility file. The save system is not independent of callbacks, tasks, scripts, and overworld constraints. It is integrated with when the runtime considers itself safe to freeze campaign state.

This connects directly back to our earlier work on menu callbacks and task states. When we studied menus as task-backed state machines, we cared about confirm and cancel branches, summary reads, move relearner states, and other visible interactions. Today, we reuse that knowledge but ask a different question. If a menu task exists, does that task own durable state? Usually no. The task coordinates interaction. It may cause a durable mutation, such as replacing a move, using an item, or setting a flag through a script consequence. But once that mutation has occurred, the durable owner is the party data, bag data, or event data, not the menu task itself.

So if your save schema includes a giant “current task list” block, I want you to be suspicious. There are engines where task serialization is appropriate, especially in simulation tools, editors, or games with full suspend states. But for a FireRed-like creature RPG, the cleaner design is usually to save campaign state at stable boundaries and rebuild tasks from context. The player loads the game; the game initializes the field; the correct map loads; scripts and objects become available; the menu task list starts empty or in a known initial state. That is not a limitation. That is a design simplification.

Let me give you a failure example. Suppose your clone stores the current visible bag menu state as part of the save. The player saves with the cursor over Rare Candy. Later, you patch the item list and insert a new item earlier in the pocket. On load, the cursor index now points to a different item. If you treat that cursor as meaningful, you may produce a strange UI bug. If instead the bag quantities are persistent and the menu cursor is disposable, the menu can choose a safe default when opened. This is a small example, but the same principle scales to maps, scripts, battles, and link state.

Now, we also have to talk about flags and variables. Flags are deceptively simple. A flag is a bit, so students think flags are easy to persist. But ownership and naming are the hard parts. A flag often encodes a story fact: this trainer was defeated, this door was unlocked, this item ball was collected, this tutorial was completed. Variables may encode richer progression state: script counters, selected options, temporary or semi-permanent event values. In prior weeks, when we discussed items as scripted unlock inputs, we talked about how inventory state and flags combine to gate access. I am not going to reteach that dispatch path now. Instead, notice the persistence implication. If a script checks a flag to decide whether an NPC gives an item, then that flag is part of the world’s memory. If it is lost, the story rewinds incorrectly. If it is corrupted, the player may be blocked or rewarded twice.

So, in the lab, when you design your flags block, do not just write “array of booleans.” Ask what namespace the flags belong to. Ask how many are reserved. Ask whether temporary flags are saved. Ask whether script-local scratch variables are saved. In a cartridge-style RPG, not every event variable deserves equal persistence. Some are durable campaign facts. Some are temporary execution aids. If you collapse those categories, you either lose progress or preserve nonsense.

Let me say that another way because it is important: persistence is a semantic filter. It is not a memory dump. It is the act of choosing which facts remain true after the machine forgets everything else.

Now, restoration. The last segment promised that we would move from what gets written to how the game becomes playable again after load validation accepts the save. This is the moment where many save systems fail in practice. A valid checksum only proves that the bytes match the checksum rule. It does not by itself give you a playable world. After validation, you must rehydrate the runtime in the right order.

Order matters. You need global or foundational save data available before systems that depend on it. You need player location before you can construct the correct overworld scene. You need party data before UI screens and field effects can accurately query the party. You need bag data before item menus and field-use item checks make sense. You need flags and variables before scripts decide which objects appear or which branches are available. And you need all of that to happen before the player regains control.

I am going to draw a restoration pipeline on the board. Raw flash read. Sector validation. Save block selection. Copy or decode into save-owned memory. Initialize runtime globals. Restore location. Load map resources. Spawn objects according to map data and event flags. Restore player avatar state. Enable callbacks and tasks for the field. That is not necessarily every function call in exact order, but it is the architectural shape you should be looking for when you inspect pokefirered/src/load_save.c chunk_d615017937c86ab6 alongside the save system chunks in pokefirered/src/save.c.

Here is a useful test for your own project. Could you delete all transient runtime objects, keep only your save blocks, and boot into a coherent scene? If the answer is no, either your save blocks are missing authoritative state, or your runtime initialization depends on hidden leftovers. Hidden leftovers are dangerous because they make load behavior work only in the same process that saved the game. On actual hardware after power-off, there are no comforting leftovers. There is flash and there is program code. Everything else must be reconstructed.

That brings us back to FireRed’s hardware discipline. The flash boundary forces honesty. RAM does not survive as your design crutch. The save system has to decide what crosses the boundary, and the load system has to rebuild everything else. Modern games sometimes hide this distinction behind engines, object serializers, cloud sync, or platform save APIs, but the architecture question is unchanged. What is the durable campaign state, and what is a rebuildable projection?

Now compare once more with Showdown’s Dex and mods model. Showdown can vary battle rules by generation or modded format. The data layer and simulator logic together define how a battle should behave under a selected rule context. When regression tests run, the important thing is that the same teams, choices, format, and data hooks produce the same result. FireRed’s save system is not a modded battle simulator, but if you hack FireRed, you create a similar compatibility problem. If your ROM hack changes species data, moves, items, or maps, old saves may point into a changed data universe. A saved party species number is only meaningful relative to the program’s species table. A saved item ID is only meaningful relative to the item table. A saved map coordinate is only meaningful relative to the map registry and layout. This is why “can reconstruct from canonical data” must include “which canonical data version?”

For advanced learners, this is where I expect you to stop saying, “the save stores the player,” and start saying, “the save stores stable identifiers whose interpretation depends on a particular executable data universe.” That phrasing is less cute, but it is much more correct.

Let us do one more concrete example. The player has a Charizard in slot one, a Bicycle in the key item pocket, the Boulder Badge flag set, and is standing in Cerulean City. What must persist? The party slot must persist as creature data. The Bicycle quantity or key item ownership must persist in the bag block. The Boulder Badge or badge-related flag must persist in the event or player progression block. The Cerulean location must persist in location or player state. What does not need to persist? The tiles currently decompressed into background memory, the menu windows that are not open after boot, the audio mixer’s exact sample position, the task that previously handled a fade, and the temporary script pointer if saving was not allowed in the middle of that script. On load, the game should reconstruct the map presentation, object events, UI baseline, audio state, and callback/task structure from the accepted save plus canonical game data.

Now, a quick comprehension check. If I change the player’s money, where should that change become durable? You should be thinking: wherever the player economic state is owned in the save blocks, not in the shop menu task. If I teach a Pokemon a move through the move relearner, where should that change become durable? In the party Pokemon data, not in the move relearner menu state. If I use a field item that changes world access, where should that change become durable? In the appropriate inventory and event state, depending on the action. We are carrying forward item-use dispatch and menu state machines as foundations, but the new question is ownership across power loss.

And notice how this prepares us for the rest of week 20. Today is “What Must Persist.” Next, when we move into “Loading and Saving FireRed State,” we will be more procedural. We will follow entry flow, validation support, continuation, save sector flow, and flash read/write boundaries more closely. The lab will ask you to design a schema and then simulate load validation and recovery. I do not want a decorative diagram from you. I want a schema that can fail in controlled ways. Corrupt the party block. Corrupt the metadata. Change the version. Drop one sector. Then tell me what the loader does and why the player either continues, falls back, or receives a new-game path.

That is also why the assessments get sharper this week. By week 19, you already had to design minimal clone versions of map loading, script execution, party data, battle turns, item use, and save persistence. So I am not asking you now whether you know that games have save files. I am asking whether your save boundary respects the ownership of all those systems. If a battle result changes party HP and experience, the party block must carry that forward. If a script changes a flag, the flags block must carry that forward. If an item use consumes inventory and unlocks a path, the bag and event state must carry that forward. But the callback that happened to process the input should not become an accidental permanent artifact.

Let us stop on that thought for a moment, because it is the hinge between persistence and the rest of the architecture. A creature RPG is not one system with a save button attached. It is a set of cooperating systems that continuously create visible behavior from data, globals, callbacks, tasks, scripts, and structs. Persistence decides which results of that cooperation become history. The save file is the game’s memory of consequences. Not animations. Not transient menus. Not every temporary variable. Consequences.

In the next portion, we will take that word, consequences, and apply it to each major block more rigorously. We will separate world consequences, party consequences, inventory consequences, and progression consequences, and we will ask how each one should be validated before it is trusted.

So, consequences. Let me put four words on the board: world, party, inventory, progression. These are not arbitrary buckets. They are a way of asking, after the console loses power and comes back, what facts must still be true for the player’s story to be continuous?

World consequence means: where is the player, what map should load, what objects should be visible, what local events have been resolved, what warp or script position is meaningful? Party consequence means: which creatures exist, what are their species, levels, stats, moves, experience, held items, status conditions, friendship, personality-derived traits, and other long-lived attributes? Inventory consequence means: what does the player own, in what quantities, and in which pockets or storage structures? Progression consequence means: which flags, variables, badges, unlocked systems, defeated trainers, and story gates have changed.

Now, notice something subtle. These categories overlap at runtime, but they should not be confused in persistence. When a story script gives the player a key item, the visible event crosses progression and inventory. The script flag may say the event has happened; the bag says the item exists. If one persists and the other does not, the world becomes inconsistent. You can get the classic failure: the NPC no longer gives the item because the flag says “already done,” but the bag does not contain the item because the inventory write was lost. That is not just a data bug. That is a broken consequence boundary.

And this is why save block ownership matters. FireRed does not treat the save as one vague object called game state. The source organization we are using this week pushes us toward more disciplined thinking. In pokefirered/src/load_save.c chunk_0755fbe98f753bb7, the load and save entry flow is where the game moves between boot-time initialization, attempted restore, and usable runtime state. In pokefirered/src/load_save.c chunk_3efebaad2e0f31e0, the validation support is the architectural counterpart to trust: before runtime systems act as though a world exists, the stored representation must prove it is coherent enough to load. And in pokefirered/src/save.c chunk_ab80cd8f4fa87d77 and chunk_852fbc242f85, the save system and sector flow make the physical reality unavoidable. The state is not floating in an abstract database. It is written across bounded flash sectors, and those boundaries shape how recovery works.

Let us pause there. If I erase the word “save” and write “commit protocol,” that may sound too database-like, but it is a useful mental model. The player thinks they selected Save. The engine has to serialize multiple owned regions of state, place them into flash-sized pieces, include metadata sufficient to later identify freshness and validity, and then on boot select a trustworthy version. So when I say “what must persist,” I do not only mean “which structs should be copied.” I mean “which consequences deserve to be committed, and how do we know the committed image is the one we should trust?”

Here is a concrete example. Imagine the player exits a battle, earns experience, a party member levels up, a trainer defeat flag is set, money changes, and the overworld resumes. Which of those must persist? The battle animation frames do not. The last cursor position on a temporary fight menu probably does not, unless the UI intentionally stores it as a convenience setting. The fact that the trainer is defeated does. The party’s new experience and level do. The money total does. The map position after the battle does, once the player is back in a stable overworld state. But the internal battle turn state should not usually become part of the normal overworld save, because FireRed’s save architecture is not trying to resume the middle of an arbitrary battle simulation frame. It saves stable game consequences, not every suspended computation.

This gives us a clean contrast with Pokemon Showdown, but remember the direction of comparison. Showdown is not replacing FireRed as our architecture source. FireRed is the cartridge RPG with flash persistence, save blocks, and overworld restoration. Showdown is useful as a secondary comparison because it makes battle state and deterministic simulation very explicit. In pokemon-showdown/data/abilities.ts chunk_da22cf969179258c, for example, abilities are represented as data with behavioral meaning and ratings; in pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f, damage behavior is expressed through hooks like damage callbacks and immunity checks. That style gives us a sharp picture of battle data hooks: a move, ability, or mod can influence the simulator through well-defined extension points.

But ask the persistence question. Does Showdown need to persist a trainer’s world flags, bag pockets, map object visibility, and flash-sector metadata? No. Its core simulator concerns are different. It often wants a battle to be reproducible from a format, teams, choices, RNG conditions, and turn log. FireRed wants the player’s entire adventure to survive power loss. That difference is the lesson. Battle hooks are excellent for deterministic regression testing, and Showdown’s Dex and mods architecture is a powerful comparison point for how data can drive behavior. But FireRed’s save blocks answer another question: after all these systems have produced consequences, which of those consequences are part of the adventure’s durable memory?

Check your understanding here. Suppose a poison damage tick occurs while the player walks in the overworld. The screen flashes, a sound plays, HP changes, maybe a message appears. Which pieces persist? The sound effect does not. The screen flash does not. The fact that the Pokemon now has lower HP does, because party state is durable. If the poison causes fainting and the engine updates related party status, that also belongs to the party consequence. If a script variable was temporarily used to route the message box, that temporary routing variable should not automatically be treated as progression unless it is one of the designated event variables. The question is always: is this a stable fact about the adventure, or just machinery that helped produce a frame of behavior?

Let me draw a simple board diagram in words. On the left, runtime systems: overworld, party, bag, flags, variables, battle, UI. In the middle, ownership boundaries: save block for player and world, save block for party and inventory, save block for event data, plus metadata. On the right, flash sectors: serialized chunks with checksums, identifiers, and some notion of save ordering. The arrows from left to middle are not “copy everything.” They are selection arrows. The arrows from middle to right are not “dump memory casually.” They are structured write arrows constrained by sector layout.

This is one of the places where advanced learners need to resist a tempting simplification. It is easy to say, “Just serialize all globals.” That is usually wrong. Globals include derived state, caches, temporary task data, callback pointers, decompression buffers, menu selections, transient battle workspaces, and half-completed UI transitions. We spent earlier weeks tracing data table to globals to callbacks to tasks to visible behavior. That tracing now pays off. You can look at a value and ask where it sits in the lifecycle. Is it source data? Runtime working state? A script consequence? A rendered effect? A restored identity? Only some of those belong in the save schema.

This also connects directly to the menu and item material from the recent lectures without re-teaching it. When an item is used, we already know there is dispatch: the item’s data and context lead to field-use or battle-use behavior, and menus often progress through task states and confirm or cancel branches. For persistence, the important part is not the menu state machine itself. It is the durable result after the state machine commits. If a healing item is consumed and HP is restored, the bag quantity and party HP are durable consequences. If the player cancels at the confirmation prompt, the cursor movement and prompt state are not consequences. If a key item unlocks a field behavior and a flag records that unlock, then inventory and progression need to agree.

Now, validation. Validation is not merely checking whether bytes exist. It is the process of deciding whether bytes can be trusted to reconstruct meaningful ownership boundaries. In the FireRed source focus, pokefirered/src/load_save.c chunk_3efebaad2e0f31e0 is important because validation support sits between raw storage and gameplay. The load path should not say, “I found some bytes, therefore I have a world.” It should say, “I found sectors whose metadata, ordering, checksums, and expected structure are good enough that I can restore the owned save blocks.” Then, and only then, can the overworld, party, bag, and flags resume their roles.

Consider a power loss during save. This is the classic reason sector validation exists. If the game writes sector one, then sector two, then power is lost before sector three, a naive loader could combine new and old data into an impossible hybrid. The player might have a new party state but an old flag state, or a new location with old map variables. The save system needs enough metadata to identify a complete coherent save set. That is why pokefirered/src/save.c chunk_852fbc242f85, the sector flow, matters architecturally. The sector is the unit the hardware gives you. The save image is the unit the game wants. Validation reconciles those two units.

And pokefirered/src/agb_flash.c belongs in the conversation even though we are not turning this into a hardware driver lecture. The file reminds us that persistence is not magic RAM with infinite writes and perfect atomicity. Flash has operations, boundaries, and failure modes. The architecture above it must respect those boundaries. When your lab asks you to design a save schema with save blocks for player, party, bag, flags, variables, location, and checksum or version metadata, you are not just drawing boxes. You are deciding how the logical adventure maps onto an unreliable physical write process.

Let me give you a failure scenario and have you reason through it. Suppose the player saves immediately after receiving a bike. The intended consequences are: the bag contains the bike key item, a story flag says the bike event was completed, maybe a script variable advanced the quest, and the player location is still the bike shop. During the save, inventory sectors are written from the new state, but event flag sectors are not fully written. On load, validation accepts a mixed image. What can happen? The player might own the bike but the NPC still acts as though the gift is available. Or the reverse: the NPC refuses to give the bike, but the bag lacks it. In a well-designed save flow, the loader should prefer the last complete coherent save image rather than accepting a partial hybrid. That is the difference between “some data survived” and “the adventure state survived.”

Now compare that with Showdown regression testing. In Showdown, if a move interaction is wrong, the natural test may be a turn sequence: set up the battle, choose moves, assert the resulting HP, status, or event log. The Dex and mod system let different generations or rule variants override data and scripts, and simulator turns can be replayed as tests. The source packet points us to pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f as an example of generation-specific battle behavior living in a mod script. That is a wonderful architecture for testing battle semantics. But FireRed’s week 20 problem is broader. We are not only asking, “Did the turn resolve correctly?” We are asking, “After the turn resolves and the game returns to an appropriate stable state, which results cross the persistence boundary?”

That boundary is also why we distinguish load initialization from normal runtime initialization. At boot, the game has to initialize enough systems to attempt load. If no valid save exists, it must initialize a new game state. If a valid save exists, it must restore owned blocks and then rebuild derived runtime structures around them. In pokefirered/src/load_save.c chunk_0755fbe98f753bb7 and chunk_d615017937c86ab6, the entry flow and continuation are the places to study this rhythm: initialize, validate, restore, continue into the runtime shell. We have seen this pattern before with callbacks and tasks. A callback slot does not itself tell you the whole game state. It tells you where execution will go next. Similarly, a save block does not contain every live computation. It contains durable data from which the correct computations can be restarted.

Here is another check for understanding. If you save while standing in a Pokemon Center, do we need to persist the exact animation frame of the nurse behind the counter? No. We need the map, coordinates, facing direction, relevant flags, party state, and perhaps variables that determine what script branch should occur later. The animation frame can be reconstructed by the overworld and object systems. But if the player has deposited a creature in a storage system, that storage content is durable. If a daycare system has accepted a creature, that daycare state is durable. The daycare source excerpt in pokefirered/src/daycare.c chunk_119931105f546346 shows how many systems touch creature, menu, event, storage, and overworld concepts at once. Persistence has to save the stable ownership state, not the temporary menu that got the creature there.

This is why I want you to think of save blocks as contracts between systems. The party system promises: given my persisted representation, I can reconstruct the player’s creatures. The bag system promises: given my persisted representation, I can reconstruct owned items and counts. The event system promises: given flags and variables, scripts can continue to gate world behavior. The overworld promises: given location and map-related state, it can place the avatar and rebuild visible map objects. The save system promises: given these blocks and metadata, it can write and later identify a coherent stored image.

The advanced part is that these promises are not independent in the player’s experience. They are independent enough to organize memory, but coupled enough that validation must protect consistency. That is why your lab should not only define fields. It should simulate a bad load. Corrupt a checksum. Remove a sector. Mix an old flag block with a new inventory block. Ask what the loader does. Does it reject the whole image? Does it fall back to the previous one? Does it initialize defaults only for a noncritical block? Be careful with that last option. Defaulting a block can be reasonable for settings or caches. It is dangerous for story, party, and inventory, because defaults can erase consequences while leaving other systems believing those consequences occurred.

Let us tie this to the overall course plan. Back in the runtime shell weeks, AgbMain, main callbacks, and task dispatch taught us that the game is always moving through controlled execution slots. In the data weeks, generated data and headers taught us that behavior starts from structured records and cross-module contracts. In the overworld and script weeks, flags and variables became the language of progression. In the party and battle weeks, creature structs, moves, battle setup, and battle transitions showed us how local simulations produce durable changes. You can see that in pokefirered/src/battle_setup.c chunk_494d132a59422c85, whose include surface alone tells us battle setup sits near overworld, items, scripts, field effects, battle transition, and load/save concerns. In the UI weeks, menus as task-backed state machines taught us that not all visible state is durable. Week 20 now asks you to integrate all of that and decide what survives.

So, when you design your own creature RPG, do not start persistence by asking, “How do I serialize my engine?” Start by listing consequences. Player identity and location. Party and storage. Inventory. Flags and variables. World object states. Money, badges, time-like counters if your design has them. Options if they are intentionally persistent. Then ask ownership: which subsystem owns the authoritative version? Then ask reconstruction: what can be derived after load? Then ask validation: how will I know this stored version is complete and compatible? Then ask migration or versioning: if the schema changes, how does the loader distinguish old valid data from nonsense?

Let me say one more thing about versioning, because it matters for modern clones even though FireRed is our historical architecture anchor. If you are building a PC or web-based RPG, you may not have GBA flash sectors, but you still have schema evolution. Your save may outlive your code. A field added in week ten of development may not exist in saves from week two. If you treat missing fields as zero without thought, you can accidentally grant or revoke progress. Metadata is not bureaucratic decoration. Version, checksum or hash, block identifiers, and save ordering are all ways of letting future code reason about past state.

And that is where Showdown’s regression culture becomes useful again. For your battle engine, regression tests can assert that a modded move hook still produces the expected turn result. For your save system, analogous tests should assert that known save images load correctly, corrupted images are rejected, old versions migrate intentionally, and partial writes recover to a coherent state. The comparison is not one-to-one, but the discipline transfers. Showdown’s simulator turn tests protect battle semantics. FireRed’s save validation protects adventure continuity.

Now, before we move into the next portion, I want you to answer this silently: if a value appears in a global variable during gameplay, what three questions should you ask before putting it in a save block? First, is it a durable consequence or temporary machinery? Second, who owns the authoritative meaning of that value? Third, can it be reconstructed safely from other persisted facts? If the answers are “temporary,” “unclear,” or “yes, it can be reconstructed,” you should hesitate before persisting it. If the answer is “this is the only authoritative record of a player consequence,” then it probably belongs in a save block, with validation metadata around it.

Good. Keep those three questions in front of you, because next we are going to get more concrete about restore order. It is not enough to know that world, party, bag, and flags persist. On load, these pieces have to come back in an order that lets dependent systems rebuild without reading uninitialized or contradictory state.

Now, restore order is where a lot of save systems reveal whether they were designed or merely accumulated. Let me put that on the board as a dependency chain, not a call graph. We are not trying to memorize the exact startup path today. We are asking, architecturally, what must be true before some other subsystem is allowed to interpret state.

Board cue: first box, “validated storage image.” Second box, “save block pointers and ownership.” Third box, “core player/world state.” Fourth box, “derived runtime systems.” Fifth box, “callbacks, tasks, UI, overworld behavior.”

The first point is validation. FireRed cannot treat flash as trustworthy RAM. Flash is persistent, yes, but it is not automatically coherent. A sector might be missing, older, partially written, or from a different expected layout. That is why the week’s source focus splits the subject between load-save entry flow, validation support, continuation, and sector flow: pokefirered/src/load_save.c chunk_0755fbe98f753bb7, chunk_3efebaad2e0f31e0, and chunk_d615017937c86ab6 give us the load and save entry behavior, while pokefirered/src/save.c chunk_ab80cd8f4fa87d77 and chunk_852fbc242f85 are the save-system and sector-flow side of the story. The architecture lesson is that “load game” is not a file read. It is a negotiation between persistent storage and runtime ownership.

So, suppose we have a saved player standing in Cerulean City with four badges, a party of six, a partially completed Rocket event, a bag containing the Bike Voucher, and a set of script variables that remember which NPCs have changed dialogue. What has to happen first? We validate the stored image. We do not spawn the player, load the map, render the NPCs, and then later discover that the flags were invalid. That would let dependent systems observe nonsense. Instead, validation decides whether we have a coherent save candidate at all. Only after that can we copy or point save blocks into their runtime homes.

Now pause here, because this is where advanced learners sometimes over-abstract. They say, “Well, I will just deserialize everything into one giant GameState object.” That can work in a small project. But FireRed’s structure is closer to several owned memory regions with many modules depending on known global locations and struct layouts. The save block design is an ownership contract. Player identity and location live in one conceptual ownership area. Party data lives in another. Bag and item quantities have their own meaning. Flags and variables are not just generic booleans and integers; they are the durable backing store for script progression. The source organization reflects that this is not a single monolithic object passed everywhere. It is a set of persistent facts that runtime systems know how to interpret.

Check for understanding: if the player’s current map group and map number are persisted, but the current camera scroll offset is not, why? Right. The map identity and coordinates are durable world position. The camera scroll offset is presentation machinery. It can be rebuilt once the map and player position are known. If you persist both carelessly, you can create contradictions: the player is logically at one tile, while the camera or object system believes another part of the map is active. That is not extra fidelity. That is a bug with a battery.

Now let’s make restore order concrete. The save system first needs to establish which saved data is valid. Then the load flow initializes or installs save block contents. Then higher-level systems can restore meaning. The world system can ask, “Which map should I load? Which warp or coordinate should I use? Which event objects are hidden because flags say so?” The party system can ask, “Which creatures are in the party, what are their stats, moves, held items, HP, status, and friendship?” The bag system can ask, “Which pockets contain which items and quantities?” The script system can ask, “Which flags and variables determine progression?” But notice the direction. The world should not make permanent claims before flags and variables are available. The party menu should not show summaries before party data is sane. The item menu should not offer field-use consequences before the bag exists and script blockers can be consulted.

This connects directly to our earlier work, but we do not need to re-teach it. When we studied item-use dispatch, we saw that an item’s visible effect depends on context: field use versus battle use, healing behavior, key item behavior, and script-driven blockers. Today’s point is narrower and deeper: after loading, those dispatch paths must be reading durable player consequences, not stale task-local state. A key item unlocking a route is not persisted because the item-use callback ran once. It is persisted because the bag, flags, and variables retain the consequences that future callbacks and scripts will consult.

Let me give you a concrete failure case. Imagine your lab schema persists the bag but forgets the event flag that says an NPC already accepted an item. On reload, the bag says the player no longer has the item, because it was consumed or exchanged, but the NPC flag says the exchange never happened. Now the script path may ask for the item again, block progression, or show pre-event dialogue. The bug is not in the menu. The bug is not in the item callback. The bug is that the durable consequence was split, and one half was not persisted. In FireRed terms, this is why we keep coming back to flags and variables as real save data, not convenience script scratch space.

Now, another board cue: I’m drawing a line between “canonical persisted facts” and “derived runtime caches.” On the canonical side, write player identity, location, party, bag, money, flags, variables, PC storage, options, and other durable game facts. On the derived side, write active callbacks, task IDs, temporary menu selection, animation counters, battle turn local scratch, decompressed graphics buffers, and many object runtime fields. Those derived systems are rebuilt by initialization paths.

That division also explains why source files like pokefirered/src/pokemon_summary_screen.c chunk_e8e17a42bbf17d46 matter to this course even though today is not a summary-screen lecture. The summary screen includes party, item, move, battle, menu, and link headers. It is a reader and presenter of data owned elsewhere. When restoring a save, we do not persist “the summary screen was on page three with this text rendered.” We persist the Pokémon and the context needed to reopen a valid UI later. The UI is allowed to be reconstructed. The creature data is not.

The same pattern shows up around battle setup. pokefirered/src/battle_setup.c chunk_494d132a59422c85 includes links into overworld, item, sound, Pokémon, load_save, safari, quest log, script, field control, battle transition, and battle configuration. That tells us something architectural: entering a battle is a convergence point of many systems. But the save file should not casually persist arbitrary halfway points inside that convergence unless the engine explicitly supports it. If the player saves in the overworld before a trainer battle, durable data can include location, flags, party, bag, and world state. The next battle can be rebuilt. Persisting the middle of a battle transition, the exact animation state, or a task-local countdown would be much more fragile.

Now let’s bring in Pokémon Showdown as a comparison, carefully. Showdown is not replacing FireRed as our main architecture source. FireRed is our persistence and handheld-runtime authority this week. But Showdown is useful as a secondary contrast because it is built around battle simulation, Dex data, mods, and regression tests. When you look at Pokémon Showdown’s data and simulator organization, especially the recurring modules under pokemon-showdown/data and pokemon-showdown/sim from the research packet, you see a system that cares intensely about reproducible battle behavior. Its data hooks, such as abilities and move callbacks, are designed to be interpreted by a simulator. The abilities file, pokemon-showdown/data/abilities.ts chunk_da22cf969179258c, even exposes a rating taxonomy in data comments, which is a very Showdown-like concern: battle semantics, competitive usefulness, and simulator-facing metadata are close together.

FireRed also has battle data and callbacks, but it is a cartridge game architecture with persistent adventure state. Showdown’s question is often, “Can I reproduce this battle turn correctly under this generation’s rules?” FireRed’s save question is, “Can I restore a whole adventure state after power loss and resume a coherent game?” Those are different centers of gravity.

Look at pokemon-showdown/data/mods/gen2stadium2/scripts.ts chunk_3480e574205d5d0f. The excerpt describes damage calculation paths: converting a move into an active move, checking immunity, handling OHKO moves, damage callbacks, fixed damage, and level-based damage. That is a clean example of simulator hooks. The system wants rule variations to be expressible, testable, and isolated by mod or generation. If a Gen 2 Stadium rule differs, the mod script can encode that difference. Persistence in that world is less about flash sectors and more about deterministic replay and state snapshots for a battle process.

So when we borrow insight from Showdown this week, we borrow the discipline of boundaries. Showdown teaches us to ask, “What is data? What is a hook? What is a mod-specific override? What is a turn-local simulation state? What can be regression-tested?” FireRed teaches us to ask, “What survives power-off? Which module owns it? How is it validated? What order restores it? What can be rebuilt from ROM data and persistent facts?” Together they sharpen the distinction between durable state and executable behavior.

Let me test that distinction. Suppose a move has a special damage callback. In Showdown terms, that callback belongs to the move or mod script behavior. Would you persist the callback itself in a save file? No. You persist the Pokémon that knows the move, perhaps the current battle state if your engine supports mid-battle saves, but not the code hook. The hook is part of the program or data ruleset. In FireRed, similarly, the ROM already contains move behavior tables and battle code. The save stores the player’s creatures and their learned moves, not a serialized copy of the battle engine.

Now, for your lab schema, this means your version metadata should identify the schema and maybe the ruleset version, but it should not duplicate immutable rule data. If your clone has a species table, move table, item table, and map table built into the ROM or shipped data package, your save can store species IDs, move IDs, item IDs, and map IDs. It should not store every species base stat, every move effect function, or every item callback. That would make saves huge, redundant, and more vulnerable to inconsistency. But, and this is the advanced caveat, once you allow mods, patches, or live data changes, IDs are not enough unless you can validate the ruleset they refer to. Showdown’s Dex and mods model makes this visible. A species ID or move ID is interpreted through a selected Dex context. If the context changes, the same symbolic name can behave differently. FireRed’s cartridge environment is more fixed, so the save can assume stable ROM tables. A modern clone may not have that luxury.

Board cue: I’m writing “stable lookup domain” beside “ID persistence.” This is one of the most important persistence ideas in the whole course. Persisting an ID is safe only if the lookup table remains compatible. If item 42 means Potion in version one and Bicycle in version two, your save schema did not persist an item. It persisted a future bug. FireRed avoids a large class of this problem because the shipped ROM data is stable. Showdown handles rule variation through Dex and mods, which makes the lookup domain explicit. Your own project needs one of those answers.

Now let’s return to flash boundaries. pokefirered/src/agb_flash.c is in the week’s source focus because the storage medium matters. It is tempting, especially if you come from web or desktop development, to treat persistence as “write JSON to disk.” On the GBA, flash persistence has physical and API boundaries. You write and read through routines that operate on sectors or chip-specific protocols. That lower-level reality influences the higher-level save design. You validate sectors. You distribute data. You maintain metadata. You do not assume an arbitrary object graph can be atomically rewritten in place.

That hardware lesson is still relevant even in modern systems. The equivalent might be localStorage quotas, mobile app sandbox writes, cloud sync conflict windows, database transactions, or console certification rules. Different medium, same architectural question: what happens if the write is interrupted? FireRed’s save-sector flow in pokefirered/src/save.c chunk_852fbc242f85 is exactly the kind of source we study because it forces us away from fantasy persistence. A save system is not correct just because serialization compiles. It is correct when it can tolerate the failure modes of its storage layer.

Let’s do a quick mental simulation. The player opens the menu and saves. Your game writes player state, then party, then bag, then flags. Power fails after the party sector but before the flags sector. On next boot, what should happen? If you have no validation strategy, you may load a mixed save: new party, old flags, maybe old location. That is the nightmare state. If you have sector checksums, save indexes, version numbers, or a two-slot strategy, you can reject the incomplete candidate and load the last coherent one. FireRed’s validation support in pokefirered/src/load_save.c chunk_3efebaad2e0f31e0 is part of that architectural story. Validation is not decoration. It is what prevents partial writes from becoming canonical history.

Check for understanding: in that failed-write example, why is loading an older complete save usually better than loading a newer partial save? Because coherence beats recency. A save file is not a journalistic record of the last bytes written. It is a contract that the game state can be interpreted as one consistent world. A slightly older but complete world is playable. A newer contradiction can trap the player or corrupt future saves.

Now, let’s connect this to week 20’s lab. You are designing a save schema with blocks for player, party, bag, flags, variables, location, and checksum or version metadata, then simulating load validation and recovery. At this point in the course, I expect you to be precise. In week 16, you decomposed the whole creature-collector RPG into runtime shell, maps, scripts, party, battle, inventory, UI, save, and link. In week 17, you traced behavior from data records through globals, callbacks, tasks, scripts, structs, and source functions. In week 18, you identified files, structs, functions, constants, data tables, and state variables. In week 19, you built minimal clone versions of map loading, script execution, party data, battle turns, item use, and save persistence. So for week 20, a schema that says “store game stuff” is not acceptable. You need ownership, restore order, validation, and failure behavior.

A good lab answer might say: the player block owns trainer name, ID, gender or appearance choice, money if your design places it there, play time, and options if tightly associated. The location block owns map group, map number, coordinates, facing direction, and respawn or warp information if applicable. The party block owns party count and fixed-size creature records, with each creature record containing species, level or experience, current HP, status, moves, PP, EV-like progression if your design has it, IV-like identity if your design has it, held item, friendship, nickname, original trainer fields, and checksum if your creature format uses one. The bag block owns pockets, item IDs, and quantities. The event block owns flags and variables. The metadata block owns schema version, save index, checksum, maybe block ID and size, and maybe ruleset identity if your data can change between versions.

But I want you to notice something: I just said “might.” The exact allocation depends on your engine. What does not depend on your engine is that each durable fact has one authoritative owner. If money is read by shops, scripts, and UI, that does not mean it should be stored three times. It means those systems should read the same authoritative value after load. If map identity is needed by the overworld renderer and the script engine, that does not mean both own it. One persistent location record owns it, and the rest consume it.

Let’s use the board again. I’m drawing arrows from save blocks outward. Party block points to battle setup, summary screen, party menu, daycare, and item healing. Bag block points to item menu, field-use dispatch, shop behavior, and script unlocks. Flags and variables point to maps, NPC visibility, dialogue branches, item blockers, and quest progression. Location points to map loading, camera initialization, object placement, and callback selection. That arrow diagram is the central architecture of today’s lecture. Persistence is not a passive archive. It is the root from which many runtime systems rehydrate.

Now, Showdown gives us a useful alternate arrow diagram. The Dex points to species data, move data, ability data, item data, formats, and mods. A battle instance points into that Dex and advances through simulator turns. Regression tests assert that given data, choices, random seeds or controlled randomness, and rule context, the result is correct. That is why pokemon-showdown/sim and pokemon-showdown/data are high-signal modules in the research packet. Their architecture makes battle reproducibility central. FireRed makes adventure restoration central. For an advanced clone, you may want both: FireRed-like save persistence for the RPG shell and Showdown-like deterministic tests for battle rules.

Here is a concrete clone design pattern. Store your adventure save as durable facts: player, world, party, inventory, flags. Store your battle rules as versioned data and code, not in each save. Then write regression tests for battle turns separately. For example, a test says: given species A with move B and ability C under ruleset R, when it attacks target D, damage and side effects match expectation. That is Showdown-inspired. Separately, a save-load test says: given a player in map M with party P, bag I, and flags F, after save, simulated partial write, and load recovery, the restored world is either the latest valid candidate or the previous valid candidate. That is FireRed-inspired.

Do you see the split? Battle tests protect rules. Save tests protect continuity. They are both persistence-adjacent, but they are not the same problem.

Now let’s talk about load initialization. A freshly booted game and a continued game share some startup machinery, but they diverge in what facts are seeded. A new game initializes default save blocks: starter values, initial map, empty or default flags, default bag, empty party until the starter is received, and so on. A continued game validates and restores existing blocks. The load-save entry flow in pokefirered/src/load_save.c chunk_0755fbe98f753bb7 is important because entry flow determines whether the game is creating state, continuing state, or rejecting invalid state. If you blur those paths, you get the classic bug where a “new game” inherits old flags or a “continue” path accidentally reinitializes something that should have persisted.

Let me make that bug vivid. A player saves after receiving the National Pokédex equivalent in your clone. On load, your initialization function runs default progression flags after reading the save. It clears the National Dex flag because the default value is false. The party and location load correctly, so the player does not immediately know something is wrong. Later, an NPC blocks content because the flag was silently reset. That is a restore-order bug. The initializer was allowed to overwrite canonical loaded state.

So your load code should have phases. Phase one, boot and allocate runtime memory. Phase two, validate persistent image. Phase three, install or initialize save blocks depending on new or continued game. Phase four, rebuild derived systems from canonical state. Phase five, enter the appropriate callback or scene. These phases can be implemented in many styles, but the boundaries matter.

Check for understanding: which phase should create task IDs for the menu that happens to appear after loading? Late. Task IDs are runtime machinery. You create them when entering that UI or scene. You do not store task ID number 7 in the save file and hope it means the same thing after boot. We spent earlier weeks on task allocation and RunTasks as per-frame dispatch, so this should now feel obvious: a task ID is meaningful inside a running process, not across power cycles.

The same applies to main callbacks. We studied AgbMain, main callback slots, and SetMainCallback2 earlier in the course. The save does not need to remember a raw callback pointer as durable state. It needs to remember enough world state that the engine can choose the correct callback after load. If you saved in the overworld, continue to overworld. If the game only permits saving from certain menus or field states, then the restore target is constrained. If you design mid-battle saving, then you must intentionally persist a battle state machine, not accidentally serialize whichever callback happened to be active.

This is also why many RPGs restrict when saving is allowed. Restriction is not laziness. It is a way of reducing the number of states that must be made durable. FireRed’s save design is shaped around a game that can restore coherent adventure state without needing to serialize every transient UI and animation state. In your own engine, every new save location is a promise. Saving during cutscenes, during trades, during link sessions, during battle animations, or during script command execution greatly expands the persistence surface. If you make that promise, you must own it.

Let’s connect link and system integration briefly, because weeks 20 and 21 sit together in the course plan. Link features are dangerous for persistence because they involve more than one machine’s truth. A trade, for example, cannot be half-persisted on one side and forgotten on the other. We saw daycare and trade-related includes in pokefirered/src/daycare.c chunk_119931105f546346, which is not our main source today but is a reminder that persistent creature ownership crosses systems: party, storage, daycare, trade, naming, moves, and event data all intersect. In week 21, when we focus more on link and integration, this save-block discipline carries forward. The question becomes: what must be atomically updated across local save data and communication protocol state?

For now, stay with single-cartridge persistence. Your schema should answer four questions for every block. What does it own? How is it sized and versioned? How is it validated? How is it restored relative to dependent systems? If you cannot answer those four questions, the block is not designed yet.

Let’s do another concrete example: the bag. The bag owns item quantities organized by pocket. It should validate item IDs, quantities, pocket capacity, and maybe sort or compaction invariants depending on your design. On load, the bag can be restored before menus are available. The menu UI then reads it later. Field-use item behavior reads it only when the player invokes an item. Scripts may check for item possession. But the bag should not own script consequences. If using a key item sets a bridge-repaired flag, the bag records that the player has or lacks the item; the event flag records that the bridge is repaired. If the item remains in inventory after use, then both facts may remain true: the player owns the key item, and the world has changed. That is not duplication because the facts are different.

Now the party block. The party owns creatures as durable individuals. A party member is not just species and level. It has identity. Current HP matters because saving after a difficult route should not heal the party unless the game explicitly does that. Status matters. PP matters. Moves matter. Held item matters. Original trainer and nickname may matter. Friendship may matter. Some stats can be derived from species, level, IVs, EVs, nature-like data, and current forms, depending on generation. So the persistence question is: do you store derived stats or recalculate them? FireRed stores enough creature data to preserve identity and progression, while many displayed stats can be recomputed. Your clone should decide intentionally. If you store both current derived Attack and the EVs that determine Attack, you need an invariant: on load, do you trust the stored stat, recalculate it, or validate one against the other?

Advanced point: derived data can be cached in a save if recomputation is expensive or required for compatibility, but then validation must treat it as cache, not truth, unless the game’s format defines it as truth. This is where real shipped games sometimes do things that are pragmatic rather than theoretically pure. Our job as architects is not to sneer at that. Our job is to know the tradeoff.

Now flags and variables. This is the hardest block to appreciate because it looks boring. A thousand booleans and small integers do not look like game design. But they are the memory of the world. Which trainers are defeated? Which doors are unlocked? Which NPCs have moved? Which gifts are collected? Which story beat is active? Which script branch should run? This is why flags and variables must be treated as first-class save data. If party is the memory of the player’s creatures, flags and variables are the memory of the world’s promises.

Board cue: I’m writing “flags are not temporary booleans.” Underline that. A local script condition that exists for two frames is temporary. An event flag that records a defeated trainer is durable. The difference is not the type. The difference is ownership and lifetime.

Now let’s fold regression testing into this. Showdown’s culture of simulator tests is valuable because battle logic has many edge cases. In FireRed-style persistence, we need the same seriousness for load/save. Your tests should not only say “save then load equals same object.” That is the easy case. They should simulate invalid checksum, older version, unknown item ID, too-large quantity, party count greater than capacity, corrupted block ID, missing sector, mismatched save index, and interrupted write. Then they should assert recovery behavior. Do we reject the save? Load backup? Clamp invalid data? Reinitialize only the damaged block? Different games choose differently, but silent acceptance is usually the weakest answer.

One more check for understanding: if a party count says 9 but the maximum party size is 6, what should the loader do? There is no universal answer, but there must be a defined answer. For a strict FireRed-like design, you probably reject that block or the whole save candidate because the invariant is broken. For a forgiving editor-friendly tool, you might clamp and report a repair. But in a shipped game, clamping without warning can destroy data and hide corruption. The important part is that validation is policy, not an accident.

As we near the end of this part of the lecture, let’s situate week 20 in the whole course. The first weeks taught you to see the runtime shell: AgbMain, callbacks, tasks, generated data, headers, and module boundaries. The middle weeks taught you to trace gameplay through maps, scripts, party, battle, inventory, and UI. Weeks 16 through 19 forced you to decompose and rebuild minimal versions of those systems. Now week 20 asks: after all that machinery runs, what remains? What must be remembered so the player’s adventure is still theirs tomorrow?

That is a different kind of architectural thinking. Runtime architecture asks, “How does behavior happen frame by frame?” Persistence architecture asks, “Which facts survive when frames stop happening?” If you persist too little, the world forgets the player. If you persist too much, you freeze temporary machinery and make future versions brittle. If you persist without validation, storage failures become game logic failures. If you restore in the wrong order, correct facts can still produce incorrect behavior.

So here is the synthesis I want you to carry into the workshop. FireRed teaches us that save data is owned, validated, sector-aware, and restored into a runtime that is mostly rebuilt. The week’s load_save.c and save.c chunks are not merely utility code; they are the boundary between a powered-off cartridge and a coherent adventure. Pokémon Showdown, used secondarily, reminds us that rules and hooks can be organized through Dex contexts, mods, simulator turns, and regression tests, but those are not the same as cartridge adventure persistence. In your own creature RPG, you want both disciplines: durable FireRed-style world continuity and Showdown-style confidence that rules behave consistently under test.

Before we move to the hands-on schema exercise, take ten seconds and ask the three questions from earlier for one value in your own design. Is it a durable consequence or temporary machinery? Who owns the authoritative meaning? Can it be reconstructed safely from other persisted facts? Now add the fourth question from this section: when, exactly, is it restored, and who is allowed to read it afterward? That fourth question is where many save systems become real.

## Source References
- pokefirered/src/battle_setup.c
- pokefirered/src/daycare.c
- pokemon-showdown/data/mods/gen2stadium2/scripts.ts
- pokefirered/src/pokemon_summary_screen.c
- pokemon-showdown/data/abilities.ts
- pokefirered/src/learn_move.c
