The idea in one box
The disc is never resized. Almost every edit overwrites bytes in place and re-encodes the sector's error-correction fields, so no file offset or table of contents ever moves. Where an edit's meaning forces a length change (a door name), the scene's script is rebuilt and recompressed into the same footprint - or the scene is skipped. New behaviour is hand-written MIPS parked in bytes the game never reads, reached by a two-instruction detour.

At a glance

Tool
legaia-patcher (CLI) and the browser ROM patcher - same code, nothing uploaded
Input / output
Your own USA disc image → a shareable PPF patch (no Sony bytes) and an optional patched copy
Three foundations
LZS encoder legaia_lzs::compress · sector write-back legaia_iso::write · PROT-entry bridge disc::DiscPatcher
Edit classes
in-place (same-size) for tiers A, B, C, E, F; relocate (footprint-bounded) for tier D
Confidence
Shipped - every mod has a disc-gated oracle test that re-decodes the patched image
Full reference
Randomizer / disc patcher (every flag, every hook)

The technique ladder

Seven techniques in ascending order of what they let you change - and of how much reverse engineering each demands before the first byte can move.

The disc-patching technique ladder, tier A (simplest) up to tier G (most advanced) More advanced - more reversing G Carrying a RAM patch on disc Super Arts Pack (by ZetaPhoenix) - a community RAM patch streamed from annex sectors by an injected loader foreign payloads F Overlay dead-region injection Seru-trading vendor · roadmap: battle assist, Seru racing, dome betting, Blood Moon new systems E Code injection via the rodata gap Bonus equipment drops · run-away EXP · enemy ally · shiny Seru new behaviour D Variable-length relocation Doors (scene transitions) · high-capacity starting bag resize + fix-ups C Rewriting field-VM bytecode Treasure chests · town shops · random encounters · house doors operand rewrite B Editing inside LZS Monster drops · monster combat stats · weapon specialty decompress / repack A Static-table overwrites Steal items · spell costs · element affinity · prices · casino same-size data Foundational - same-size data edits
Each rung is a page in this series. Variants (the GIVE_ITEM splice, the new-game seed stamp, the string injection, the engine-facing config blob) are specialisations of a parent tier, tagged on their catalog rows rather than given their own rung.

Read in order

Later rungs assume the earlier ones' machinery. If you read only one, read tier B - it is where "we can only overwrite bytes in place" stops being a limitation.

TierPageWhat it is aboutWhy a player might care
AStatic-table overwritesFixed-width stat tables in the executable - steal items, spell costs, element affinities, prices, casino prizes.The simplest way anything on the disc changes, and where every randomizer starts.
BEditing inside LZSMonster records live compressed inside fixed slots; an encoder the retail decoder accepts is what makes them editable.Why "edit the monster" needed a compressor before it needed an editor.
CRewriting field-VM bytecodeChests, shops, encounters and house doors are operands in each scene's script.How a chest in Rim Elm can hold something it never held.
DVariable-length relocationA door carries its destination's name inline, so re-pointing it resizes the record.The door randomizer and the high-capacity starting bag.
ECode injection via the rodata gapHand-assembled MIPS detoured into reclaimed space in the executable.New behaviour, not just new data - and the traps that bite the first time.
FOverlay dead-region injectionA whole new screen hosted in a menu overlay's dead space: the Seru-trading vendor.Where genuinely new features come from.
GCarrying a RAM patch on discA community GameShark mod - the Super Arts Pack by ZetaPhoenix - parked in annex sectors and streamed into free RAM by an injected loader, installed byte-for-byte.How someone else's cheat-device mod becomes a cold-boot disc patch, credit intact.

Three capabilities the preservation track never needed

The asset-preservation half of the project only reads the disc. Writing one back needs three pieces, and every mod stands on them.

CapabilityModuleWhy
LZS encoderlegaia_lzs::compressRetail ships only a decoder. Greedy LZSS, slightly looser than Sony's packer, always accepted - Legaia LZS.
Sector write-backlegaia_iso::writeA CD sector carries error-correction fields over its payload; overwrite the payload and they must be recomputed or the sector reads as damaged - disc geometry.
Disc bridgedisc::DiscPatcherMaps a "PROT entry N, offset K" edit to the sector it lives in through the PROT.DAT table of contents.

The editing model: same-size, except when it can't be

  • In place. Each target sits in a fixed slot with slack (a monster's 0x14000-byte slot) or is a fixed-width table field, so the patch is a byte overwrite plus a sector re-encode. No offset anywhere moves.
  • Relocate. When the edit itself changes a byte count, the MAN relocation engine rebuilds the decompressed script, fixes every disturbed offset, and recompresses into the asset's original footprint - or leaves the scene vanilla.
  • Either way the disc's total size never changes, which is what makes a PPF patch a complete description of the edit.

Mod catalog

Every current mod grouped by technique, plus the roadmap. The table is generated from site/_content/writeups/disc-patching/mods.toml at build time. Gap flags mods that share the executable's reclaimed rodata region - a finite resource, and the contention that tier F exists to escape. Edit is in-place (same-size) or relocate (footprint-bounded).

ModFlagTargetEditGapStatusTest
Tier A - Static-table overwrites - Same-size byte edits to a fixed table in SCUS or a raw overlay. No compression, no relocation - but reverse-engineered to the exact field.
Steal items--stealsSCUS steal table DAT_80077828, item byte at +1 (chance byte untouched)in-place-shippedsteal_patch_real.rs
Spell MP costs--spell-costSCUS spell table DAT_800754C8, cost byte at +3in-place-shippedspell_cost_real.rs
Equipment stat bonuses--equip-bonusSCUS equip-bonus table DAT_80074F68, stat tuple +0..+4 (mask/slot bytes kept)in-place-shippedequip_bonuses_real.rs
Special-attack power--move-powerBattle overlay (PROT 0898) move-power table, power halfword at +0x00in-place-shippedmove_power_real.rs
Element-affinity matrix--element-affinityBattle overlay (PROT 0898) 8x8 affinity matrixin-place-shippedelement_affinity_real.rs
Item prices--shops (pricing)SCUS item table 0x80074368, u16 price at record +2in-place-shippeditem_price_real.rs
Casino prize exchange--casinoMenu overlay (PROT 0899) raw prize table at file offset 0x15D00in-place-shippeddisc_patch_real.rs
Starting level stamp--starting-levelSCUS new-game roster seed: level literal +0x130, growth-curve stats, lead XP +0x0/+0x4in-place-shippedstarting_level_real.rs
Unused-item naming (Seru Bell) string--unused-itemsSeru Bell name string at reserved gap 0x8007AB40 + name-pointer repoint for id 0xFDin-placeshared gapshippedunused_content_real.rs
Tier B - Editing inside LZS - The value lives in a compressed slot: decompress, mutate, re-pack (one-step lazy matching, about as tight as Sony's - not tighter), zero-pad back to the fixed slot so nothing downstream moves.
Monster drops--dropsMonster archive (PROT 867) slot, drop id/chance at +0x48/+0x49in-place-shippedmonster_drop_real.rs
Monster combat stats--monster-statsMonster archive (PROT 867) slot, HP/MP/ATK/DEF/INT/SPD halfwords at +0x0C..in-place-shippedmonster_stats_real.rs
Enemy difficulty scale--enemy-stat-scaleMonster archive (PROT 867) slot, the same HP/MP/ATK/DEF/INT/SPD halfwords at +0x0C.., multiplied - one factor for all seven, or per statin-place-shippedmonster_stats_real.rs
Enemy attack count--enemy-attack-countMonster archive (PROT 867) slot, each affordable attack entry's AGL-cost byte at entry +0x74, divided by the multiplier so the AGL gauge affords more (or fewer) strikesin-place-shippedattack_count_real.rs
Weapon specialty--weapon-specialtyPlayer battle files (PROT 0863..0865) swing record, arm-cost byte at +0x74in-place-shippedweapon_specialty_real.rs
Delilas party swap structural rebuild--delilas-party V,N,Gmonster archive slots 162-164 + player battle files PROT 863-865 + field pack PROT 0874 + nilboa NPC pack PROT 0639 + battle voice bank PROT 0869 + readef.DAT art slots + XA arts-voice banks + SCUS arts-name table + new-game template names + PROT 0898 element/camera rowsplay as Gi / Lu / Che (any assignment); duels + dome Master legs field Vahn / Noa / Gala; arts shouts follow --delilas-arts-voice (default original; adjusted = pitch/formant re-voice); save-select/card-icon/load-screen portraits + sibling-name dialog follow the mapping; menu portraits / HUD faces stay retail; the cast route yields the SCUS gap to any hard consumercross-rig battle-model rebuild both directions (anatomy permutation + rest-pose bake + 4bpp texel re-layout), monster-block renames, template name writes-shippeddelilas_party_real + party_swap_real + delilas_cast_stage_real + delilas_cast_remap_real + enemy_anim_mirror_real + nivora_field_real
Super Art damage power--super-art-powerper-strike power run of the Super Arts' replacement rows in the player record[0] art bankcomposes with everything - a damage row claims none of the SCUS arenasame-size byte edits inside the LZS record, recompressed into the retail footprint-shippedsuper_art_power_real
Monster battle skinslegaia-patcher monster-texture4bpp texture pages + CLUTs inside a monster's battle_data block (PROT 867)browser: the monster tier of the texture-replacement pagePNG in, bit-exact 4bpp re-layout into the block's own page footprint, block recompressed-shippedmonster_texture_real
Custom monster models structural rebuildlegaia-patcher monster-modela monster's whole 3D model (mesh + texture) inside its battle_data blockthe mesh must keep the retail part count - animations pose parts by index, so a same-part-count mesh inherits every retail moveOBJ+PNG out, custom model re-encoded (byte-exact TMD encoder) and re-pooled into the block-shippedmonster_model_real
Tier C - Rewriting field-VM bytecode - The data is bytecode - rewrite an opcode's inline operand and recompress the per-scene MAN within its original footprint budget. Needs an opcode-aware walk to find sites safely.
Treasure chests--chestsPer-scene MAN: GIVE_ITEM op 0x39 operand + 0xC2 display tokenin-place-shippedchest_patch_real.rs
Town shops--shopsPer-scene MAN: inline op 0x49 shop-stock item idsin-place-shippedshop_patch_real.rs
Random encounters--encountersPer-scene MAN: formation monster-id bytes (count/reserved preserved)incl. encounter-scope + on-by-default solo-strong passin-place-shippedencounter_patch_real.rs
House doors (intra-town)--house-doorsPer-scene MAN partition-0: player-warp op 0xA3 0xF8 tile operandsin-place-shippedhouse_door_patch_real.rs
Tier D - Variable-length relocation - When an edit must change a byte count: rebuild the decompressed MAN, fix every internal offset / table / delta, keep it inside the asset's footprint.
Doors (scene transitions)--doorsPer-scene MAN: 0x3F transition destination name (resizes the record)relocate-shippeddoor_patch_real.rs
Starting bag (high-capacity) splice--starting-itemsOpening-scene MAN: guarded GIVE_ITEM block (0x70 test / 0x39 give / 0x50 set) spliced inrelocate-shippedstarting_bag_real.rs
Starting items (seed code) stamp--starting-itemsSCUS new-game seed routine FUN_80034A6C: reclaimed region re-stamped with grant codelow-count path; high-count uses the MAN splice (G) abovein-place-shippedstarting_items_patch_real.rs
Tier E - Code injection via the rodata gap - Behavior the game can't express as data: detour an existing routine into hand-assembled MIPS parked in a reclaimed all-zero region of the executable.
Bonus equipment drops--equipment-dropsDetour at reward routine 0x8004F610 -> hand-assembled routine + id table at gap 0x8007AB80in-placeshared gapshippedequipment_drops_real.rs
Run-away EXP--flee-expDetour at battle-action escape teardown 0x801E5A10 -> routine at gap 0x8007AD00in-placeshared gapshippedflee_exp_real.rs
Enemy ally (charm)--enemy-allyDetour at battle setup 0x80051990 -> charm routine at gap 0x8007ACA0; + one-word victory-mask widen 0x801E6638 in overlay 0898A per-battle chance to flip an enemy onto your side, riding the game's own Confuse/Charm retarget; gated to multi-enemy fights so scripted solo and tutorial battles can't softlock. Full story on the Tier E page.in-placeshared gapshippedenemy_ally_real.rs
Shiny Seru--shiny-seru9 detours across the battle / menu / capture paths; routines + data in four read-watch-verified-dead SCUS regions outside every live table (gap1 0x80077728, arena1 0x8007AE00, arena2 0x8007AFF8, slot6 0x80078A88)A per-battle chance that a capturable enemy spawns shiny (+35% stats, translucent); the Seru you capture from it keeps a permanent +35% damage bonus, stored in a spare per-Seru byte so it survives saving. The Tier E page tells the placement story (the 'zero is not dead' trap that bit this three times).in-placeshared gapshippedshiny_seru_real.rs
Super Arts on the move list--show-super-artspause-menu Moves list: performed-gated Super/Miracle rows with derived arrow inputs, AP-sorted interleavea Super Art has no AP field but the chain that performs it pays; rows appear once performedSCUS injection-gap code + the free 16th learned-id slot (+0x75D performed flag); menu overlay renderer hookshared gapshippedsuper_art_list_real
Tier F - Overlay dead-region injection - Host hand-assembled code inside a resident overlay's reference-free dead space - so a whole new screen or system costs nothing and never competes for the shared rodata gap.
Seru trading vendor config--seru-tradeMenu overlay (PROT 0899) dead region 0x801E74E0..0x801E83E0: full Trade screen; SCUS config blob 0x8007AF00 drives the engine mirrorconfig blob is the only gap-resident byte; the UI escapes the gap entirelyin-place-shippedseru_trade_real.rs
Enemy HP bars--enemy-hp-barTwo-word detour at the damage-popup renderer (PROT 0898, 0x801DF6B8); the routine laid over four routines nothing on the disc references: 0x801F2D54 / 0x801F463C / 0x801DBB2C in the battle overlay and FUN_8005126C in SCUSA red HP gauge over every living monster, drawn every frame from the roster panel's HP label and the AP meter's gauge primitive (red meter + percentage numeral, no blue chrome), fed by the displayed-HP mirror so it steps per hit inside a combo; one row per monster slot along the top of the screen, tracking each monster's screen X through the game's own billboard projector. Hosts are unreferenced code, not zero padding, so 'zero is not dead' does not apply and no arena byte is claimed.in-place-shippedenemy_hp_bar_real.rs
Enemy battle assist(planned)Battle overlay: a defeated/random enemy joins as a non-controllable ally actornew battle-actor behavior; likely overlay-hosted code + an extra actor slotin-place-planned-
Seru racing(planned)New minigame (horse-racing shape): race state machine, odds, betting payoutnew system; hosted in a minigame overlay's dead spacein-place-planned-
Muscle Dome spectate + betting(planned)Muscle Dome overlay: watch two NPCs fight, place a pre-match wager, settle payoutextends the existing match SM with a spectator + betting pathin-place-planned-
Blood Moon event(planned)Periodic overworld event: red tint + stronger enemies + better loot for a window every ~30 minperiodic global timer + encounter/loot modulation + overworld CLUT tintin-place-planned-
Tier G - Carrying a RAM patch on disc - A payload that targets free RAM no overlay covers cannot be hosted - it must be streamed: parked in annex sectors and read to its address at load time by an injected stub calling the game's own CD reader. Also the rung where a third-party mod's bytes ride the disc verbatim.
Super Arts Pack (by ZetaPhoenix)--super-arts-pack3764-byte community RAM patch streamed to free RAM 0x801FD000 at battle load; 14 hook words across SCUS + PROT 0898fifteen extra Super Arts, five per character; ZetaPhoenix's block and hook set installed verbatim (the loader stub is the carrier's only added code), incl. his fix for retail's mis-centred art name banner, relocated to arena 1annex sectors + in-place word editsshared gapshippedsuper_arts_pack_real

Planned rows concentrate at the top of the ladder, where system-level changes live.

See also