# Headers as Architecture Maps

Lecture ID: lecture_w02_d01
Week 2, Day 1

## Objectives
- Trace FireRed gameplay from data records through globals, callbacks, tasks, scripts, structs, and concrete source functions.
- Connect week 2 material to the overall course plan.

## Transcript
All right, let’s pick up from where week one left us. Last week we built the mental model of FireRed as a runtime shell: the game boots, installs a main callback, dispatches tasks each frame, and lets different systems temporarily own the screen. You already saw the callback spine in `pokefirered/src/main.c`, especially `SetMainCallback2` in `chunk_6cfd90f6d0136799`, and you saw task allocation and per-frame dispatch in `pokefirered/src/task.c`, with `CreateTask` in `chunk_cb0bb91a147bfb59` and `RunTasks` in `chunk_3ffebc0341225936`. I am not going to reteach those mechanics today. Instead, I want you to use them as a foundation.

Today’s phrase is: headers are architecture maps.

Now, when I say that, I do not mean headers are documentation in the friendly tutorial sense. FireRed’s headers are not a polished API guide written for new engineers. They are architecture maps because they tell us where module boundaries are, what data crosses those boundaries, what functions other systems are allowed to call, and which pieces of global state have become shared contracts. If you are trying to understand a decompiled or production C game, the header files often tell you the shape of the architecture faster than reading every source file from top to bottom.

Let me put the first board diagram up. I’m writing a chain across the board:

data records, generated C data, header declarations, globals and structs, callback or task entry point, concrete source function, visible behavior.

That chain is the major course pattern. We used a simpler version of it in week one when we traced from a data table to a callback to something the player can see. In week two, we are adding the build-time and cross-module pieces. In other words, we are asking: before runtime begins, how did the data become C data? Once it is C data, which header announces it to the rest of the program? Which source file owns it? Which runtime state machine consumes it?

That is why this week is not just “learn some header files.” It is “learn how FireRed’s source tree tells you the architecture before the game ever runs.”

Let’s start with a concrete source clue. In the research packet, `pokefirered/include/battle.h`, `chunk_4079f42fde2b4370`, is identified as the battle action and battler contract. That phrase matters. Battle is not one function. Battle is a cooperating system with battlers, selected actions, controller state, move effects, status effects, animation phases, AI decisions, and result handling. A header like `battle.h` is where the larger program learns the battle system’s public vocabulary. It is the place where other modules can say, “I know what a battler index means,” or “I know what a battle type flag means,” or “I know the globally visible state that battle code expects.”

Now compare that with `pokefirered/include/bag.h`, `chunk_8f2c0bdfc3224a33`, which the packet describes as the bag UI interface. That is a different kind of contract. The bag is not merely a data structure containing items. It is also a screen, a menu, a user flow, and a transition target. The header tells us how other systems ask the bag to appear or how the bag returns control. That means `bag.h` is not just “inventory declarations.” It is a cross-module agreement between inventory data, UI flow, item behavior, and callback ownership.

So, pause for a check. If I give you only two headers, `battle.h` and `bag.h`, and ask which one is more likely to expose long-lived battle-state globals and which one is more likely to expose screen-entry functions, what would you predict? Take a second. Battle needs shared runtime state because many source files participate in turn execution. Bag needs a public interface because many contexts can open the bag, but the bag screen should own its internal menu machinery. That prediction is exactly the architectural reading skill I want you to develop.

Now let’s use the map idea on an actual gameplay path.

Suppose the player is walking in grass and a wild encounter begins. Do not jump straight to “the battle starts.” That phrase hides the architecture. Instead, trace the layers. The map and encounter data exist as records. Some of that data is compiled into C arrays or binary assets. Runtime overworld code detects conditions for an encounter. Battle setup code coordinates the transition. Then the battle system takes ownership through callbacks and its own state. The visible behavior is: the screen flashes, the transition plays, the battle scene loads, and the player sees a wild creature.

The research packet gives us a useful source anchor for this crossing point: `pokefirered/src/battle_setup.c`, `chunk_494d132a59422c85`. The excerpt is just an include block, but that include block is extremely revealing. It includes `global.h`, `task.h`, `overworld.h`, `item.h`, `sound.h`, `pokemon.h`, `load_save.h`, `safari_zone.h`, `quest_log.h`, `script.h`, `script_pokemon_util.h`, `event_data.h`, `event_object_movement.h`, `metatile_behavior.h`, `event_scripts.h`, `fldeff.h`, `fieldmap.h`, `field_control_avatar.h`, `field_player_avatar.h`, `field_screen_effect.h`, `field_message_box.h`, `vs_seeker.h`, `battle.h`, `battle_transition.h`, and other battle-related headers.

That include list is a map of the handoff. Battle setup is not pure battle logic. It is connective tissue. It knows about the overworld, player avatar, field effects, scripts, sound, save data, battle transitions, and battle contracts. When you see a source file with that kind of include list, you should immediately think: this module sits at a boundary. It coordinates multiple systems. It probably contains functions that translate one runtime mode into another.

Notice how that builds on week one. Last week, when we talked about `SetMainCallback2`, the important idea was that a major screen or mode can take over the second main callback. Today, the question becomes: how do we know which module is allowed to set that transition up? One answer is: read the headers and include graph. A module like battle setup includes the public contracts of both sides. It can talk to overworld contracts, script contracts, battle contracts, transition contracts, and task contracts.

Now, let me write a smaller version of this on the board:

overworld state plus event data plus encounter condition points into battle setup, battle setup uses battle.h and battle_transition.h, callback control moves into battle runtime, battle globals and structs drive turn behavior.

That is not a call stack. Be careful. It is an architecture trace. A call stack is what the CPU is doing at one moment. An architecture trace is how responsibility moves through data, headers, globals, callbacks, tasks, scripts, and source functions over time.

This distinction is important for advanced learners because FireRed’s architecture is not expressed through modern service objects or dependency injection containers. It is expressed through C compilation units, global state, static data, callback slots, task arrays, and headers. If you read it like a small object-oriented application, you will keep looking for the wrong kind of boundary. If you read it as a GBA-era C program, the boundaries become much clearer.

Let’s take the bag as a second example because it contrasts nicely with battle.

The bag has item records. It has pockets. It has quantities. It has menu state. It has item-use behavior. It also has several entry contexts: opening the bag from the field, opening it during battle, selecting an item for a script or special action, maybe returning to a previous callback after the bag closes. The header `pokefirered/include/bag.h`, `chunk_8f2c0bdfc3224a33`, is useful because it tells us what the rest of the program is allowed to know about that UI. If a field script needs the player to choose an item, or battle needs the player to choose a usable item, the caller should not need to understand every scroll position, tilemap update, cursor sprite, or pocket rendering function. Those are internal implementation details. The header is the public door.

Here is the design rule I want you to hear: a header is a promise, but in a C game codebase it is often also a leak. It promises that certain functions, types, constants, or globals are available. But it may also leak state that a cleaner architecture would hide. When you inspect FireRed, you should ask two questions. First, what does this header intentionally expose? Second, what does this header expose because the codebase needs shared mutable state to make the runtime practical?

In battle, shared mutable state is hard to avoid. You have multiple battlers, a turn order, move choices, selected targets, side effects, messages, animations, and controllers. Many parts of the battle engine need to agree on indices and flags. That is why `battle.h`, `chunk_4079f42fde2b4370`, is architecturally dense. It gives us the vocabulary for the battle runtime. In bag, the public contract is more about entering and exiting a UI mode cleanly.

Now, let’s connect this to the week’s build-time topic. The danger with the phrase “data-driven” is that people hear it and immediately imagine runtime JSON files. FireRed is data-driven, but not in that way. A lot of data is prepared before the game runs. The build pipeline converts assets and structured source data into C arrays, binary blobs, headers, and generated files. That is why this week includes `pokefirered/build_tools.sh`, `chunk_1a4a3316f8593aaf`, the `pokefirered/Makefile`, `chunk_30eb2e53e9597446`, and `pokefirered/tools/jsonproc/jsonproc.cpp`, `chunk_dd905c2bfaba76d6`.

So, again, I’m writing another board chain:

JSON or asset source at development time, build tool, generated C or binary include, compiled ROM data, runtime pointer or array access.

That is the difference between build-time flexibility and runtime flexibility. FireRed does not need to parse a creature species JSON file while the player walks around Pallet Town. That would be wasteful on the Game Boy Advance. Instead, the toolchain can transform structured data into C-compatible tables before compilation. The runtime gets compact, directly addressable data. The human developer still gets a manageable data source.

This is where your lab this week comes from. You will create a mini JSON-to-C table generator for creature species records, then document which parts run at build time and which parts exist at runtime. The goal is not simply to write a script. The goal is to train your architecture vocabulary. If your generator reads JSON and emits a `.c` file, then the JSON parser belongs to the build tools. The emitted table belongs to the runtime program. The header that declares that table is a contract. The source file that uses the table is a consumer. The player-visible behavior is downstream from all of that.

Let me give you a small imaginary example, but keep it faithful to the pattern. Suppose we have a species record for a creature with base HP, attack, defense, type, catch rate, and front sprite ID. At development time, you might author that in JSON because it is readable. The build tool converts it into a C struct initializer. A header declares the species table and the struct shape. The creature creation code reads the table when generating a wild creature or displaying the party screen. The battle code later reads derived stats and move data. The visible result is that the creature has the correct stats, name, type, and sprite.

Now, where is the architecture map? It is not just in the JSON. It is in the header that says, “Here is the shape of a species record,” and in the source files that include that header. If only party creation includes it, that tells one story. If battle, Pokédex, party menu, summary screen, and evolution code all include it, that tells a broader story. Headers let us see reuse pressure.

This is why I want you to stop treating include statements as boilerplate. In a C codebase, include statements are architectural evidence.

Let’s return to `battle_setup.c`, `chunk_494d132a59422c85`, because it is a beautiful example. The file includes `script.h` and `event_scripts.h`, which tells us battle setup can be reached or shaped by the scripting/event layer. It includes `overworld.h`, `fieldmap.h`, and player avatar headers, which tells us the transition depends on field context. It includes `pokemon.h`, because battle must be populated with creature data. It includes `battle.h` and `battle_transition.h`, because it eventually crosses into battle runtime and battle presentation. It includes `task.h`, reminding us that this handoff is not merely a blocking function call. It participates in the frame-based runtime shell we studied last week.

Check for understanding: why is the include list not proof of exact control flow? Because an include list only tells us what names are visible to the compiler in that file. It does not tell us which functions call which other functions in a particular situation. But why is it still useful? Because it tells us which modules this source file is structurally allowed to coordinate. That is architecture evidence, not execution proof.

Good. Now I want to make the course-level connection explicit. Weeks one and two form the “runtime shell and source contracts” module. Week one gave you the runtime skeleton: main callbacks, tasks, per-frame dispatch, and mode ownership. Week two gives you the source contracts and build-time pipelines that feed that skeleton. After this, when we move deeper into overworld maps, scripts, creature data, inventory, battle, save, and link systems, you should not approach each topic as isolated code. You should ask the same recurring questions. What data records define the behavior? Are those records handwritten C, generated C, binary assets, or transformed from JSON? Which header exposes the public contract? Which globals or structs carry state? Which callback, task, or script advances it? Which source function produces the behavior the player sees?

That question set is your architecture lens for the rest of the course.

Let’s bring in a comparison carefully. The research packet mentions Pokémon Showdown files such as `pokemon-showdown/data/abilities.ts`, `chunk_d5bdb17c16d6f1a4`, and move or item logic in other chunks. Showdown is a useful comparison because it represents battle behavior in a modern TypeScript data-and-callback style. For example, the abilities excerpt in `chunk_d5bdb17c16d6f1a4` shows ability definitions with event hooks such as modifying a move or reacting to a damaging hit. That is very different from FireRed’s C organization, but it helps us name the contrast. Showdown can attach behavior directly to data objects as callbacks in a hosted runtime. FireRed generally compiles data and procedural code into a ROM-oriented C program, where headers and globals coordinate the engine. So when we compare them later, do not say “one is data-driven and one is not.” Say instead: both are data-driven in different architectural environments.

FireRed’s environment is constrained by the GBA, by C, by static compilation, and by a ROM build pipeline. Showdown’s environment is constrained by server simulation, TypeScript structures, and deterministic battle testing. Both need a way to express rules. But the contracts live in different places.

Now, a small board exercise. Imagine you are assigned to find out how a battle item can be used. You are not allowed to read every file linearly. Where do you start?

You might start with the bag interface, `pokefirered/include/bag.h`, `chunk_8f2c0bdfc3224a33`, because that tells you how the bag UI is entered and what public operations exist. Then you look for item behavior declarations, because selecting an item is not the same as applying an item. Then you look for battle context, likely through `battle.h`, `chunk_4079f42fde2b4370`, because item use in battle must affect battlers, selected targets, turn state, or battle messages. Then you look for source files that include both inventory-related headers and battle-related headers. A file that includes both is a bridge candidate. Then, only after you have the map, you dive into concrete function bodies.

That is how an advanced engineer reads a large C game. You do not wander. You triangulate.

Let me say that another way. Headers give you nouns and legal verbs. Source files give you procedures. Build rules give you origins. Runtime callbacks and tasks give you timing. Scripts give you authored progression. Globals and structs give you memory shape. Player-visible behavior is the last stop, not the first explanation.

Now, let’s briefly mention the build tools because they are part of the same map. `pokefirered/build_tools.sh`, `chunk_1a4a3316f8593aaf`, is part of the tool suite setup. The `pokefirered/Makefile`, `chunk_30eb2e53e9597446`, contains generated data rules according to the packet. And `pokefirered/tools/jsonproc/jsonproc.cpp`, `chunk_dd905c2bfaba76d6`, supports JSON/template generation. These are not runtime modules. The GBA is not invoking `jsonproc.cpp` while the player opens the bag. The Makefile and tools run on the developer’s machine. Their outputs become part of the compiled program.

This matters because students often blur the timeline. They say, “The game reads JSON.” Be precise. The build system may read JSON. The runtime game may read a generated table. Those are different claims. Precision here is not pedantry; it is architecture.

Let’s do a quick comprehension check. If I ask, “Where does the JSON parser live in the final ROM?” what is the answer? In this architecture, it usually does not. The parser belongs to the build tool, such as the kind of template-driven generation represented by `jsonproc.cpp`, `chunk_dd905c2bfaba76d6`. The ROM receives generated data or compiled assets. If I ask, “Where does the header fit?” the answer is: the header lets runtime source files agree on the shape and availability of the generated data or functions.

Good.

Now, before we move into the next portion of the lecture, I want to recap the first major idea. Week one taught us how FireRed keeps time: callbacks own modes, tasks advance work frame by frame, and the main loop dispatches those pieces. Week two teaches us how FireRed shares meaning across files: headers declare contracts, generated data enters the compiled program through build rules and tools, and source modules coordinate through explicit includes, globals, structs, and function declarations.

So when you look at `battle.h`, do not just see a list of declarations. See the battle system announcing its public vocabulary. When you look at `bag.h`, do not just see inventory-related names. See a UI module’s entry points and promises to other systems. When you look at `battle_setup.c` and its include list in `chunk_494d132a59422c85`, see a bridge between overworld, scripts, creature data, transitions, tasks, and battle. And when you look at the Makefile and `jsonproc`, see the pre-runtime machinery that turns author-friendly data into ROM-friendly structures.

We are going to build from this next by taking one header more slowly and asking, line by line, what kind of architectural contract each declaration represents: a type contract, a global-state contract, a function-entry contract, a constants contract, or a generated-data contract.

So let us take that categorization seriously. A header declaration is not merely “something the compiler needs.” That is the beginner interpretation. At our level, the more useful interpretation is that a header declaration tells you which part of the system is allowed to know which fact.

Now, on the board, I’m going to write five labels: type, global, function, constant, generated data. Those are not C language categories in the strict grammar sense. They are architectural categories. They help us read a file like `pokefirered/include/battle.h`, specifically the source focus chunk `chunk_4079f42fde2b4370`, as a map of obligations between modules.

A type contract says, “Other modules may talk about this shape.” A global-state contract says, “Other modules may observe or mutate this shared runtime fact.” A function-entry contract says, “Other modules may ask this subsystem to do something through this entry point.” A constants contract says, “Other modules must use the same symbolic vocabulary.” And a generated-data contract says, “A build-time pipeline will produce something whose runtime shape must match this declaration.”

That last one is the bridge into the rest of week 2. We are not only studying C headers this week. We are studying how data becomes code-shaped enough to be linked into the game. That is why our source focus includes `pokefirered/build_tools.sh`, chunk `chunk_1a4a3316f8593aaf`, the `pokefirered/Makefile`, chunk `chunk_30eb2e53e9597446`, and `pokefirered/tools/jsonproc/jsonproc.cpp`, chunk `chunk_dd905c2bfaba76d6`. The header tells us what runtime code expects. The Makefile and tools tell us how author-facing data is transformed into that expected form before the ROM ever runs.

Let’s make that concrete. Suppose the player opens the Bag and selects an item that can start an interaction affecting a Pokémon or a battle state. I am deliberately saying this at the architecture level first, not at the move-by-move level. There is inventory data. There is bag UI state. There are callbacks that own the screen. There are tasks that manage per-frame UI transitions. There may be scripts if the item belongs to an overworld event path. There are Pokémon structs and party data. And if the item affects battle, there is a handoff into the battle subsystem’s vocabulary.

Now where do we learn the legal words for those handoffs? We learn many of them from headers. `pokefirered/include/bag.h`, chunk `chunk_8f2c0bdfc3224a33`, is not just an inventory convenience file. It is the bag module saying, “Here are the ways outside code may enter or coordinate with the bag interface.” And `pokefirered/include/battle.h`, chunk `chunk_4079f42fde2b4370`, is the battle module saying, “Here are the action concepts, battler concepts, and shared declarations that other modules must use if they want to participate in battle.”

Pause and check your understanding here. If you see a function declared in a header, should your first question be, “Where is this implemented?” That is a useful question, yes. But architecturally, the first question should be, “Who is allowed to call this, and what system boundary does it cross?” Implementation location tells you where the work happens. Header exposure tells you that some other module needed permission to name that work.

Let’s connect this to `battle_setup.c`, because it is the most obvious bridge file in today’s packet. The include list in `pokefirered/src/battle_setup.c`, chunk `chunk_494d132a59422c85`, pulls in `task.h`, `overworld.h`, `item.h`, `pokemon.h`, `script.h`, `event_data.h`, `event_object_movement.h`, `field_control_avatar.h`, `field_player_avatar.h`, `battle.h`, `battle_transition.h`, and more. I want you to read that include list almost like an architectural confession. Battle setup cannot be purely “battle.” It needs the overworld because battles begin from field situations. It needs scripts because scripted trainers, wild encounters, and special cases are initiated by event logic. It needs Pokémon data because the party and enemy data must become battle participants. It needs task support and transitions because the player has to see an animated move from field mode into battle mode. It needs item-related includes because some encounter flows and special modes depend on inventory or mode state.

So when a student says, “Where does battle start?” the advanced answer is not a single function name. The advanced answer is, “Battle start is a boundary-crossing protocol.” The local source evidence for that is exactly the breadth of the include list in `battle_setup.c`, chunk `chunk_494d132a59422c85`, paired with the declarations exposed by `battle.h`, chunk `chunk_4079f42fde2b4370`.

Now, let me draw a path. Imagine this as a left-to-right trace on the board. Data record, then global or save-backed state, then script or field trigger, then setup function, then callback transition, then task-managed animation, then battle main loop, then visible behavior. I am not re-teaching the callback spine from week 1; you already practiced that in the week 1 lab and homework. I’m using it as prior machinery. The new point is that headers tell us which names can survive each arrow in that trace.

At the data-record end, FireRed often wants compact, compile-time structures, not runtime-parsed documents. That matters because a Game Boy Advance ROM is not a web server. The runtime does not want to discover schemas from JSON. The runtime wants addressable tables, constants, arrays, binary assets, and C symbols. So our week 2 phrase, “data-driven does not mean runtime JSON,” is not a slogan. It is a build architecture claim.

Look at the toolchain pieces in today’s source focus. `pokefirered/build_tools.sh`, chunk `chunk_1a4a3316f8593aaf`, represents the project’s tool-building stage. The `pokefirered/Makefile`, chunk `chunk_30eb2e53e9597446`, represents the dependency and generation stage, where source and data artifacts are transformed into build outputs. And `pokefirered/tools/jsonproc/jsonproc.cpp`, chunk `chunk_dd905c2bfaba76d6`, represents a specific style of generation: consume JSON-like authoring data and templates, then emit source-shaped artifacts. The important architectural move is this: JSON can be part of the development workflow without being part of the runtime architecture.

Let me say that another way. If a map layout, species table, or item definition begins life in a human-editable format, that does not imply the game engine contains a JSON parser. It may mean the repository contains a generator. The generator runs on the developer’s machine. It emits C, assembly, binary blobs, or included data. The ROM receives the result, not the editor format. This is why headers and generated data belong in the same week. A generated `.c` or `.h` file still has to satisfy the contracts that runtime modules compile against.

Here is a concrete example for your lab. You are going to create a mini JSON-to-C table generator for creature species records. Suppose your authoring JSON has fields like species id, base HP, base attack, type one, type two, catch rate, and growth curve. At build time, your generator reads that JSON and emits a C array of `struct SpeciesInfo` records. At runtime, the battle or summary screen code does not ask, “What is the JSON field named base_attack?” Runtime code indexes the compiled table and reads a field at a fixed offset in a struct. That is a completely different architectural posture.

Now, if you were designing the header for this mini system, what would belong there? Maybe the `struct SpeciesInfo` definition belongs there if multiple modules need to inspect the shape. Maybe symbolic species constants belong there if many modules need stable identifiers. Maybe an `extern const struct SpeciesInfo gSpeciesInfo[];` declaration belongs there if the array is defined in generated C but used elsewhere. That `extern` is a generated-data contract. It says: something will provide this symbol at link time, and every consumer agrees on its type.

Let’s pause. Quick check: if the generator changes the order of fields in the emitted initializer, but the struct definition in the header does not change, what happens? If the initializer is positional, values may silently land in the wrong fields, or the compiler may catch type mismatches only in some cases. If the initializer is designated, the risk is lower. But either way, the architectural lesson is that the generator and the header are coupled. The generator is not an isolated utility. It is part of the source contract.

That is exactly the sophistication I expect after week 1. Earlier, it was enough to say, “This callback eventually runs this behavior.” Now I want you to say, “This behavior exists because a build-time artifact satisfied a header contract, a global made the state reachable, a callback selected the owner of the frame, and a task or script advanced the interaction.”

Let’s return to `battle.h`. We do not need to enumerate every declaration in the lecture hall, but we do need to read its role. A battle header tends to expose shared battle vocabulary because battle is a highly connected subsystem. Many modules need to know what a battler is, which side a battler belongs to, what actions exist, what buffers or status values are meaningful, and which functions are safe entry points. That is why the source focus names `pokefirered/include/battle.h`, chunk `chunk_4079f42fde2b4370`, as the battle action and battler contract. The phrase “battler contract” matters. It means outside systems do not get to invent their own representation of a participant. They must speak the battle module’s language.

Contrast that with a more private implementation file. A `.c` file can contain helper functions and local static state that no other module should name. That is internal architecture. A header is external architecture. So the question becomes: has the project exposed too much, too little, or just enough? In a decompilation project like FireRed, we are observing the architecture that matches a shipped ROM and a reconstructed source organization. But as system designers, we can still learn the general rule: public declarations are pressure points. They show where systems must coordinate.

Now I want to bring in the inventory side. `bag.h`, chunk `chunk_8f2c0bdfc3224a33`, is a different kind of map from `battle.h`. Battle is a simulation-heavy subsystem with action vocabulary and participant state. Bag is an interface-heavy subsystem. It needs to expose entry points that other modes can use to show the bag, return from it, or use item behavior. That means its public surface often reveals navigation promises. Where can the game go after the bag closes? Which callback resumes? Which item-use mode is being invoked? Which pocket or selection state matters?

Notice how that ties directly to our prior callback work without redoing it. In week 1, you learned that main callbacks decide which broad mode owns the frame, and tasks can manage smaller state machines inside that mode. In week 2, headers tell us which modules are allowed to request those mode changes. If an overworld script opens the bag, it cannot simply will a menu into existence. It calls declared functions, passes declared types or constants, and relies on shared globals and callbacks that the target module understands.

Let’s make the trace more specific. A player presses A on an overworld object. Field control interprets the input and object context. A script may run. The script may set variables or flags in event data. If the script opens a menu or starts a battle, it crosses into another subsystem through declared entry points. If battle starts, `battle_setup.c` is one of the bridge areas where the necessary pieces meet; again, its include list in chunk `chunk_494d132a59422c85` is evidence of that cross-system dependency. Once control transfers into battle, battle code uses the battler vocabulary exposed by `battle.h`, chunk `chunk_4079f42fde2b4370`. If the player instead opens the bag, bag code uses the interface vocabulary exposed by `bag.h`, chunk `chunk_8f2c0bdfc3224a33`.

What I want you to see is that the architecture is not hidden behind one master object. FireRed is not organized like a modern service with dependency injection containers and runtime reflection. It is a ROM-oriented C program with explicit includes, globals, arrays, callbacks, tasks, scripts, and build products. The map is distributed. Headers are one of the best ways to reconstruct that map.

Now, a comparison will help. The research packet includes Pokémon Showdown sources, such as `pokemon-showdown/data/abilities.ts`, chunk `chunk_d5bdb17c16d6f1a4`, where abilities can attach behavior through event-like callbacks such as modifying moves or reacting to damaging hits. It also includes `pokemon-showdown/data/mods/gen2stadium2/scripts.ts`, chunk `chunk_3480e574205d5d0f`, where damage calculation is expressed through high-level TypeScript logic over moves, targets, immunities, and callbacks. That is useful as a secondary comparison because Showdown is also data-driven, but it lives in a very different runtime world. Showdown can represent behaviors as JavaScript or TypeScript objects with functions attached. FireRed, by contrast, compiles data and behavior into GBA-suitable C and binary assets. So do not flatten the phrase “data-driven” into one implementation style. Data-driven in Showdown can mean dynamic objects and event hooks. Data-driven in FireRed often means generated constants, tables, and compiled routines.

This is why your lab generator should not become a tiny web app that loads JSON at runtime. That would miss the lesson. The lab is to separate authoring time from runtime. At authoring time, JSON is a convenient source format. At build time, your tool emits C. At compile and link time, the C becomes symbols and ROM data. At runtime, gameplay code reads structs and constants. If you document those four phases clearly, you will have understood the center of week 2.

Let me ask another check-for-understanding question. If I hand you a header that declares `extern` data, and I hand you a Makefile rule that generates a C file defining that data, which one is “the source of truth”? The answer is not a single file. The source of truth is the contract among them. The header is the type-level promise to consumers. The generator is the production mechanism. The Makefile is the scheduling and dependency mechanism. The generated C is the concrete provider. The linker enforces that the symbol exists. The runtime code assumes all of that already happened.

That is an advanced answer because it refuses to confuse convenience with architecture. The JSON may be convenient. The header may be small. The generated file may look boring. But together they create the conditions for gameplay.

Let’s do a small board exercise. I’ll write `gSpeciesInfo` on the board. Around it I’ll draw four arrows. One arrow from `species.json`, representing author-facing data. One arrow from `jsonproc` or our mini generator, representing build-time transformation. One arrow from a header declaration, representing the public type and symbol contract. One arrow to battle, summary, Pokédex, or party code, representing runtime consumers. Now, erase the JSON arrow from the runtime half of the board. That is the important move. The player’s GBA is not parsing your authoring file when damage is calculated or a summary screen is drawn.

This also changes how you debug. If the wrong stat appears in battle, the bug could be in the JSON input, the generator template, the generated C, the header struct, the consumer indexing logic, or the runtime state that chose the species. A weak debugging approach says, “The data is wrong.” A stronger approach asks, “At which boundary did the representation stop matching the contract?” That question is exactly why we are learning to read headers as architecture maps.

Now, before we move on, let’s recap the through-line. Week 1 gave us the runtime spine: callbacks own broad modes, tasks advance lightweight per-frame state, and scripts can drive progression. We are not replacing that model. We are adding the compile-time and cross-module contract layer beneath it. Week 2 asks: where did the data and declarations come from before the callback ever ran? Which headers made it legal for one subsystem to name another subsystem’s types, globals, constants, and functions? Which tools generated data into a form the ROM can use?

And this connects forward to the rest of the course. When we later study overworld maps, event scripting, battle calculation, inventory flow, save data, and link behavior, we will keep asking the same trace question. Start from a record. Find the generated or compiled representation. Identify the global or save-backed state that stores the current facts. Locate the script, callback, or task that advances the behavior. Then land in concrete source functions. That is the discipline. Without it, FireRed becomes a pile of files. With it, FireRed becomes a set of cooperating state machines and data contracts.

So, as we continue, I want you to keep the five labels visible: type contract, global-state contract, function-entry contract, constants contract, generated-data contract. Every time we open `battle.h` or `bag.h`, classify what you see. Do not rush to implementation. Ask what relationship the declaration creates. Then, once the relationship is clear, follow it into the `.c` files, the generated sources, the Makefile rules, or the tools. That is how a header stops looking like a list and starts looking like a map.

Now, let’s make this less abstract by walking one subsystem boundary and treating the header as the first architectural artifact, not the last. I’m going to use battle first because battle is where FireRed’s architecture becomes visibly contractual. In `pokefirered/include/battle.h`, chunk `chunk_4079f42fde2b4370`, the declarations are not just “battle-related things.” They tell us which pieces of state are shared widely enough that multiple source modules must agree on their meaning. When you see names like battle type flags, battler identifiers, selected actions, current move state, outcome state, or entry points into battle control, you are looking at a module boundary that other systems are allowed to touch.

And notice the word “allowed.” That is important. A header is not a diary of everything a module knows. A header is the public shape of what other modules may rely on. So if battle exposes a battler contract, it is saying, “Other modules may speak about battlers using these identifiers, these constants, these structs, and these functions.” It is not saying, “Every piece of battle logic belongs everywhere.” That distinction is the difference between an architecture and a bowl of spaghetti.

Let me put a small trace on the board. We’ll start with an overworld event that leads into combat. I’ll write: map or script condition, then battle setup, then global battle state, then callback transition, then battle loop. We are not re-teaching the callback spine from week one, but we are using it. You already know that FireRed moves between major modes through callback slots, and you already know that tasks can provide lighter per-frame state machines inside those modes. Today the new point is this: the header tells you which vocabulary survives that transition.

So, imagine a trainer battle. The player steps into a trainer’s line of sight. The overworld system detects an event condition. Eventually, that route enters battle setup code. The research packet gives us a useful anchor in `pokefirered/src/battle_setup.c`, chunk `chunk_494d132a59422c85`, where the include list alone is revealing. That file includes `overworld.h`, `script.h`, `event_data.h`, `field_player_avatar.h`, `battle.h`, `battle_transition.h`, and other modules. Stop there for a second. Before reading a single function body, the includes tell us the role of the file. It is not “the battle engine.” It is an adapter between field state, script state, event state, transition effects, and battle entry.

That is a very advanced reading move. Many students open the implementation and look immediately for the biggest function. I want you to learn to pause at the include surface. What domains does this source file need to speak? What contracts does it consume? `battle_setup.c` needs field control concepts, because the player was in the overworld. It needs script concepts, because many battles are triggered or parameterized by scripts. It needs event data, because flags and variables determine whether a trainer has been defeated or which battle is being launched. It needs `battle.h`, because once setup is done, the result must be expressed in the battle subsystem’s public vocabulary.

Check your understanding here. If I ask, “Why doesn’t battle setup just directly manipulate every internal detail of the battle engine?” the answer is not merely “because that would be messy.” The stronger answer is that the architecture needs a stable seam between preparing a battle and running a battle. The setup side can decide what kind of battle this is, who the opponent is, which transition to show, and what event state matters afterward. But the battle loop itself owns the detailed action resolution. The header is where the shared terms are legalized.

Now let’s connect that to `battle.h` as an architecture map. When you classify declarations in that header, some are constants contracts. Battle type flags are the classic example. A wild battle, trainer battle, double battle, link battle, Safari battle, and so on are not random booleans passed informally through the program. They are bit-level facts that multiple systems may test. Setup code sets or combines them. Battle code interprets them. UI code may branch on them. Result handling may depend on them. That makes the constants a shared architecture language.

Some declarations are global-state contracts. If the battle engine exposes current battler state, selected move state, battle outcome, or arrays representing battlers, those declarations announce that battle is a stateful runtime mode. This matters because students sometimes say, “Couldn’t we just make battle a pure function from two teams to a result?” In a modern simulator, perhaps you might design it that way. But FireRed is running on the Game Boy Advance, inside a frame-based shell, with animations, input, music, palette changes, scripts, and save consequences. The battle cannot be a single pure calculation. It is a long-running state machine that must preserve its current facts across frames.

Some declarations are function-entry contracts. A function like a battle initialization callback or a battler query function is an invitation to cross the boundary in a controlled way. You do not need to know the internal layout of every battle controller just to ask which battler is at a position. You do not need to know every script of battle animation just to enter battle mode. So, on the board, under function-entry contract, I’ll write: “narrow verbs over wide state.” Good headers often give other modules narrow verbs. They do not force every caller to reason about the full internal machine.

Now compare this with `pokefirered/include/bag.h`, chunk `chunk_8f2c0bdfc3224a33`. The bag subsystem has a different shape. It is UI-heavy. It needs to be entered from multiple places: the start menu, battle, field scripts, item selection workflows. A bag header therefore tends to advertise entry points, pocket identifiers or context information, callbacks for returning to the caller, and item-selection behavior. Again, do not read it as “a list of bag functions.” Read it as the legal ways other modules can ask the bag UI to run.

Here is the concrete example. Suppose the player is in battle and chooses an item. The visible behavior is simple: open the bag, choose a potion or ball or held item depending on context, return to battle, apply the item, continue the turn. Architecturally, that is not simple. The bag must know it was opened from battle, because not every bag action is valid. The battle must pause its own action flow and expect a result. The item data must be interpreted. The UI must remember pocket scroll positions and selection state. The callback return path must be preserved. If you trace from visible behavior backward, you will hit `bag.h` not because it implements all of this, but because it names the cross-module interface that makes this workflow possible.

And this is where week two connects directly to week one. Last week, when we traced callbacks and tasks, we were mostly asking, “Who runs each frame?” This week, we ask, “What vocabulary allows one frame-running system to call another without collapsing boundaries?” A callback tells us where control goes. A header tells us what meanings can cross with that control.

Let me slow down and give you a diagnostic test you can use during lab. When you open a header, ask: if I delete this declaration, which other architectural region loses a word it depends on? If deleting a constant breaks battle setup, battle UI, and result handling, that constant is probably a cross-module contract. If deleting an extern global breaks many unrelated modules, that global is a shared-state contract, and possibly a risky one. If deleting a function prototype breaks only one source file, maybe the declaration is too public, or maybe that one source file is the intended client. The header lets you reason about dependency pressure before you dive into implementation details.

Now I want to bring generated data into this, because the week is not only “headers.” It is “Headers, Generated Data, and Build-Time Pipelines.” FireRed’s source tree is not just handwritten C files. Some runtime tables are produced or embedded by tools before the game ever boots. The research packet points us to `pokefirered/build_tools.sh`, chunk `chunk_1a4a3316f8593aaf`, as the tool suite entry point, and to `pokefirered/Makefile`, chunk `chunk_30eb2e53e9597446`, for generated data rules. That means some files you read as “source” are actually products of build-time transformation.

This is a major architecture point, so I’ll say it plainly. Data-driven does not automatically mean runtime-parsed. In FireRed’s architecture, much of the data-driven shape is resolved before runtime. The game may be built from JSON-like or asset-friendly inputs, but the shipped program wants compact C arrays, binary blobs, symbols, and tables that can be addressed cheaply. That is why this week’s lab asks you to create a mini JSON-to-C table generator for creature species records and then document which parts run at build time and which parts exist at runtime. The documentation is not busywork. It is the architecture.

Let’s connect that back to headers. If a generated C table represents species records, some header or included declaration must tell the rest of the program what a species record looks like, or at least how to refer to it. The generated-data contract is not just “there is a JSON file somewhere.” The contract is the compiled result: the struct layout, the symbol name, the constants used as indexes, and the functions or globals that consume the table. Runtime code does not care that a designer-friendly representation existed earlier unless the runtime parser also ships. In a GBA game, you should assume the runtime wants pre-digested data unless evidence says otherwise.

The tool `pokefirered/tools/jsonproc/jsonproc.cpp`, chunk `chunk_dd905c2bfaba76d6`, is a useful example of that build-time philosophy. Even from the packet summary, we know it belongs to a JSON and template-driven source generation flow. The interesting architectural claim is not “FireRed uses JSON,” full stop. That would be too shallow. The better claim is: FireRed can use JSON as an authoring format while still presenting generated C or assembly-compatible data to the game. In other words, JSON may be a source-of-truth format for humans and tools, but it is not necessarily a runtime dependency.

Board cue here: I’m drawing two boxes. Left box says “authoring world.” Right box says “runtime world.” In the authoring world, place JSON, PNGs, maps, layouts, templates, and tool scripts. In the runtime world, place compiled tables, embedded binary assets, C structs, constants, globals, callbacks, and tasks. Then draw an arrow through the Makefile and build tools. That arrow is the build pipeline. Your job in this week’s lab is to label the arrow, not just the boxes.

Now, why does this matter for an advanced creature RPG architecture course? Because when you design your own creature collector, you will be tempted to put everything in runtime JSON because it feels flexible. For a desktop prototype, that can be reasonable. For a constrained runtime, or for deterministic shipping behavior, or for a codebase that wants compile-time symbols and linkable assets, build-time generation is often cleaner. FireRed demonstrates that data-driven architecture can mean “the behavior is controlled by tables,” while the tables themselves are compiled into the program.

Let’s do a check for understanding with a deliberately tricky question. If a species’s base stats are authored in JSON and converted into a C array during the build, is the running game JSON-driven? I would not say that. I would say the project’s content pipeline is JSON-authored, and the runtime is table-driven. That distinction matters. If a bug appears in battle damage calculation, you inspect the runtime table layout and battle code. If a bug appears where a species has the wrong value after rebuilding, you inspect the JSON input, the generation template, and the generated output. The debugging path depends on knowing which world owns the error.

Let’s bring `bin2c` into the same frame. The packet names `pokefirered/tools/bin2c/bin2c.c` under the tools module, and the source focus names `build_tools.sh` as part of the tool suite. A binary embedding tool is architecturally similar to JSON generation, even though the input is different. It takes an asset that is not naturally a C source file and produces a representation that the C build and linker can consume. The runtime does not open a file browser and load arbitrary assets. It refers to compiled symbols and data placed in the ROM. So again, the source tree contains two kinds of reality: authoring reality and runtime reality.

This is also why headers can map more than handwritten modules. A header may declare symbols whose contents are generated. That header is still the architectural contract. If a generated table changes shape, the header and consuming code must agree. If the generation rule changes the symbol names, the references break. If constants used as indexes drift away from generated table order, you get logical corruption. In a creature RPG, that can be subtle. One species’s data might appear under another species’s constant. A move table might line up incorrectly. A trainer party might reference the wrong species. The compiler may not always save you if the types are compatible but the meanings are misaligned.

Now let’s revisit battle through that lens. Battle is a runtime state machine, yes, but it consumes data that is heavily table-shaped: species data, move data, item effects, trainer parties, encounter information, and more. The battle header’s battler contract gives runtime code a way to talk about active participants. The data tables give the participants their stats, moves, types, and constraints. The setup path chooses which records become active. The callback path lets battle own the screen and frame loop. The outcome path returns consequences to the overworld or script system. That is the full trace: data records through globals, callbacks, tasks, scripts, structs, and concrete source functions.

Say that trace with me mentally, because it is the central skill of the course. Data records. Globals or save-backed state. Callback or script entry. Task or state machine progression. Struct interpretation. Concrete source functions. Player-visible behavior. If you can perform that trace, you can understand a system like FireRed without needing someone to narrate every function.

Let’s take the bag version of the same trace. The data record may be an item definition and the player’s save-backed inventory quantities. The global or save-backed state records what the player owns, which pocket it belongs to, and perhaps UI position state. The function-entry contract in `pokefirered/include/bag.h`, chunk `chunk_8f2c0bdfc3224a33`, determines how another system opens the bag and with what context. A callback gives the bag control of the screen. Tasks inside the bag UI manage cursor movement, pocket switching, message prompts, and item action menus. The concrete source functions eventually apply an item, cancel, or return selection information to the caller. The visible behavior is that the player picks an item and the correct thing happens.

Here is the sophistication I want you to add after week one’s homework. Do not merely identify “there is a task.” Ask what state the task owns that the header deliberately does not expose. A good module boundary hides internal sequencing. In battle, other modules should not micromanage every animation substate. In bag, battle code should not manipulate every cursor, window, and menu task directly. The header should expose the entry points and shared facts, while the implementation owns the internal dance.

Now, FireRed is an older C codebase and a decompilation project, so you will absolutely see more global exposure than you might choose in a fresh architecture. Do not respond to that with simplistic judgment. The useful question is not, “Is this perfectly encapsulated by modern standards?” The useful question is, “Given the hardware, the compiler model, the original game architecture, and the decompilation target, what contracts did the system settle on?” Sometimes a global is a practical coordination point. Sometimes it is an architectural liability. Your job is to classify it before condemning or imitating it.

Let me give an example of a liability. If many modules can write to a global battle outcome directly, then finding the cause of a wrong post-battle result becomes harder. But if only battle code writes it and other modules read it after returning from battle, the same global functions more like an explicit handoff register. The declaration alone may not tell you which is true. The header starts the investigation; source references finish it. That is why our workflow is header first, reference search second, implementation third.

In lab, when you build the mini JSON-to-C species generator, I want you to deliberately create a tiny header for the generated output. Do not just emit a `.c` file and include it randomly. Define the species struct. Define the species constants or indexes. Declare the generated table. Then write one runtime function that consumes the table without knowing anything about JSON. That separation is the entire lesson. If your runtime function opens the JSON file, you have built a different architecture from FireRed’s build-time pipeline. That is not automatically wrong in general, but it is wrong for this lab’s objective.

And when you document it, use two verbs carefully: “generate” and “interpret.” The tool generates C from JSON and templates. The compiler compiles that C. The runtime interprets the resulting table according to struct definitions and constants. The runtime does not generate the table. The generator does not apply battle damage. Those roles are separate. If you blur them in your explanation, you will blur them in your design.

Let’s pause for a recap. Headers are maps because they name the legal relationships between modules. `battle.h`, chunk `chunk_4079f42fde2b4370`, maps battle’s shared vocabulary: battlers, actions, battle types, state, and entry points. `bag.h`, chunk `chunk_8f2c0bdfc3224a33`, maps the bag UI’s entry and return contracts across contexts like field and battle. `battle_setup.c`, chunk `chunk_494d132a59422c85`, shows an adapter source file whose include surface already tells us it bridges overworld, scripts, events, transitions, and battle. `build_tools.sh`, chunk `chunk_1a4a3316f8593aaf`, the `Makefile`, chunk `chunk_30eb2e53e9597446`, and `jsonproc.cpp`, chunk `chunk_dd905c2bfaba76d6`, remind us that some architecture happens before the ROM ever runs.

So the transition I want to make next is from “what the header exposes” to “why the build system must agree with the header.” Because once generated data enters the picture, a header is no longer only a source-level agreement between humans writing C. It becomes an agreement between humans, generators, templates, compiler rules, and runtime consumers. That is a much stronger contract. And if you are building a serious creature RPG, that is exactly where your architecture either becomes scalable or starts quietly accumulating contradictions.

Now, put that sentence on the board: the generator must satisfy the same contract as the hand-written module. That is the architectural point.

If a human writes a C file that exports a table, and another human writes a header that declares that table, we already understand the contract. The declaration must match the definition. The consumers must interpret the records correctly. The array length, struct layout, enum values, and sentinel conventions all have to agree. Fine. But when we introduce a generator, the same obligations do not disappear. They move earlier. They become build-time obligations.

So imagine our mini lab version. We have a `species.json` file. It says Bulbasaur has base HP, base Attack, types, catch rate, growth rate, maybe an evolution method. Our generator reads that JSON and emits a C initializer for an array of species records. Now ask: who is the client of that generated C? It is not the generator. The generator is gone at runtime. The client is the compiled game code. The battle system, the summary screen, the party menu, the Pokédex, the evolution code, and maybe the AI code all see only C data in ROM. So if our generator emits fields in the wrong order, the runtime does not say, “Ah, this JSON property was misplaced.” The runtime just reads the wrong byte or halfword and behaves incorrectly.

That is why this week’s title is not “headers as include files.” It is “headers as architecture maps.” A header tells us the shape that generated and hand-written code must meet. In `pokefirered/include/battle.h`, chunk `chunk_4079f42fde2b4370`, the battle interface is not merely a pile of declarations. It is a map of which concepts are allowed to cross the boundary into battle: battlers, actions, move choices, battle types, status flags, controller communication, and shared globals. You should read that header as a public treaty between the battle engine and the rest of the game. When another module wants to initiate or inspect battle behavior, it does not get unlimited access to “battle” as a vague idea. It gets the names and shapes declared there.

And let me make that more concrete. Suppose the overworld system has just detected a wild encounter. We have already studied the callback shell and the task loop, so I am not going to rebuild that mechanism from scratch. Use it as assumed infrastructure. The field code is running in the main runtime shell; some event or movement logic determines that a battle must begin; now the program has to cross from field semantics into battle semantics. At that moment, it needs shared concepts. What is the battle type? Who are the battlers? Which party data will be read? Which global flags tell battle what kind of scene it is entering? Those answers cannot live only in a `.c` file hidden deep inside battle. They must be declared as cross-module contracts, and that is what `battle.h`, chunk `chunk_4079f42fde2b4370`, is doing.

Now compare that to `pokefirered/include/bag.h`, chunk `chunk_8f2c0bdfc3224a33`. The bag header is a different kind of map. It does not expose a full combat universe. It exposes an interface for entering and using the bag UI and item behavior from other contexts. The bag has to be reachable from the field, from battle, and from scripts or menu flows. But those contexts do not all need the same thing. In the field, using an item might affect the overworld or a party creature. In battle, using an item has to feed into turn structure and battle restrictions. So the bag interface becomes an adapter boundary. It is the place where the inventory system says, “Here are the ways other systems may ask me to operate.” That is architecture in the header.

Let’s pause and check understanding. If I ask you, “Where is the bag implemented?” you might say, “In the bag source files.” Fine. But if I ask, “Where is the bag’s architectural boundary visible?” the better answer is the header, because the header shows what other systems are allowed to assume. Same with battle. Implementation tells us how a system works internally. The header tells us how the rest of the program is permitted to depend on it.

Now, slide forward to the build system. In `pokefirered/build_tools.sh`, chunk `chunk_1a4a3316f8593aaf`, we see the project treating tools as part of the codebase, not as an afterthought. That matters. A creature RPG has too much structured data to maintain every emitted byte by hand forever. Species tables, map data, trainer parties, item data, text, graphics, and binary assets all need a disciplined path into the ROM. The build tools are part of the architecture because they define how source assets become compiled assets.

And then the `pokefirered/Makefile`, chunk `chunk_30eb2e53e9597446`, gives us the dependency story. The Makefile is not just a convenience script that runs the compiler. It is a graph of transformations. Some files are C sources. Some are generated C or assembly. Some assets are converted. Some binary blobs are embedded. If the header is the map of source-level contracts, the Makefile is the map of build-time causality. It answers, “When this input changes, what must be regenerated so the compiled ROM still satisfies its own declarations?”

This is the point I want you to carry into the lab. Data-driven does not automatically mean runtime JSON. In a desktop game or server simulation, you might load JSON at startup, validate it, keep it in memory, maybe hot reload it. In a Game Boy Advance ROM architecture, FireRed’s style is different. The data-driven quality comes from tables, constants, generated source, and embedded assets that are fixed at build time. The runtime is data-driven because it reads structured records. But the runtime is not necessarily parsing the authoring format.

Now let me draw a pipeline on the board in words. On the far left: authoring data. That might be JSON. In the middle: a generator. For FireRed’s week-two focus, `pokefirered/tools/jsonproc/jsonproc.cpp`, chunk `chunk_dd905c2bfaba76d6`, represents a template-driven source generation approach. On the right: emitted C or other compile-ready source. Then the compiler and linker produce ROM data. At runtime, gameplay code reads the compiled arrays and structs. Notice the generator is not in the runtime path. The JSON parser is not in the runtime path. The template is not in the runtime path. Only the compiled result is.

That distinction solves a lot of confusion. Students often say, “If it is data-driven, where is the JSON loader?” In this architecture, that is the wrong question. The right question is, “Where does the data become a shape that the runtime can consume?” The answer may be before compilation. `jsonproc.cpp`, chunk `chunk_dd905c2bfaba76d6`, belongs to that pre-runtime world. The `Makefile`, chunk `chunk_30eb2e53e9597446`, decides when that pre-runtime transformation happens. The generated output must match the headers that runtime C includes. That is the full contract.

Now for a concrete example. Suppose our species record struct has fields in this order: base HP, base Attack, base Defense, type one, type two, catch rate. The header declares that struct. The battle calculation code reads base Attack when computing physical damage. The party summary screen reads type one and type two when drawing the UI. The capture code reads catch rate when resolving a ball throw. Now suppose the generator accidentally emits catch rate before type two. The compiler may still accept the code if the initializer types are compatible enough. The ROM builds. The game boots. But now some species might have a nonsense secondary type, or the catch formula might read what was intended to be a type constant. That is not a “JSON bug” at runtime. It is a violated architecture contract between generator, header, and consumer.

This is why advanced architecture work is often about boring-looking boundaries. You do not get stability by saying “we are data-driven.” You get stability by making sure every stage agrees about the data’s shape. Header declarations, generated definitions, Makefile rules, and source consumers all have to line up.

Let’s connect this back to `battle_setup.c`, chunk `chunk_494d132a59422c85`. The include list there is already a little architectural diagram. It pulls in `overworld`, `item`, `pokemon`, `script`, `event_data`, `battle`, `battle_transition`, and other modules. That tells us battle setup is not pure battle logic. It is an adapter. It translates from field conditions, events, scripts, items, and party state into a battle entry. So if generated data feeds species or trainer definitions, and the battle setup code selects or prepares an encounter, the path is not “JSON to battle” directly. It is authoring data to generated ROM tables, tables to globals and structs, field or script logic to setup code, setup code through battle contracts, then battle callbacks and tasks render behavior frame by frame.

Say that again in your own mental model: data record, compiled table, runtime state, boundary function, callback or task, visible behavior. That is the trace pattern for this course.

Now, I want to contrast FireRed briefly with a later or different architecture, because it sharpens the point. The research packet includes Pokémon Showdown data modules like `pokemon-showdown/data/abilities.ts`, chunk `chunk_d5bdb17c16d6f1a4`, where ability behavior can include functions such as move modification hooks. That is a TypeScript server simulator architecture. There, data and behavior can live close together as objects with callback-like properties. Showdown is excellent for deterministic battle modeling and testing, but it is not FireRed’s architecture. FireRed’s GBA runtime leans much more heavily on compiled C tables, globals, and engine functions. So when we use Showdown later as a comparison, do not import its architectural assumptions backward into FireRed. In FireRed, a header like `battle.h` and a build rule in the `Makefile` are often more revealing than a modern runtime data object would be.

Now, check for understanding. If I give you a generated species table and ask, “What must you inspect before trusting it?” what are the answers? First, inspect the struct or declarations that define the runtime shape. Second, inspect the generator or template that emits that shape. Third, inspect the Makefile rule that decides when the generated file is rebuilt. Fourth, inspect at least one consumer function that reads the fields. If you stop after reading the JSON, you have studied the authoring surface, not the game architecture.

Let’s take the bag as our second concrete example. Imagine an item record says an item can be used in battle and has an effect function. The inventory UI must display it, the field menu may allow or deny it, and the battle menu may route it into a turn action. The `bag.h` interface, chunk `chunk_8f2c0bdfc3224a33`, is where you start asking, “What are the legal entrances into this system?” But the item’s actual player-visible behavior depends on more than the header. It depends on item data, current runtime context, global state, task or callback flow, and the function that actually applies the effect. So again, header as map does not mean header as whole implementation. It means the header tells you which roads exist between modules.

Now, as advanced learners, I want you to notice a design pressure here. Header contracts are powerful, but they can also become too wide. If `battle.h` exposes every internal global and every helper function, then the rest of the codebase can start depending on battle internals. That makes future changes harder. On the other hand, if the header exposes too little, adapter modules have to duplicate knowledge or reach around the boundary. A mature C codebase often reflects a long negotiation between “hide internals” and “make the hardware-era game shippable.” FireRed is not a textbook microservice design. It is a production game codebase constrained by ROM, hardware, compiler behavior, and team workflow. So read the headers with sympathy for those constraints.

Board cue here: I am writing two phrases side by side. On the left: “human-facing contract.” On the right: “machine-facing contract.” A header is both. Human-facing, it tells programmers what concepts exist. Machine-facing, it tells the compiler how translation units agree. A generated file also has both sides. Human-facing, the JSON or template tells content authors what they can specify. Machine-facing, the generated C must match exact types and names. The Makefile ties the two together by making the machine-facing transformations repeatable.

This is also why `bin2c` matters in the week context. Even without diving into its full implementation today, the role is clear: binary embedding is another build-time bridge. Art, tiles, palettes, compressed assets, and other binary resources often need to become linkable symbols. Once embedded, the runtime does not know or care that the source began as a PNG, binary, or intermediate asset. It sees addresses, arrays, and symbols. Again: authoring form on the left, runtime contract on the right.

So when you build your lab generator, do not merely make it print a C array. Document the boundary. Write down what runs at build time: JSON parsing, validation, ordering, template rendering, file emission. Then write down what exists at runtime: the compiled array, the struct definition, the enum constants, and the functions that read them. That documentation is not busywork. It is the architecture map.

Let’s do a quick spoken trace, end to end. A designer changes a creature’s base Speed in JSON. The generator reads that JSON and emits a C initializer. The Makefile rule notices the generated output needs to be updated, or at least defines the path by which it can be updated. The compiler builds the generated table into the ROM. At runtime, the party creation code or battle stat calculation code reads the species record. Battle setup, like the adapter role visible in `pokefirered/src/battle_setup.c`, chunk `chunk_494d132a59422c85`, moves the game from overworld conditions into battle state. The battle module consumes battler and action contracts declared in `pokefirered/include/battle.h`, chunk `chunk_4079f42fde2b4370`. The main shell and tasks we covered last week keep dispatching frame behavior, but now the content of those frames is shaped by the generated data. That is the course thesis in motion.

And notice, we did not have to re-explain the startup loop to use it. That is what I mean by layering the course. Week one gave you the runtime spine. Week two asks what contracts and build-time transformations feed that spine. Later, when we get deeper into battle, inventory, maps, and save data, we will keep using this same trace pattern. We will ask: what data exists, what header exposes it, what source file owns the behavior, what globals or structs carry state, and what callback, task, or script advances it?

One final check before we move on. If someone says, “FireRed is hard to understand because there are so many globals,” what should you answer? You should not simply defend globals or condemn them. You should ask what contract the global participates in. Is it a battle-wide state variable declared for cross-module access? Is it private to one source file? Is it populated from generated data? Is it read by a task, a callback, or a script command? The architectural question is not “global bad?” The architectural question is “what runtime role does this shared state play, and where is that role declared?”

So the synthesis for this part is: headers tell us the shape of cross-module trust; build rules tell us how generated artifacts come to satisfy that trust; and runtime systems turn those trusted shapes into behavior. `battle.h`, `bag.h`, `battle_setup.c`, `build_tools.sh`, the `Makefile`, and `jsonproc.cpp` are not separate trivia items. They are different windows onto the same system. One shows battle’s public language. One shows inventory’s entry points. One shows an adapter between field and battle. One shows the project’s tool suite. One shows transformation dependencies. One shows template-driven generation. Together, they teach us that architecture is not only where the code runs. Architecture is also where the code is promised, generated, compiled, and finally consumed.

Now, let’s press one level deeper into that synthesis, because this is where many architecture readings either become precise or become vague. If I say, “the header is a contract,” that is true, but it is not yet useful enough. A contract between whom? For what kind of behavior? At what moment in the program’s life? And what is the cost when the contract is wrong?

So, on the board, I am going to draw a four-stage chain. First: authored source data or handwritten declarations. Second: build-time transformation. Third: compiled symbols and memory layouts. Fourth: runtime dispatch. And I want you to see that FireRed is constantly moving between these four stages. The mistake is to think “data-driven” means the fourth stage is always reading flexible data files. In this codebase, very often the data-driven decision was made before the cartridge ever boots. The data is generated, packed, named, declared, linked, and only then consumed by runtime systems that expect C arrays, constants, structs, and function signatures.

That is why the header is not just a convenience for the compiler. The header is the place where a module says, “Here is the shape by which the rest of the game may know me.” In `pokefirered/include/battle.h` chunk_4079f42fde2b4370, the public shape is large because battle is not a small feature. It is a whole operating environment inside the game. The field system, trainer setup code, item system, party system, battle controller logic, scripts, and UI all need a shared vocabulary for battle actions and battlers. That header is not merely saying, “Here are some functions.” It is saying, “When the rest of the engine talks about battle, these are the action codes, these are the shared globals, these are the expected structures, and these are the external entry points.”

Compare that to `pokefirered/include/bag.h` chunk_8f2c0bdfc3224a33. The bag header has a different architectural personality. It is still a contract, but the contract is more interface-oriented. It exposes how other systems can open or interact with the bag UI, how item use connects outward, and how callers enter inventory flows. Battle’s header reads like a broad shared language for a high-complexity runtime. Bag’s header reads more like a service boundary for a feature that many callers need but fewer callers should deeply own. That difference matters. Advanced architecture reading is not just counting declarations. It is noticing what style of relationship a header establishes.

Let me make that concrete. Suppose the player is walking in grass, a wild encounter begins, the party is loaded into battle state, the player later opens the fight menu, chooses a move, and then perhaps after battle uses an item from the bag. We can describe that as gameplay. But architecturally, we should be able to trace it as a sequence of contracts being activated. Somewhere outside battle, encounter and battle setup logic decides that battle should begin. `pokefirered/src/battle_setup.c` chunk_494d132a59422c85 is a useful source focus here because its include list already tells a story. It includes field, event, script, Pokémon, item, sound, save, battle, transition, and related headers. That file is an adapter zone. It is where the overworld’s conditions and script-driven events are translated into the battle engine’s expectations.

Now pause on that phrase, adapter zone. In a clean-room architecture diagram, you might draw overworld and battle as boxes, with an arrow between them. In the actual FireRed codebase, the arrow has substance. It has includes. It has globals. It has functions. It has constants. It has event flags. It has transitions and sound calls and save-related implications. And the header files tell you which names can cross that boundary without violating the project’s own language.

So if I ask you, “Where is the boundary between field gameplay and battle gameplay?” the beginner answer is, “When the screen transition happens.” That is player-visible and true at the UI level. But the architecture answer is more distributed. The boundary begins in the declarations that allow battle setup to speak to the systems it coordinates. It continues in the source functions that populate battle globals and choose a transition. It is enforced by the callback shell we studied last week, because once the game is in a different main callback mode, different per-frame behavior owns the machine. And it is reflected in the data structures battle expects when it begins.

Quick check for understanding. If I show you a C file with fifteen includes, should your first reaction be, “bad coupling”? Not automatically. The more careful reaction is, “What architectural role is this file playing?” If it is a low-level helper that claims to format a number as text, fifteen feature includes might be suspicious. If it is battle setup, fifteen includes may indicate that the file is intentionally coordinating a transition between major systems. Coupling is not a smell in the abstract. Undeclared or confused responsibility is the smell.

Now, let’s connect this to generated data. On the board, under the four-stage chain, I am going to write “JSON is not necessarily runtime.” That sentence is one of the central week-two ideas. In a modern web service, you may be used to JSON as something parsed at startup or request time. In this GBA decompilation context, JSON can be an authoring format. It can be a human-editable source that is consumed by a build tool and converted into C or binary assets. The runtime does not need a JSON parser just because the repository has JSON files. This is the distinction the lab is going to force you to document.

The build tooling gives us evidence. `pokefirered/build_tools.sh` chunk_1a4a3316f8593aaf identifies the project’s tool suite as part of the build environment. The `pokefirered/Makefile` chunk_30eb2e53e9597446 supplies generated data rules, which means the dependency graph itself records how authored inputs become compiled outputs. And `pokefirered/tools/jsonproc/jsonproc.cpp` chunk_dd905c2bfaba76d6 is the template-driven generator that makes this pattern explicit: structured data goes in, source-like output comes out, and the game later consumes the result as compiled program material.

That is the part I want you to bring into the lab. You are going to create a mini JSON-to-C table generator for creature species records. The point is not to worship JSON. The point is to learn to separate authoring convenience from runtime architecture. If your generator reads `species.json` and emits `species_info.h` or `species_info.c`, and then your gameplay code indexes `gSpeciesInfo[SPECIES_BULBASAUR]`, the runtime is not data-driven in the same way a server that parses JSON on demand is data-driven. It is table-driven. It is compile-time generated. It has strong static expectations after generation.

Let’s walk the example slowly. Imagine an input record for a species. It has an id, a display name, base HP, base attack, base defense, type one, type two, and a growth rate. At build time, your generator validates that the id exists, that type names map to known constants, and that every species has required fields. Then it writes a C initializer. Maybe it writes `{ .baseHP = 45, .baseAttack = 49, .type1 = TYPE_GRASS, .type2 = TYPE_POISON }`. At runtime, a battle calculation function does not ask, “Where is the JSON file?” It asks, “What is the base stat in this indexed table?” That difference changes the failure modes. A missing comma in JSON is a build failure. A bad index or mismatched enum is a compile or runtime logic failure. A malformed runtime save record is another category entirely.

Now, why do headers matter here? Because generated C still has to satisfy a declared shape. If your generated table is declared as an array of `struct SpeciesInfo`, then every consumer trusts that structure. The header is the architectural promise, the generator is the machinery that fulfills the promise, and the compiled table is the runtime object that gameplay consumes. If you alter the struct without altering the generator, you have broken the pipeline. If you alter the generator without altering the declaration, you may silently emit data that no consumer interprets correctly. This is why in larger C projects, generated code is architecture, not just tooling.

Let me draw another small board diagram. Left side: “authoring records.” Middle: “jsonproc-style generator.” Right side: “C tables and extern declarations.” Then below the right side: “battle, party, UI, scripts.” The downward arrow is the important one. Once the source exists in C form, it participates in the same linking and inclusion system as handwritten code. That is what makes the generated artifact a first-class member of the architecture. It is not a sidecar document.

Now, bring in the specific FireRed source focus. `pokefirered/tools/jsonproc/jsonproc.cpp` chunk_dd905c2bfaba76d6 supports the claim that this project uses template-driven source generation rather than treating JSON as a runtime dependency. I am being careful with language here. I am not saying every data item in FireRed begins as JSON, and I am not saying every generated artifact passes through one tool. I am saying this codebase contains build-time data processing patterns, and when we see JSON in that context, our first question should be, “What generated source or asset does this become?” not “Where is the runtime JSON parser?”

That also helps us interpret `bin2c`, which appears in the tool suite context for `pokefirered/tools`. A binary-to-C embedding tool is another form of boundary conversion. It takes something that may originate as a binary asset and turns it into a C representation that the linker can place into the ROM. Again, data-driven does not mean late-bound. It can mean the project authors manipulate data outside ordinary C editing, then convert it into stable compiled forms. The runtime gets fast indexed access. The build gets more complexity. That is a trade.

Here is the concrete tradeoff. Runtime JSON gives you flexibility after deployment, easier mod-style editing, and often better introspection during development. But on a Game Boy Advance cartridge, a runtime JSON parser would cost ROM, RAM, CPU, and engineering complexity. Compile-time generation shifts the flexibility earlier. You can still author in structured formats, but the shipped program receives compact tables, constants, and binary blobs. The architecture becomes less about parsing and more about layout, indexing, and contracts.

This is also why comparison with Pokémon Showdown must be handled carefully in this course. Showdown is valuable as a secondary comparison because it models battle behavior in a modern, dynamic TypeScript environment. For example, `pokemon-showdown/data/mods/gen2stadium2/scripts.ts` chunk_3480e574205d5d0f shows damage logic expressed with runtime objects, move properties, callbacks like damage callbacks, immunity checks, and conditional behavior around move fields. That is an excellent contrast point. In Showdown, a move or ability record can carry executable hooks in a way that fits a server simulator. FireRed’s architecture has different constraints and different idioms. So we can learn from the comparison, but we do not project Showdown’s runtime model backward onto the GBA engine.

Same with abilities in `pokemon-showdown/data/abilities.ts` chunk_d5bdb17c16d6f1a4. There, you can see ability behavior expressed through named event hooks such as modifying moves or reacting to damaging hits. That is a powerful, extensible event-style model. FireRed does not ask the same architectural question in the same way. FireRed is organized around C functions, tables, globals, scripts, callbacks, tasks, and build products. If you try to read FireRed as though every data record should carry arbitrary scriptable behavior, you will misread the codebase. If you read it as a constrained cartridge program that uses data tables and explicit engine paths, the structure becomes much clearer.

Let me ask a check question. Suppose you add a new creature species to the lab generator, and the generated C compiles, but the battle UI shows the wrong type icon. Where do you look? Do not say “the battle code” immediately. You trace the chain. Was the JSON authoring record correct? Did the generator map the type string to the right constant? Did the generated table line up with the enum order? Did the header declaration match the actual struct fields? Did the UI read the same field you think it read? Did an asset table map that type constant to the wrong icon? The debugging method follows the architecture map. Data record, generated output, declarations, globals or tables, runtime consumer, visible behavior.

This is one of the reasons I keep insisting that headers are maps. They are not complete maps. A header will not tell you every branch taken at runtime. But it tells you which names are meant to survive outside a translation unit. That is a huge clue. When a declaration appears in a header, the module is allowing other modules to depend on it. When a value remains `static` in a C file, the module is hiding it. When generated output creates a symbol that is declared elsewhere, the generator is participating in that visibility design.

Now let’s apply this to battle actions and battlers. In a battle system, there is a difference between “a battler exists,” “a party member exists,” and “a species exists.” A species record is a template, the party member is a saved or runtime creature instance, and the battler is that instance projected into battle state. Headers help us keep those roles separate. `pokefirered/include/battle.h` chunk_4079f42fde2b4370 is part of the contract for battle’s projection of creatures into an active combat context. The player’s Bulbasaur as saved party data is not identical to battler zero in the current battle. Battler zero has turn order, selected action, current move context, status interactions, side conditions, controller state, and UI obligations. That is not just “the Pokémon object.” It is a role inside the battle machine.

If you collapse those concepts, bugs become mysterious. You might ask, “Why not just read the species table every time?” Because species data does not tell you current HP after damage, volatile effects, chosen action, or which controller should emit commands. You might ask, “Why not just mutate the base species data when a move changes a stat?” Because base species data is a shared template. You would corrupt every creature of that species. So the architecture separates template data, instance data, and battle-role data. Headers mark where those layers can talk.

Now, notice how this connects to last week without redoing last week. We already studied the callback spine and task dispatch. Today we are saying: those runtime schedulers do not float in isolation. They consume declarations and data shapes established elsewhere. A callback can only call functions it can see. A task function can only manipulate state whose layout it understands. A script command can only trigger behavior that has an exposed interpreter path. So the week-one runtime shell gives motion to the week-two contracts. Headers and generated data provide the shapes; callbacks and tasks provide the frame-by-frame execution.

Let me give you a spoken debugging scenario. A player selects an item from the bag during a field flow. The wrong callback resumes afterward, or the game returns to the wrong menu. In a beginner mindset, you search for “bag bug” and start reading UI code. In an architecture mindset, you open `pokefirered/include/bag.h` chunk_8f2c0bdfc3224a33 and ask, “What entry points does the bag expose? Does the caller supply a return callback? Does the bag own the next screen, or does it call back into the previous context?” Then you inspect the source that uses that interface. If the bag is acting as a modal UI feature, its header often reveals how control enters and how control is expected to leave. That is the contract. The bug is often a broken promise about ownership of continuation.

That word continuation is worth keeping. In FireRed, many flows are continuation-heavy, even without using that academic term in the code. You enter a UI, give it a callback or mode, let it run over multiple frames, and eventually it returns to some prior context. Week one gave you the scheduling model for that. Week two gives you the declaration model that lets modules safely hand those continuations around. Battle setup is another continuation problem: field gameplay is interrupted, battle takes over, and later the game resumes or branches depending on outcome.

Now, when we get to scripts later in the course, this same idea will reappear in a different costume. A script command may look like a tiny bytecode instruction, but it often triggers large C-side behavior. A field script says, in effect, “Start this battle,” or “Give this item,” or “Open this message box.” The script VM does not contain all the domain logic. It calls into systems whose headers define their callable surfaces. So the path from script to visible behavior travels through the same architecture map: script command, C function, shared state, callback or task, UI or engine behavior. You are learning to trace that path now so that later, when the script layer is added, it does not feel magical.

Let’s return to build rules for a moment. The `pokefirered/Makefile` chunk_30eb2e53e9597446 matters because architecture is also dependency order. If generated data must exist before compilation, the build graph encodes that fact. If an asset must be converted before linking, the build graph encodes that too. In a mature engineering sense, “the architecture” includes those transformations. You cannot understand the runtime shape without understanding how the runtime artifacts came into existence.

Here is a simple way to test yourself. When you see a table in C, ask three questions. Is it handwritten, generated, or converted from a binary asset? Who declares its shape? Who consumes it at runtime? If you can answer those three questions, you are reading architecturally. If you can only say, “This is an array,” you are reading syntactically.

For the lab, I want you to make those questions explicit in your documentation. Do not merely say, “The generator outputs C.” Say what runs at build time: JSON parsing, validation, constant mapping, template emission. Then say what exists at runtime: compiled arrays, enum constants, struct layouts, and functions that index those arrays. Then say what does not exist at runtime: no JSON parser, no text keys, no dynamic schema validation, unless you intentionally add them. That last sentence is not pedantry. It is the lesson.

Let’s also talk about failure visibility. Build-time generation improves some failures because they happen earlier. If your JSON has an unknown type, a good generator can fail the build with a direct message. But it can also hide failures if generation is too permissive. If the generator silently emits `TYPE_NORMAL` for an unknown string, you now have a semantic bug that looks like a gameplay bug. The architecture is only as good as the contract enforcement. Headers enforce some things through C types. Generators enforce other things through validation. Tests, which we will talk more about as the course develops, enforce behavior across the boundary.

This is where Showdown again gives us a useful contrast, especially for deterministic battle testing. Modern simulator data such as `pokemon-showdown/data/mods/gen4/items.ts` chunk_ef574f63c118f2f6 can encode item behavior with hooks that modify damage or base power. The runtime engine can test those hooks in a high-level environment. FireRed’s C architecture makes many of those behaviors more explicit and dispersed. That does not mean less architectural discipline. It means the discipline lives in tables, switches, constants, function boundaries, and build artifacts. Different platform, different expression of the same design pressure: represent a large ruleset without turning every rule into untraceable special-case chaos.

So, if you are taking notes, capture this principle in your own words: data-driven architecture is not a single technology choice. It is a relationship among authoring format, transformation pipeline, declared shape, and runtime consumer. JSON is one possible authoring format. C tables are one possible runtime representation. Headers are one way to publish the shape. Make rules are one way to enforce the transformation order. Tasks, callbacks, and scripts are ways to animate the resulting state. FireRed uses all of those layers.

Now I want to do one more board exercise before we move on. Imagine a line with these labels: species record, generated species table, party instance, battle battler, selected action, battle animation, field resume. I am going to circle “generated species table” and “battle battler.” These are not adjacent by accident. The species table helps initialize or compute properties for creatures, but once battle is underway, the battler state is the active runtime role. Then I draw arrows from `battle.h` to “battle battler” and from the Makefile and jsonproc-style tooling to “generated species table.” That drawing is the architecture map. No single file contains the whole drawing, but the files we are studying let us reconstruct it.

Check yourself here: if a new move has the wrong base power in battle, which part of the line do you inspect first? If the generated move table contains the wrong number, the runtime may be innocent. If the table is correct but the battle calculation applies a modifier incorrectly, the data is innocent. If the UI displays one value while calculation uses another, you may have two consumers with different assumptions. Architecture tracing prevents blame from becoming random.

And this is also why I do not want you to memorize FireRed as a pile of filenames. I want you to learn a method. The method is: find the public contract, identify the generated or handwritten data source, locate the runtime owner, trace the dispatch mechanism, and then connect it to player-visible behavior. We used that method last week with callbacks and tasks. Today we are adding headers and build products. Later, we will add scripts, save blocks, map objects, and battle controllers in more detail.

One last transition before the next part. Up to now, we have been treating headers mostly as maps of who may call whom and who may share what state. Next, we are going to look at a subtler issue: headers also reveal which concepts the codebase treats as stable enough to name globally. A globally named concept is expensive. Once many modules depend on it, changing it is no longer a local edit. That is why constants, structs, and extern declarations in headers are architectural commitments. In a small program, that sounds obvious. In a creature RPG, with battle, bag, scripts, maps, save data, and generated assets all leaning on shared names, it becomes one of the central forces shaping the whole engine.

So let’s slow down on that word, stable. A stable concept in this codebase is not necessarily a concept that never changes during execution. It is a concept that the source tree has decided many modules are allowed to know by name. That is the important distinction. The player’s current HP is not stable as a value. It changes constantly. But the idea that a battler has HP, that a battle has battlers, that battlers have actions, that bag screens can return to a caller, that generated data is compiled into addressable tables, those are stable architectural ideas. They become names. Once they become names in headers, they become shared vocabulary.

If I were standing at the board, I would write this as a chain: private implementation detail, file-local helper, module interface, global engine concept. And then I would draw a big warning arrow between module interface and global engine concept. Not because global engine concepts are bad. You cannot build FireRed without them. The warning is that they carry cost. A local helper can be renamed or rewritten with minimal blast radius. A global concept in a header has consumers you may not immediately remember. So when we read a header, we are not just reading declarations. We are reading the list of ideas the engine has decided to make public.

This is why pokefirered/include/battle.h, chunk_4079f42fde2b4370, is so valuable for us this week. The research packet labels that chunk around the battle action and battler contract, and that phrasing is exactly right. The battle engine is not merely a pile of functions. It has a public language for battlers, actions, battle state, and the pieces that other modules are allowed to reference. A battler is not just a sprite on the screen. It is a participant in turn order, targeting, controller dispatch, party ownership, HP bookkeeping, status effects, UI presentation, and script consequences after the battle ends. The header tells us which of those ideas have escaped the private implementation and become part of the shared engine vocabulary.

Now compare that with pokefirered/include/bag.h, chunk_8f2c0bdfc3224a33. The bag is easy to underestimate because, as players, we experience it as a menu. But architecturally, the bag is a callable subsystem. It can be entered from the field. It can be entered from battle in constrained ways. It must know enough about pockets, items, selection behavior, and exit behavior to return control to the right place. So the bag header is not simply saying, “Here are some menu functions.” It is saying, “Here is the interface by which the rest of the game may treat inventory UI as a service.” That is a much stronger architectural statement.

Let’s make that concrete. Suppose the player is in a wild battle and chooses an item. The visible story is simple: open bag, select potion, heal Pokémon, return to battle. The source story crosses several boundaries. The battle code must expose enough state for the bag flow to know it is being used in battle rather than from the overworld. The item system must expose what a potion is and what it can target. The Pokémon party model must expose mutable creature records. The UI must render pockets and choices. Then control has to return to the battle callback path rather than to the field callback path. No single source file owns that whole story. The only way the story can be coherent is if the public names in headers agree on the shape of the interaction.

And here is the key architectural lesson: the header does not contain the whole behavior, but it contains the commitments that make the behavior composable. If you add a new item effect and only edit the item implementation, you may not have changed the architecture. If you add a new kind of bag entry point, or a new battle action category, or a new globally visible data structure, you probably have changed the architecture. That is the level of distinction I want you to carry into the week two homework and quiz. At this point in the course, saying “the battle system calls the bag” is too vague. I want you to be able to say, “The battle flow and bag flow meet through public contracts declared in headers such as pokefirered/include/battle.h, chunk_4079f42fde2b4370, and pokefirered/include/bag.h, chunk_8f2c0bdfc3224a33, while the concrete transition code lives in source modules that include those contracts.”

We can see this boundary pressure very clearly in pokefirered/src/battle_setup.c, chunk_494d132a59422c85. The excerpt in the packet shows battle setup including a long list of headers: task, overworld, item, pokemon, load_save, safari_zone, quest_log, script, event_data, movement, field effects, battle, battle_transition, and battle controllers. That include list is not random noise. It is an architectural fingerprint. Battle setup sits at a crossing point. It has to understand enough about the overworld to leave it, enough about scripts and event data to know why the battle is happening, enough about Pokémon data to initialize participants, enough about tasks and callbacks to transfer execution, and enough about battle transition and controllers to enter the battle runtime. The include list is the map of those obligations.

Pause here and check your understanding. If I ask, “Where is the battle system?” the advanced answer is not one file. The advanced answer is a set of contracts and flows. Battle state has public declarations in battle headers. Battle entry is coordinated by setup code. Per-frame control is connected to the callback and task shell we studied last week. Battle participants are backed by party and species data. Visual entry is routed through transition code. Scripts or map events may be the reason the battle began. That is the architecture. The system is not only where the damage formula lives.

Now, let’s connect this to generated data, because this is the second half of today’s lecture and the bridge into our next meeting. FireRed is data-driven, but not in the modern runtime-JSON sense. Some of the source data may begin in JSON-like or asset-like formats. Some build tools may transform structured input into C. But by the time the game is running on the target, those records are compiled tables, embedded binaries, constants, and arrays. The runtime is not wandering around parsing JSON files every frame. That distinction is why the week context pairs headers with generated data and build-time pipelines.

Look at the toolchain pieces in the packet. pokefirered/build_tools.sh, chunk_1a4a3316f8593aaf, is part of the tool suite story. pokefirered/Makefile, chunk_30eb2e53e9597446, is called out for generated data rules. pokefirered/tools/jsonproc/jsonproc.cpp, chunk_dd905c2bfaba76d6, is identified as JSON and template-driven source generation. These are not runtime modules in the same sense as battle setup or bag UI. They are part of the architecture because they decide what the runtime source will look like after generation. That means the architecture exists in two time zones: build time and runtime.

Let me say that again because it is one of the central ideas of the week. A creature RPG’s architecture is not only what happens after the title screen. It includes the pipeline that turns author-friendly data into machine-friendly code and assets. A species record can be edited in a data form, transformed by a generator, compiled into a table, referenced through declarations, and finally consumed by battle, summary screens, evolution logic, learnsets, encounter logic, or UI. If you only inspect the runtime C, you see the final table. If you only inspect the JSON or source data, you see the authoring format. The architecture is the relationship between those stages.

On the board, I would write a small trace. Authoring record. Generator. Generated C or data include. Header declaration. Compiled object. Runtime global table. Gameplay function. Player-visible behavior. That is the week two version of the trace we practiced in week one. Week one gave us the callback and task spine, so we knew how the engine keeps moving. Today we are adding the source-contract and build-pipeline layer, so we know how named data and named interfaces enter that moving engine.

Here is a concrete example using the lab idea. In the workshop, you are creating a mini JSON-to-C table generator for creature species records. Imagine your JSON has a species name, base HP, base attack, elemental types, and catch rate. The generator emits a C table. A header exposes either the struct shape, the external table declaration, or both, depending on how you design it. At runtime, the battle code does not ask, “Where is the JSON file?” It asks, “Given this species id, what are the compiled stats and types?” That means your generator is not a convenience script off to the side. It is part of the contract. If you change the JSON schema but not the template, build breaks. If you change the generated struct but not the consumers, compile breaks or, worse, behavior breaks. If you change the header, you are changing what other modules are allowed to believe about species data.

This is also why bin2c-style embedding matters. The packet points us toward pokefirered/tools as a recurring module, including bin2c files. The exact technical detail can vary by asset, but the architectural pattern is stable: binary assets can be converted into C-addressable data, compiled into the ROM, and then referenced by runtime code. That gives the runtime simple access to bytes while preserving an authoring pipeline outside the game. Again, data-driven does not mean runtime-flexible in the way a web service might be runtime-flexible. On the Game Boy Advance, data-driven often means the game behavior is shaped by tables and assets that were generated before the ROM ever booted.

Now, one quick comparison to sharpen the point. In Pokémon Showdown, which our research packet uses as a secondary comparison source, you often see data objects with behavior hooks attached directly in TypeScript. For instance, pokemon-showdown/data/abilities.ts, chunk_d5bdb17c16d6f1a4, shows ability definitions with callback-style behavior such as modifying a move or reacting to a hit. pokemon-showdown/data/mods/gen2stadium2/scripts.ts, chunk_3480e574205d5d0f, shows battle damage logic inspecting move properties and invoking move-specific callbacks. That is a very different runtime environment. It is still data-driven, but the data and behavior relationship is expressed through TypeScript objects and functions in a server-style simulator architecture. FireRed’s C codebase, by contrast, leans heavily on compiled data, headers, globals, generated assets, and engine-specific dispatch. Both are serious architectures. They just place the contracts in different forms.

So when we use Showdown later in the course, especially for deterministic battle tests and modern battle-data modeling, do not accidentally import its architecture backward into FireRed. FireRed is not trying to be a live TypeScript rules engine. It is a ROM-oriented C program with build-time transformation and runtime tables. That difference should change how you read the source. In Showdown, you may ask, “Which callback on this data object handles the rule?” In FireRed, you more often ask, “Which generated table, which header declaration, which global state, and which callback or task path cooperate to produce this behavior?”

Let’s do a comprehension check. Suppose I ask you to add a new creature field, let’s say a “habitat temperature preference,” purely for a Pokédex-style screen. Where should your first design question be? Not “Where do I draw the text?” The first question is, “Is this runtime state, generated species data, save data, or a derived UI-only value?” If it is generated species data, then you need to touch the authoring format, the generator or template, the generated table, the struct or declarations exposed through headers, and the UI consumer. If it is save data, now persistence enters the picture, and the blast radius is different. If it is just derived UI text, maybe it should not become a global field at all. That judgment is architecture.

Another check. Suppose you want a new battle action, maybe something like “scan,” where the player can inspect a wild creature without using an item. If you treat that as a local battle menu hack, you will probably get stuck. A battle action is a public concept in the battle contract. It may affect input menus, action selection, controller behavior, turn resolution, scripts, UI messages, and possibly link battle compatibility. That is why the battle action material in pokefirered/include/battle.h, chunk_4079f42fde2b4370, matters. The header tells you that battle actions are not just menu labels. They are values carried through the battle engine.

Let me pull back for a recap. Last week, we built the runtime picture: the main callback slots, the task array, and the per-frame dispatch model. We are not re-teaching that today, but we are using it. This week, we are asking what feeds that runtime picture and what names let modules cooperate. Headers are the public maps. Generated data is the compiled terrain. Build tools are the machinery that shapes the terrain before runtime. Source functions are the roads the execution actually walks. A strong FireRed trace needs all four.

So if a player sees a Pokémon’s move animation, or opens the bag, or triggers a trainer battle, we can now ask more precise questions. Which data record existed before runtime? Which generated table or embedded asset did the build produce? Which header made that table or function visible? Which global state carried the current mode? Which callback or task advanced the frame? Which script or setup function initiated the transition? Which concrete source function performed the visible operation? That is the level of tracing this course is moving toward.

And this connects directly to the overall course plan. Weeks one and two are the foundation: runtime shell, source contracts, and build-time data. After this, when we move deeper into overworld maps, event scripting, creature data, battle controllers, inventory, UI, save blocks, and link systems, we will keep reusing the same question pattern. We are not collecting trivia about files. We are building a method for reading a commercial-scale creature RPG as cooperating state machines and contracts. The headers tell us what the codebase has chosen to name globally. The generated files tell us what the build has frozen into the ROM. The callbacks and tasks tell us how control moves over time. The scripts tell us how content requests engine behavior. The structs tell us what shape the game believes its world has.

So here is the synthesis I want you to leave with today. A header is an architecture map because it records dependency permission. It says, “This concept may be known outside its home.” That permission is powerful. It is how battle setup can coordinate overworld state, scripts, Pokémon data, item behavior, transitions, and controllers, as we saw from pokefirered/src/battle_setup.c, chunk_494d132a59422c85. It is how the bag can be a reusable subsystem rather than a one-off menu, as indicated by pokefirered/include/bag.h, chunk_8f2c0bdfc3224a33. It is how battle actions and battler state become shared engine language, as indicated by pokefirered/include/battle.h, chunk_4079f42fde2b4370. And it is how generated data becomes ordinary runtime C data after tools such as the ones represented by pokefirered/build_tools.sh, chunk_1a4a3316f8593aaf, pokefirered/Makefile, chunk_30eb2e53e9597446, and pokefirered/tools/jsonproc/jsonproc.cpp, chunk_dd905c2bfaba76d6, have done their work.

Next time, we will sharpen the phrase “data-driven,” because it is one of those phrases that can either clarify everything or hide all the important engineering. For FireRed, data-driven does not mean the runtime is dynamically interpreting loose JSON. It means authored data, generated source, compiled tables, and stable headers cooperate with a fixed engine. Keep that distinction in your notes, because it will save you from a lot of wrong mental models when we start reading the creature data and map data more closely.

## Source References
- pokemon-showdown/data/mods/gen2stadium2/scripts.ts
- pokemon-showdown/data/abilities.ts
- pokefirered/src/battle_setup.c
- pokemon-showdown/data/mods/gen4/items.ts
- pokemon-showdown/data/mods/gen9ssb/moves.ts
- pokemon-showdown/data/text/moves.ts
