The ROM map
This page maps the FireRed/LeafGreen cartridge ROMs: images, function tables, script readers, the species table, the French Easy Chat vocabulary and the twelve international builds. Addresses are French FireRed (BPRF, version 0x0A) unless a table says otherwise; LeafGreen’s are on LeafGreen. pokeldn/frlg/rom/rom_map.py records how each address was obtained and tests/test_rom_map.py checks it against console dumps.
The cartridge image
The Switch release carries the GBA ROM as the only file in its RomFS.
| title id | RomFS file | size | sha1 |
|---|---|---|---|
| 01004B3023412000 | /FireRed_f.gba | 16777216 | 07566b82dbd2a91321698f730f6400ae4c56ddf1 |
| 010087C02342E000 | /LeafGreen_f.gba | 16777216 | 9f774956dfbad7f69ddb91fb91d2c26e54408f75 |
| 0100554023408000 | /FireRed_e.gba | 16777216 | baa452d0b24629dd7782cfc07a8984085dde1311 |
| 010034D02340E000 | /LeafGreen_e.gba | 16777216 | 62b9fc77549dbc67032eb6cbd0ea6ad3b825690f |
The header at 0xA0 reads POKEMON FIRE BPRF and POKEMON LEAF BPGF, version 0x0A, checksum valid. The French FireRed 1.0.1 update leaves the guest ROM unchanged apart from the wrapper’s three load-time patches (frlg_rom.md, The breakpoint hooks); 0x09000000..0x0A000000 reads open bus (each halfword its own address halved). The English pair (BPRE, BPGE) is byte-identical to pret/pokefirered’s pokefirered_switch.gba and pokeleafgreen_switch.gba.
scratchpad/base_romfs.py reads the Program NCA’s single CTR RomFS section:
./.venv/bin/python scratchpad/base_romfs.py "$NSP" --list
./.venv/bin/python scratchpad/base_romfs.py "$NSP" --extract /FireRed_f.gba --out scratchpad/FireRed_f.gba
EWRAM is at the same addresses in both builds
The English ELF’s EWRAM symbols are the French cartridge’s (gDecompressionBuffer 0x0201C000, gPlayerParty 0x02024280, gPlayerPartyCount 0x02024025). IWRAM differs. No symbol claims 0x0203FBAC (highest symbol end) to 0x02040000 (scratchpad/ram_survey.py).
The English cartridges
pokeldn/frlg/rom/builds.py holds one table per cartridge (BPRF, BPGF, BPRE, BPGE, BPRS, BPGS, BPRD, BPGD, BPRI, BPGI, BPRJ, BPGJ): IWRAM globals, the ROM functions payloads, hooks, stubs and call-chain use, and the data pointers gifts carry. The host picks a table from the game code in MysteryGiftLinkGameData [mystery_gift.c:369] before anything address-dependent is sent; a console with no table, or one excluded by --console-build CODE or --version, is refused with nothing sent.
English addresses come from the ELFs, each checked against the retail image.
| region | French to English |
|---|---|
| EWRAM | unchanged |
| IWRAM 0x03000000..0x03001B6F | unchanged |
IWRAM 0x03002370..0x0300602F English, from gMain (0x030022D0 French, 0x03002380 English) | +0xB0 |
IWRAM 0x03006090..0x03007583 English, gSoundInfo (0x03005F80 French) to gFlash | +0x110 |
| IWRAM 0x03007590 and up: the stack, the interrupt vector | unchanged |
| ROM | no single offset; every function is looked up |
English values: gRngValue 0x030042D0, gSaveBlock1Ptr 0x030042D8, gSaveBlock2Ptr 0x030042DC, gIntrTable[4] 0x030027E0, gLastWrittenSector 0x03004650, gSaveCounter 0x03004660. English LeafGreen’s RAM is English FireRed’s; of a resident hook’s addresses only m4aSoundMain moves (0x081E07C4 against 0x081E07E8). tests/test_frlg_english_cartridges.py runs the payloads through each image’s Client_RunBufferScript (skipped without scratchpad/frlg_en/).
The BIOS wrappers
libagbsyscall.s links as THUMB svc N ; bx lr pairs in the decomp’s order, 0x24 lower on LeafGreen:
| wrapper | svc | FireRed | LeafGreen |
|---|---|---|---|
ArcTan2 | 0x0A | 0x081E21D4 | 0x081E21B0 |
BgAffineSet | 0x0E | 0x081E21D8 | 0x081E21B4 |
CpuFastSet | 0x0C | 0x081E21DC | 0x081E21B8 |
CpuSet | 0x0B | 0x081E21E0 | 0x081E21BC |
Div | 0x06 | 0x081E21E4 | 0x081E21C0 |
LZ77UnCompVram | 0x12 | 0x081E21E8 | 0x081E21C4 |
LZ77UnCompWram | 0x11 | 0x081E21EC | 0x081E21C8 |
The game’s LZ77 streams are VRAM-safe and match gbagfx output. LZ77 does not shrink payload code; it helps only data over 1024 bytes.
The first anchor
anchors returns 0x08148C75, the return address in Client_RunBufferScript [mystery_gift_client.c:274]. Its literal pool:
0x08148C88 -> 0x0201C000 gDecompressionBuffer
0x08148C8C -> 0x0300422C &gSaveBlock2Ptr
0x08148C90 -> 0x03004228 &gSaveBlock1Ptr
MysteryGiftClient_CallFunc at 0x08148C94 indexes sClientFuncs (0x0845DBD0, eight entries) by client->funcId. Every other address is reached from literal pools, bl targets and pointer tables, each landing on a known prologue (scratchpad/rom_read.py).
The four function tables
gScriptCmdTable 0x08163650 214 entries the field script commands
gSpecialVars 0x081639A8 21 entries
gSpecials 0x081639FC 444 entries gSpecialsEnd 0x081640EC
gStdScripts 0x081640EC 10 entries
gMysteryEventScriptCmdTable 0x081DE144 17 entries
script_data runs from 0x08163650 to 0x081DE188, where lib_text (libgcnmultiboot) starts [ld_script_rev10.ld:318-330].
gSpecialVars: twelve words each 2 above the last (gSpecialVar_0x8000= 0x020370B4, anEWRAM_DATAglobal) [event_data.c:16].-
gScriptCmdTable: 214 THUMB pointers; entries 0 and 213 are bothScrCmd_nop.pokeldn/frlg/rom/scrcmd_names.pynames every entry (scrcmd_names.handler("additem")).0x23 callnative 0x0806D854 0x44 additem 0x0806DED0 0x25 special 0x0806D7EC 0x79 givemon 0x0806F834 0x29 setflag 0x0806E0EC 0x90 addmoney 0x0806F998 gSpecials: 444 entries fromScrCmd_special’s literal pool [scrcmd.c:101], called through 0x081E2224; 171 areNullFieldSpecial.rom_map.SPECIAL_ADDRESSESholds them androm_map.special_function("HealPlayerParty")resolves one.gStdScripts: ten words into 0x081A76xx..0x081AB5xx.gMysteryEventScriptCmdTable: found throughsMysteryEventScriptContext(0x0203AA38), whose table pair sits at +0x5C/+0x60 [mystery_event_script.c:27,52] (table-scan --table-delta 0x44 --table-runlen 2over EWRAM, after a Mystery Event gift in the same boot).
| # | command | handler | # | command | handler | |
|---|---|---|---|---|---|---|
| 0 | nop | 0x080DE451 | 9 | givenationaldex | 0x080DE61D | |
| 1 | checkcompat | 0x080DE401 | 10 | addrareword | 0x080DE641 | |
| 2 | end | 0x080DE3F5 | 11 | setrecordmixinggift | 0x080DE66D | |
| 3 | setmsg | 0x080DE465 | 12 | givepokemon | 0x080DE681 | |
| 4 | setstatus | 0x080DE455 | 13 | addtrainer | 0x080DE78D | |
| 5 | runscript | 0x080DE49D | 14 | enableresetrtc | 0x080DE7D5 | |
| 6 | initramscript | 0x080DE5B5 | 15 | checksum | 0x080DE7E9 | |
| 7 | setenigmaberry | 0x080DE4B9 | 16 | crc | 0x080DE831 | |
| 8 | giveribbon | 0x080DE581 |
From a table entry to the function behind it
A handler’s bl targets in address order are the decomp’s calls in source order [scrcmd.c:463-590]:
ScriptReadHalfword 0x0806D1E8 AddBagItem 0x0809DA70
VarGet 0x08071DDC RemoveBagItem 0x0809DBC4
GetVarPointer 0x08071CC8 CheckBagHasSpace 0x0809D9EC
FlagSet 0x08071EF4 CheckBagHasItem 0x0809D92C
FlagClear 0x08071F1C AddPCItem 0x0809DDB4
FlagGet 0x08071F44 IncrementGameStat 0x080587A4
gSpecialVar_Result is 0x020370CC; Random is 0x080486B0. scratchpad/handler_workers.py prints a handler’s bl targets; tools/frlg/rom_functions.py --table specials|field|mystery-event|callable does it across a table and plans the next --dump-address windows.
The ROM is agbcc-built and ends a THUMB function pop {r4,r5,r6}; pop {r1}; bx r1 (BC70 BC02 4708), never pop {..., pc}. pokeldn/frlg/rom/thumb.py matches bx Rn and treats a return as the end of a block only when a prologue follows.
Naming 300 workers offline
scripts/gen_worker_names.py zips every dumped body against the decomp and writes pokeldn/frlg/rom/worker_names.py, under four checks:
| check | what it rules out |
|---|---|
| length | inlining, __umodsi3, a macro read as a call |
| anchor | a misaligned body: every measured address must land back on its own name |
| agreement | a target named differently by two callers |
| link order | a name in the wrong place in the ROM (longest ascending chain kept) |
A gap between anchors holding one unnamed target and one source call is forced. Reading the source:
firered_switchisGAME_REVISION=10 MODERN=0[Makefile:227]: the#if REVISION >= 0xAblocks are live.VarGet(ScriptReadHalfword(ctx))isbl ScriptReadHalfwordthenbl VarGet.ScriptReadByteis a macro and emits nobl[include/script.h:24].NDEBUGholds: no assert calls.
rom_map name | the decomp | bodies agreeing |
|---|---|---|
GET_MON_DATA | GetMonData3 | 15 |
SCRIPT_CONTEXT_SET_NATIVE | SetupNativeScript | 11 |
SET_RESPAWN | SetLastHealLocationWarp | 1 |
SCRIPT_MOVEMENT_START | ScriptMovement_StartObjectMovementScript | 2 |
CHANGE_AMOUNT_MONEY_BOX | ChangeAmountInMoneyBox | 2 |
ME_CHECK_COMPATIBILITY / ME_SET_INCOMPATIBLE | CheckCompatibility / SetIncompatible | 1 / 3 |
GetMonData2 aliases GetMonData3 [pokemon.c:2970]. 0x0806D0EC is StopScript(ctx) [script.c:76]; ScriptContext_Stop(void) [:360] is 0x0806D418.
Two mixed-image hazards
- Never read two cartridges’ dumps as one image: LeafGreen keeps this code −0x2C away, and a mixed image misnames calls.
script_read.every_dumptakes one cartridge. - Read every dump at once:
scrcmd.Memorymerges overlapping dumps (tools/frlg/script_read.py --with-every-dump).
What is measured
Every body behind every table is read: 213 field commands, 17 Mystery Event opcodes, 27 callable functions, 272 specials. NullFieldSpecial is bx lr at 0x080CE8DC; GetLeadMonIndex is 0x080CE818; DoDiveWarp 0x08081CC8, DoFallWarp 0x08081DA0.
The Mystery Event VM’s workers:
| worker | address | called by |
|---|---|---|
StringExpandPlaceholders | 0x0800CADC | the last call of every handler that leaves a message |
RunScriptImmediately | 0x0806D438 | runscript |
InitRamScript | 0x0806D5F0 | initramscript |
GiveGiftRibbonToParty | 0x080A43B0 | giveribbon |
EnableRareWord | 0x080C1658 | addrareword |
CheckCompatibility | 0x080DE300 | checkcompat |
SetIncompatible | 0x080DE330 | checkcompat’s else |
memcpy | 0x081E44F4 | addtrainer |
CalcCRC16 | 0x080489A0 | crc |
StringCopyN | 0x0800C8CC | setenigmaberry, givepokemon |
StringCompare, SetEnigmaBerry | 0x0800C938, 0x080A01B0 | setenigmaberry |
VarSet | 0x08071DF8 | setenigmaberry; no ScrCmd calls it |
SpeciesToNationalPokedexNum | 0x08046994 | givepokemon |
GetSetPokedexFlag | 0x0808C860 | givepokemon |
ItemIsMail, GiveMailToMon2, CompactPartySlots | 0x0809BB18, 0x0809B964, 0x080971FC | givepokemon |
The call veneers are four bytes each from _call_via_r0 at 0x081E2224 (r4 0x081E2234, r6 0x081E223C).
Save backup and restore
LoadGameSave [save.c:803] and gRfu.sendQueue.count [link_rfu_2.c:3131] on every cartridge. LoadGameSave opens 70 b5 00 06 and pools gDecompressionBuffer; the queue count is read by the leaf ldr r0, =gRfu; ldr r1, =0x8D2; adds r0, r0, r1; ldrb r0, [r0]; bx lr.
| cartridge | LoadGameSave | gRfu | gRfu.sendQueue.count |
|---|---|---|---|
BPRE | 0x080DDBF4 | 0x03005590 | 0x03005E62 |
BPGE | 0x080DDBC8 | 0x03005590 | 0x03005E62 |
BPRF | 0x080DDFD4 | 0x030054E0 | 0x03005DB2 |
BPGF | 0x080DDFA8 | 0x030054E0 | 0x03005DB2 |
BPRD, BPRI | 0x080DDF14 | 0x030054E0 | 0x03005DB2 |
BPGD, BPGI | 0x080DDEE8 | 0x030054E0 | 0x03005DB2 |
BPRS | 0x080DDFFC | 0x030054E0 | 0x03005DB2 |
BPGS | 0x080DDFD0 | 0x030054E0 | 0x03005DB2 |
BPRJ | 0x080DED68 | 0x03005520 | 0x03005DF2 |
BPGJ | 0x080DED3C | 0x03005520 | 0x03005DF2 |
sSaveSlotLayout agrees on all twelve except sector id 4: 3816 bytes on the Latin cartridges, 3776 on the Japanese ones.
Reading the console’s scripts
scripts/gen_scrcmd_args.py generates operand widths from the decomp’s macros into pokeldn/frlg/rom/scrcmd_args.py; memory-dump plus scrcmd.disassemble then reads any script:
0x081A7624 6A lock
0x081A7625 5A faceplayer
0x081A7626 67 message 0x00000000
0x081A762B 66 waitmessage
0x081A762C 6D waitbuttonpress
0x081A762D 6C release
0x081A762E 03 return
This is Std_MsgboxNPC. A walk from each gStdScripts entry ends on a terminator at the byte before the next.
The conditional macros
| command | width | structure |
|---|---|---|
applymovement | 7 bytes | .ifb \map branch |
waitmovement, removeobject, addobject | 3 bytes | .ifb \map branch |
applymovementat, waitmovementat, removeobjectat, addobjectat | 9, 5, 5, 5 bytes | alternate branch |
warp and the eight other warps | 8 bytes | formatwarp sub-macro |
the ten buffer* commands | 4 bytes | stringvar sub-macro |
showobjectat, hideobjectat, resetobjectsubpriority | 5 bytes | map sub-macro |
trainerbattle | 6 + 4..16 bytes | type-dependent branch |
A macro is a command macro only when every route starts with its own opcode byte and reaches no other command macro (giveitem is two instructions). trainerbattle is scrcmd_args.VARIABLE; an unknown type makes scrcmd.shape answer None. goto is a terminator alongside end and return. The 266-byte trainer-battle region 0x081A7699..0x081A77A3 is a fixture in tests/test_script_cmd_table.py.
Naming the operands
tools/frlg/script_read.py DUMP.bin --base ADDR:
0x081A76A8 4F applymovement 0x800F (VAR_LAST_TALKED), 0x081A77B0
0x081A76B2 26 specialvar 0x800D (VAR_RESULT), 0x0036 (Script_HasTrainerBeenFought)
0x081A76BC 06 goto_if 0x05 (!=), 0x081A76CD
0x081A76C2 25 special 0x0038 (PlayTrainerEncounterMusic)
- An operand of 0x4000 or more is a variable reference in any command [event_data.c:214,235].
pokeldn/frlg/rom/symbol_names.pyhas the 274 var and 1470 flag names. scrcmd_args.PARAMSnames each operand, which picks the table (specialu16 intogSpecials,callstdu8 intogStdScripts).- Conditions are <, =, >, <=, >=, != [scrcmd.c:65].
Following a script to what it reaches
scrcmd.follow chases every goto, call, goto_if and call_if and collects unheld addresses; data pointers are reported, not followed. scrcmd.dump_plan turns the misses into --dump-address lines. charmap.decode is for names, charmap.decode_message for dialogue. A command running past the dump’s end is reported as a short dump.
The species table
gSpeciesInfo = 0x0824CDFC, stride 28 (the decomp’s 26 plus two 00 00 padding bytes). Its data matches the English decomp. Traps when parsing the decomp: [SPECIES_NONE] = {0}, on one line, PERCENT_FEMALE(x), the noFlip bitfield, and hex #defines.
Finding CreateMon from it
pokemon.o’s first &gSpeciesInfo pool belongs to CreateMon [pokemon.c:1755]:
| symbol | address | how |
|---|---|---|
CreateMon | 0x08041150 | the disassembly (f0b5 4746 80b4 87b0) |
ZeroMonData | 0x08041090 | its first call |
CreateBoxMon | 0x080411C0 | its call between ZeroMonData and SetMonData |
SetMonData | 0x08043A78 | called with 56 (MON_DATA_LEVEL), then 64 (MON_DATA_MAIL) with 255 |
CalculateMonStats | 0x08041B78 | its last call |
The Spanish FireRed cartridge
Spanish FireRed base v0 (0100EB702342C000) contains FireRed_s.gba, game code BPRS, revision 0x0A. builds.BPRS and vendor/gblink-cards/symbols.json (language id 7) carry its addresses.
| symbol | Spanish FireRed |
|---|---|
gRngValue, gSaveBlock1Ptr, gSaveBlock2Ptr | 0x03004220, 0x03004228, 0x0300422C |
gMain, gIntrTable[4], gSoundInfo | 0x030022D0, 0x03002730, 0x03005F80 |
VBlankIntr, Client_RunBufferScript | 0x0800071C, 0x08148CD0 |
CreateMon, GetMonData3, Random | 0x08041164, 0x080432F8, 0x080486C4 |
CB1_Overworld, CB2_Overworld, RunTextPrinters | 0x08059E5C, 0x08059EDC, 0x08002D50 |
m4aSoundMain, ReadFlash | 0x081E089C, 0x081E224C |
MapGridGetElevationAt, MapGridGetCollisionAt | 0x0805C658, 0x0805C6D8 |
| Cut, Rock Smash, Strength message return pointers | 0x081C1909, 0x081C19FD, 0x081C1AE6 |
GetMoney, SpeciesToNationalPokedexNum (table) | 0x080A376C, 0x080469A8 (0x0824B9DE) |
The badge checks point to message scripts at 0x081C1901, 0x081C19F5 and 0x081C1ADE; EventScript_CurrentTooFast starts at 0x081AA346 (lockall). Translated text moves each resume script. tests/test_frlg_english_cartridges.py, tests/test_noclip.py and tests/test_follower.py use scratchpad/frlg_es/FireRed_s.gba when present.
The French Easy Chat vocabulary
All 1006 language-dependent Easy Chat words are read out of the console’s ROM into pokeldn/frlg/text/easychat_french_words.py; easychat_french.french(id) answers from it.
The slot problem
An Easy Chat id is (group << 9) | index. pokeldn/frlg/text/easychat_words.py names what the English ROM keeps in each slot; the French ROM keeps other words (EC_WORD_ENJOY renders STRESSE, EC_WORD_DONE FURAX, SPEECH/12 LES). Mail, the trainer card quote, the visiting trainer, --denied-message and the questionnaire gate depend on the French table.
Finding the table
sEasyChatGroups[] is 22 entries of { const void *wordData; u16 numWords; u16 numEnabledWords; } [src/data/easy_chat/easy_chat_groups.h:26], at 0x083E3700. Its counts equal the English build’s, so an id is the same slot in both languages. Word arrays and text span 0x083DE2C8..0x083E3700. tests/test_easychat_french.py requires render and ROM evidence to agree. TRAINER/11 is DRESSEUR; DRESSEURS is not in the table.
What the table says
EC_GROUP_STATUS(109 Abilities) is nearly a direct translation (stenchPUANTEUR,thick_fatISOGRAISSE,air_lockAIR LOCK).EC_GROUP_FEELINGSis re-binned: slot 13 (disappoints) is RAVI, 44 (eat) HUMILIE, 51 (drink) HONTEUX.EC_GROUP_SPEECH: LES and L’ take the slots English spends oncaseandmiss.
807 slots (EC_GROUP_POKEMON, POKEMON_2, MOVE_1, MOVE_2) print from gSpeciesNames / gMoveNames [easy_chat.c:155], localized by the console; easychat.species_word(55), easychat.move_word(177) and easychat.is_language_safe handle them.
Using it
from pokeldn.frlg.text import easychat, easychat_french
easychat_french.french(easychat.WORDS["enjoy"]) # 'STRESSE'
easychat_french.render(ids) # the line as the console will print it
easychat_french.check(ids, strict=True) # raises on anything unread or out of range
POKELDN_RADIO=esp32:auto ./.venv/bin/python -u bin/frlg_mg_host.py --live --keys PROD_KEYS \
--buffer-script string-gather --gather-address 0x083DF5C0 --gather-count 42 \
--gather-stride 12 --dump-file DUMP.bin --version firered # one group per run
./.venv/bin/python scratchpad/ec_words.py --group 4 DUMP.bin
LeafGreen’s Easy Chat region is FireRed’s at −0x1C4 with identical vocabulary.
The international revision 0x0A cartridges
The host has address tables for both versions in English, French, German, Italian, Spanish and Japanese. Functions are paired with the English ELF by unique instruction windows with relative calls and pointer literals masked; RAM globals come from literal pools; field-move scripts are paired as command sequences. SpeciesToNationalPokedexNum is paired through its neighbours, which share its instruction sequence.
| cartridge | ROM file | SHA-256 |
|---|---|---|
BPRD | FireRed_d.gba | 04f43a43f7cf9109561cd1744d108f50202c931ce31b30abaa6f18950824d20d |
BPRE | FireRed_e.gba | d32c8df8702293716ad8de68755fe5903161f4829a0a2e486bfa7e74a0b62e94 |
BPRF | FireRed_f.gba | 9edf7a3137536b0ccf024a732025da9a0ea1330eb9fd13b2ffed19f5e008c147 |
BPRI | FireRed_i.gba | cf7fa25cf57fbf12f24709efdd1d1aa8056efb4e9b6520ac7d068d3de13ff135 |
BPRJ | FireRed_j.gba | e2cdfb0415ef09e887e9d27b925ff02a713a92f004843490cf6b88b68db6cd01 |
BPRS | FireRed_s.gba | d4dee5aeb5313e073d6067bee37278b0204886958467633978bb746bbe3d5b76 |
BPGD | LeafGreen_d.gba | eb0e340b5efb3ccb7bab104183d2050fa834261dd816c5ad8221e4e98c04342b |
BPGE | LeafGreen_e.gba | 993a8a5695a4f7e4dfe8ceec55beb43e020e82b492854d457d4aa3f864d20f08 |
BPGF | LeafGreen_f.gba | 751346c16c0cf3a3ec601d6c2d3c482b54609c5d00ca74f2f668d75b31b8efde |
BPGI | LeafGreen_i.gba | b99b9c58c83a61b5bd8a1a69b6f21ce0cfab8d1f6065b53b81a323801c9b2895 |
BPGJ | LeafGreen_j.gba | a2aa939a23a36610902be51169042efac78f2f6db6a433e06443a2ee09d4f19e |
BPGS | LeafGreen_s.gba | a943ecfd6560115b184dc244daed71ba328a67334f343eb35b39932c3ce7c731 |
| symbol | German FireRed | German LeafGreen | Italian FireRed | Italian LeafGreen | Spanish LeafGreen |
|---|---|---|---|---|---|
gmain | 0x030022D0 | 0x030022D0 | 0x030022D0 | 0x030022D0 | 0x030022D0 |
sb1ptr | 0x03004228 | 0x03004228 | 0x03004228 | 0x03004228 | 0x03004228 |
sb2ptr | 0x0300422C | 0x0300422C | 0x0300422C | 0x0300422C | 0x0300422C |
rng | 0x03004220 | 0x03004220 | 0x03004220 | 0x03004220 | 0x03004220 |
vblank_intr | 0x08000730 | 0x08000730 | 0x08000730 | 0x08000730 | 0x0800071C |
create_mon | 0x08041178 | 0x08041178 | 0x08041164 | 0x08041164 | 0x08041164 |
client_run_buffer_script | 0x08148BA4 | 0x08148B80 | 0x08148BE4 | 0x08148BC0 | 0x08148CAC |
save_slot_layout | 0x083FCEC0 | 0x083FCCFC | 0x083F464C | 0x083F4488 | 0x083F7674 |
The ROM-backed tests run each cartridge’s Client_RunBufferScript, CreateMon, GetVarPointer, VBlankIntr, saved-hook loader and collision functions under Unicorn, and every card under mGBA.
Japanese layout
Japanese FireRed and LeafGreen use a different EWRAM layout:
| symbol | Japanese |
|---|---|
| party, party count | 0x020241E0, 0x02023F85 |
| player avatar, object events | 0x02036FA8, 0x02036D68 |
| palette fade, wild-encounter disable byte | 0x020379E8, 0x02038624 |
gSpecialVar_0x8000 | 0x02036FE8 |
gMain, gIntrTable[4] | 0x030022E0, 0x03002740 |
gRngValue, gSaveBlock1Ptr, gSaveBlock2Ptr | 0x03004230, 0x03004238, 0x0300423C |
RunTextPrinters (0x08002D38) reads its array at 0x02020030 with a 0x20 stride (0x24 internationally).
SaveBlock1 is 0x3D40 bytes (0x3D68 internationally); sector id 4 covers 0xEC0 bytes (0xEE8). RamScript keeps checksum 0x361C, magic 0x3620, body 0x3624.
Wonder Cards are 164 bytes and Wonder News 224. SaveWonderCard is 0x081481F4 (BPRJ) / 0x081481CC (BPGJ), validation 0x08148250 / 0x08148228. Card CRC at SaveBlock1 +0x3204, data at +0x3208, news at +0x3124. BufferCardText (0x08149934) reads title bytes 10..27, subtitle 28..40, four 20-byte body lines at 41..120 and two 20-byte footers at 121..160. The native .wc3 layout is in Gift files.
The Wonder Cards and Wonder News accept lists hold activities 6 and 7 (BPRJ 0x08410EC4, 0x08410EC8; BPGJ 0x08410E4C, 0x08410E50) where every other cartridge holds 21 and 22 [src/data/union_room.h:405-406]. The accepted RFU serials are {0x0002, 0x7F7F} (BPRJ 0x083FC378). All twelve titles list 0x01006fa0233f8000 as their first local communication id.
Japanese names use the kana table and a five-character trainer-name limit; Latin accents share byte values with kana, so built-in gift prose drops accents and wraps at 26 characters, two lines a page. CreateMon names species 25 ピカチュウ. The Japanese Team cards use their source’s Japanese struct sizes; two prompts are shortened.
Retail verification
| cartridge | verified on retail hardware | source |
|---|---|---|
Italian LeafGreen (BPGI, revision 0x0A) | All 41 Build fields match console memory; follower installation; Master Ball card and item delivery | Contributor verification |