Cheat databases
A GameShark is a 90s cheat cartridge: every code it applies is a RAM address plus a value to write there each frame. "Infinite HP (Vahn)" only works if its target really is where Vahn's HP lives, so twenty-year-old community cheat lists are a stack of memory-layout facts verified on real hardware. This page covers the two lists the project ingests, how a code is encoded, what the codes pin, and how the engine replays them to check its own memory map.
At a glance
- Where
data/cheats/legaia-ntsc-u.gs.txt(GameShark text) anddata/cheats/legaia-ntsc-u.cht(Mednafen) - committed; labelled(address, value)pairs only, no Sony bytes- Region
- NTSC-U (
SCUS_942.54) - Parser
crates/cheats(legaia_cheats) + thecheat-toolCLI- Applier
legaia-engine play-window --cheat-fileviaengine_core::cheat_applier- Pins
- Character record, inventory, battle actor, script-VM globals, camera, minigame scratch, four code-patch sites
- Used by
- Save record, RAM map, Inventory, Battle
Data files
| File | Format | Shape |
|---|---|---|
legaia-ntsc-u.gs.txt | GameShark text dump | One write per line, fixed columns |
legaia-ntsc-u.cht | Mednafen .cht | Indexed cheatN_desc / _code / _enable triplets; multi-write effects join codes with + |
Both files describe the same effects in different encodings; cheat-tool diff compares them.
How a code is encoded
A code is eight hex digits of address and four of value. The high byte of the "address" is not an address at all - it is the operation prefix that says what kind of write (or condition) the cartridge performs.
| Prefix | Operation | Example |
|---|---|---|
| 0x80 | u16 little-endian write | 80084816 0064 → mem16[0x80084816] = 0x0064 |
| 0x30 | u8 write | 300848A3 0000 → mem8[0x800848A3] = 0x00 |
| 0xD0 | if mem16 == value, execute next line | D007B7C0 0100 (Select pressed) |
| 0xE0 | if mem16 != value, execute next line | E007B83C 0003 |
Column layout of the GameShark text dump
| Column | Meaning |
|---|---|
| R | read/write classifier (always R - a cheat is a write) |
| I | encoding flag (always I) |
| 1 / 2 / 4 | width in bytes |
| L | endianness (little) |
| 0 | compression group (always 0) |
| 80084816 | address, 8 hex digits, no 0x |
| 64 | value, hex |
| rest of line | description |
For prefix 0x80 the parser trusts the width column (1 / 2 / 4) over the prefix and rewrites the prefix to match - hand-edited dumps occasionally disagree with themselves.
Parser, classifier and CLI
crates/cheats parses both files into one Database, then sorts every address into a RAM region (a Category) with a detail label such as the record field it hits.
| Item | Purpose |
|---|---|
parse_gs_text / parse_mednafen_cht | Either file → Database |
Database::dedupe_identical | Drop the "Have 99 Items × 70" duplicate sprawl |
classify_address | One address → Category + detail label |
Category | CharacterRecord / PartyMoney / Inventory / BattleActor / ScriptVmGlobal / PadInput / CameraGlobal / WorldStoryFlag / Minigame / FieldVmCollision / ScratchActiveActor / CodePatch / Unknown |
taxonomy::classify_writes | Roll a set of changed RAM addresses into per-region buckets; the classification half of a write tracer (mednafen-state write-taxonomy LEFT RIGHT) |
cheat-tool list data/cheats/legaia-ntsc-u.gs.txtcheat-tool classify data/cheats/legaia-ntsc-u.gs.txt --dedupecheat-tool diff data/cheats/legaia-ntsc-u.gs.txt data/cheats/legaia-ntsc-u.chtcheat-tool offset-table data/cheats/legaia-ntsc-u.chtparse and extract-offsets are the other subcommands. In the release archive the databases are embedded, so no path is needed (modding guide).
What the write taxonomy is for
Diffing two save states yields a set of changed addresses. classify_writes flags the two interesting kinds: writes outside every known data region (Unknown) and writes into the 0x8007Bxxx script-VM / build-flag scratch (ScriptVmGlobal, the band holding the 0x8007B8C2 build-mode selector and the debug-menu enable). It is pure - input is a list of u32 - so it is unit-tested on synthetic deltas.
What the corpus pins, per region
Each row is one RAM region the classifier knows and what the cheats in it establish about the game's memory layout.
| Region (Category) | Addresses | What the cheats establish |
|---|---|---|
| CharacterRecord | 0x80084708 + slot * 0x414 | Per-character 0x414-byte record; every offset is named on the save record page |
| PartyMoney | 0x80084540..0x800845DC | Party-wide globals: scene-name slot 0x80084540, game time 0x80084570 (u32), member count 0x80084594, member ids + Noa / Gala join gates 0x80084598..9A, gold 0x8008459C (u32), casino coins 0x800845A4 / A6, scene-name mirror 0x800845DC |
| Inventory | 0x80085958, 2-byte stride | (id, count) pairs. "Have 99 Items" stamps 0x63 across a 72-slot page - the cheat's span, not an engine bound; the live window is 128 or 256 slots (inventory) |
| BattleActor | 0x800EC9E8 + n * 0x2D4 | Per-actor battle record; HP / MP offsets in the table below |
| ScriptVmGlobal | 0x8007B450..0x8007B83C | Menu-request, encounter counter, save-anywhere, pad and next-game-mode registers (table below) |
| CameraGlobal | 0x8007B6F4 | Camera mode word ("Control Camera", "Small Maps") |
| WorldStoryFlag | 0x80085600..0x80085800 | Story-flag block. No shipped cheat lands here; the Door-of-Wind bitmask at 0x8008575C / 5E is claimed first by the script-VM arm (classifier ordering, not a fact about the cheat) |
| Minigame | 0x801D3CAC..0x801DC06C | Minigame scratch - fishing tension / cast power / life / fish id at 0x801D9168 / 9274 / 9298 / 91CC, Baka Fighter 0x801DBFC4 / BFF0 / C06C, dance points 0x801D53CC, slot machine 0x801D3CAC. Citations only; the engine's minigame ports do not consume them |
| FieldVmCollision | 0x801D065C / 06BC / 071C / 078C | "Walk Thru Walls": four 1-byte gates in the field overlay; 0x06 in all four disables the collision check |
| ScratchActiveActor | 0x8007A6BC | Scratch for the currently-acting character; every "Infinite HP / MP" hits it first, then the per-record copy |
| CodePatch | four sites | Cheats that patch instructions, not data (table below) |
Battle actor offsets
| Offset | Field | Cheat |
|---|---|---|
| +0x14C | hp_curr | "Infinite HP" (per character) |
| +0x14E | hp_max | read at battle init |
| +0x150 | mp_curr | "Infinite MP" |
| +0x152 | mp_max | |
| +0x172 | hp_max_settled | "Infinite HP" second site |
| +0x174 | mp_max_settled | "Infinite MP" second site |
The second-site pair is the copy that survives the per-frame stat-aggregation clamp (how we know); cheats write both so the value stays pinned through clamping.
Script-VM globals
| Address | Cheat | Engine meaning |
|---|---|---|
| 0x8007B450 | "Status Modifier Menu", "Save Anywhere", "Shop Modifier", "End of Game Stat Page" | Menu-request register the menu overlay polls each frame |
| 0x8007B5FC | "No Random Battles" | Encounter step counter (set to 0x377 to force or suppress a trigger) |
| 0x8007B6A8 | "Save Anywhere (Press Select+X)" | Save-anywhere allow flag |
| 0x8007B7C0 | condition gate in many cheats | Pad state register the D0 / E0 lines read |
| 0x8007B83C | "Press R2 For Debug Menu" | Next game-mode register (0x1A = FMV) |
Code patches
Three cheats write 0x2400 to an instruction's upper halfword (addr % 4 == 2), turning it into addiu $zero, $zero, imm - the GameShark nop idiom. The fourth shortens a branch. All four are callsite anchors a static address scan would not find.
| Address | Cheat | Effect |
|---|---|---|
| 0x8004309E | "Infinite Items All Slots" | nops the count-decrement instruction |
| 0x8004390E | "Remove Vahn's Chest" | nops a draw-call site |
| 0x8007EA96 | "Maxed HP for All Characters" | Past the end of the SCUS image (text ends 0x8007B800) - patches something other than SCUS code |
| 0x800422F4 | "Bought Any Item ... 99 Quantity" | Writes 0001 over bne r2, r0, +2 in the inventory-add clamp, so the clamp to 99 always runs |
Runtime applier
legaia-engine play-window --cheat-file <PATH> applies every entry once at boot, routing each write through the ram_map registry to the matching World / CharacterRecord field. Conditional lines count as always-true unless --cheat-strict is given (which then skips every button-gated cheat, since the engine does not emulate the pad register).
Cheat report (49 entries, 96 writes; 71 applied, 25 unmapped, 0 unknown):
ok Infinite HP (Vahn)............... 4/4 writes
ok Infinite Gold (Never Glitchy).... 1/1 writes
skip Walk Thru Walls.................. 0/4 writes
ok- every write mapped onto engine state.skip- every write landed in a regionram_mapdoes not map (Walk Thru Walls:FieldVmCollision).part- a mixed entry.
The report is a memory-map regression check: a cheat that stops applying means the engine field was renamed (update ram_map), is no longer exposed (update WorldField), or the database row is stale.
How we know
| Evidence | Address | What it proves |
|---|---|---|
| Cheat corpus itself | every row above | Each published code is an (address, value) pair that visibly worked on hardware |
| Stat aggregation clamp | FUN_80042558 | Why "Infinite HP" needs the +0x172 / +0x174 second site |
| Next-mode writer | FUN_801E30E4 | 0x8007B83C is the next-game-mode register (writes 0x1A for FMV) |
| Inventory-add clamp | 0x800422F4 | The slti r2,r2,100 / addiu r3,99 / sb sequence the "99 quantity" patch shortens into |
| SCUS image extent | 0x80010000..0x8007B800 | 0x8007EA96 is not SCUS code |