At a glance

I want to...SubcommandSection
Randomize a playthroughlegaia-patcher randomize1
Apply a friend's patchlegaia-patcher verify3
Rebalance one monster by handlegaia-patcher monster-block4
Replace a texture with my own PNGlegaia-patcher tim-list / tim-export / tim-replace4b
Translate the gamelegaia-patcher translate ...5
Read a memory-card savesave-tool6
Look up an item or an art without a discgamedata-tool7
Classify GameShark codescheat-tool8
Two safety properties to rely on
  • The tool never writes to --input. The original image is opened read-only; output goes to the PPF patch and (optionally) a separate patched copy. Keep your original dump pristine anyway - it is your only source.
  • Nothing shareable contains Sony bytes. The PPF patch and the manifest are safe to share; a patched .bin (--output) is for local play only, never redistribute it.

No terminal? The browser ROM patcher runs the randomizer, the language packs and the texture-replacement flow client-side - the same code compiled to WebAssembly; your disc never leaves the tab.

A patched USA image also boots in the port (legaia-engine accepts it like the retail disc). The port reads the disc's data, so data edits - drops, steals, encounters, chests, doors, shops, starting items - take effect there; mods that work by injecting MIPS code into the game's executable only run where that executable runs, on an emulator or a console.

1 · Randomize (onto a scratch copy)

./legaia-patcher randomize --input "/path/to/Legend of Legaia (USA).bin" --seed 12345 --patch legaia-12345.ppf --output legaia-12345.bin --manifest legaia-12345.toml
your disc .bin--input, opened read-only legaia-patcher--seed + feature flags .ppf + manifestno Sony bytes: share it patched .bin + .cue--output: local play only
Your original image is never written to. What you share is the patch; the patched disc stays on your machine.

A few seconds on a modern machine. The run prints a disc: <label> line identifying the input build, the resolved numeric seed (so any run can be reproduced), and a spoiler-safe change summary. The same seed + options over the same disc is byte-deterministic: you and a friend get identical patches. --output also writes a .cue beside the .bin so emulators can open it directly. --dry-run plans without writing.

Nothing is randomized unless asked. --drops shuffle re-rolls monster item drops; dozens of flags cover encounters, chests, steals, arts combos, doors, shops, monster stats, spell costs, equipment, and code-hook extras (--equipment-drops, --flee-exp, --enemy-ally, --shiny-seru, ...). Read-only preview subcommands - drops, chests, steals, arts, doors, shops, monster-stats, spell-costs, and more - list what a feature would touch before you commit.

2 · The region guard

The randomizer's offsets and injected code target the USA build (SCUS-94254). Running randomize or verify against any other disc - the PAL SCES builds included - stops with a region error, because a USA-built patch "applies" to a PAL disc yet produces a corrupt hybrid. --allow-region-mismatch overrides the guard; only pass it when you know the patch was built for that exact disc. The PAL discs themselves are documented in tooling/pal-localizations.md.

3 · Share and verify a patch

Share the .ppf (and the manifest). The recipient applies and checks it against their own dump in one step:

./legaia-patcher verify --input "/path/to/their Legend of Legaia (USA).bin" --patch legaia-12345.ppf --output legaia-12345.bin

verify applies the patch to a copy, confirms every record applied cleanly, and re-parses the result. The same region guard applies here - that is what catches "this patch was built against a different disc" before anyone plays a corrupt hybrid.

4 · Hand-edit one monster (stats / element / name)

The randomizer shuffles monster stats wholesale; for a curated rebalance you edit records directly. Every monster lives in the battle_data archive (PROT entry 867) as an LZS-compressed block - monster-block does the slot math and the compression for you, so the edit loop is dump → hex-edit → write:

  1. Dumpmonster-block --id 10 --dump m10.bin writes the decoded block (monster-stats lists the 1-based ids).
  2. Hex-editChange fields in the stat record at the top of m10.bin (layout below).
  3. Write backmonster-block --id 10 --write m10.bin --output edited.bin --patch m10.ppf
./legaia-patcher monster-block --input "/path/to/disc.bin" --id 10 --dump m10.bin
./legaia-patcher monster-block --input "/path/to/disc.bin" --id 10 --write m10.bin --output edited.bin --patch m10.ppf

The stat record starts at +0x00 of the dumped block; multi-byte fields are little-endian. The name is a NUL-terminated string elsewhere in the same block, at the block-relative offset the first word holds.

+0x00name offsetu32
+0x04effect + resource pointers2 × u32
+0x0CHPu16
+0x0EAGLu16
+0x10MPu16
+0x12ATKu16
+0x14DEF Au16
+0x16DEF Bu16
+0x18INTu16
+0x1ASPDu16
+0x1C u8
+0x1Delementu8
+0x3ESeru idu8
+0x3Fcatch %u8
+0x44goldu16
+0x46EXPu16
+0x48drop itemu8
+0x49drop %u8

The write is the same machinery as the randomizer - the block is recompressed (the encoder need not match Sony's bytes; the retail decoder accepts any valid stream), padded back into its fixed 0x14000-byte slot, and the touched sectors get their EDC/ECC re-encoded. With --output the tool re-parses the patched image and confirms the record still decodes before declaring success. The equivalent flags on the extracted tree are asset monster-archive --dump-block / --write-block (extracting assets).

4b · Replace a texture

Any texture on the disc can be swapped for your own PNG - same loop, three subcommands:

  1. Find ittim-list prints coordinates, dimensions and curated labels (try grepping for "title").
  2. Exporttim-export writes it as an editable PNG.
  3. Edit + replaceKeep the exact pixel size, then tim-replace patches it back.
./legaia-patcher tim-list --input "/path/to/disc.bin"
./legaia-patcher tim-export --input "/path/to/disc.bin" --entry 890 --offset 0x14228 -o title.png
./legaia-patcher tim-replace --input "/path/to/disc.bin" --entry 890 --offset 0x14228 --png title.png --output edited.bin --patch title.ppf

The replacement must keep the original's dimensions and fit its palette (the error names the offending pixels; --quantize folds extras to the nearest color), alpha becomes PSX transparency, and compressed (lzs-tier) textures apply only when the edit recompresses into the retail footprint. The same flow runs entirely in the browser on the ROM patcher page (scan / thumbnails / preview).

5 · Translate the game

The translate family exports the disc's text as an editable YAML language pack and imports a filled pack back onto a copy of the disc. It has its own step-by-step guide, written for translators: translating the game covers the browser and command-line paths, how much room a line has, and why a line can stay English.

A language pack composes with every randomizer option: on the ROM patcher, pick a language and any randomizer options and patch once. Owners of an official European or a fan-translated disc can lift its text onto the USA disc instead of translating from scratch (translate lift-official; translations from another disc).

6 · Inspect saves

save-tool reads PSX memory-card images (.mcr and friends, e.g. from an emulator's card directory):

./save-tool dir card.mcr
./save-tool saves card.mcr
./save-tool character card.mcr --block 1 --offset 0x5C8
./save-tool party card.mcr --block 1 --offset 0x5C8 --count 3

dir lists directory entries (magic-checked); saves lists active save blocks + product codes. In a retail Legaia save block the character-record array starts at block offset 0x5C8 with a 0x414-byte stride: 0x5C8 is the first character, 0x9DC the second, and so on. Each record's display name sits at record-relative +0x2A7. --offset / --block accept 0x-hex or decimal. sc-diff diffs the save block of two cards to pin what a gameplay change wrote.

7 · Look things up without a disc

gamedata-tool bakes in the curated game-data tables (arts, magic, items, weapons, armor, accessories, enemies, shops, casino, fishing) - handy as ground truth beside the randomizer's read-only listings:

./gamedata-tool find "Healing Leaf"
./gamedata-tool list arts --character Vahn
./gamedata-tool arts-by-command Vahn "Arms,Ra-Seru,High"
./gamedata-tool shop "Rim Elm"
./gamedata-tool dump-json enemies

8 · Cheat databases

cheat-tool parses GameShark / Pro-Action-Replay cheat collections and classifies each code by the RAM region it targets. The community Legaia NTSC-U databases are embedded, so no input file is needed:

./cheat-tool list
./cheat-tool classify
./cheat-tool extract-offsets
./cheat-tool parse my-cheats.cht

list names the built-in databases, classify rolls them up per category, extract-offsets pulls the codes that land in the character record, and parse points at your own file. A file that parses to zero entries warns on stderr rather than silently printing [].

See also