# Legend of Mana PSX Decompilation **Languages:** English | [日本語](README_JP.md) [![US Progress]][us progress site] [![JP Progress]][jp progress site] [![Build and Progress](https://github.com/celophi/lom-decomp/actions/workflows/progress.yaml/badge.svg)](https://github.com/celophi/lom-decomp/actions/workflows/progress.yaml) [US Progress]: https://decomp.dev/celophi/lom-decomp/SLUS_010.13.svg?mode=shield&measure=code&label=US%20Progress [us progress site]: https://decomp.dev/celophi/lom-decomp/SLUS_010.13 [JP Progress]: https://decomp.dev/celophi/lom-decomp/SLPS_021.70.svg?mode=shield&measure=code&label=JP%20Progress [jp progress site]: https://decomp.dev/celophi/lom-decomp/SLPS_021.70 A **matching decompilation** of the PlayStation game **Legend of Mana**. Both the North American and the Japanese releases are **100% matched**. The project reconstructs readable C source code that compiles down to the original MIPS machine code that exists on the disc, byte-for-byte. Two regional releases are targeted: - **North America** - `SLUS_010.13` (disc serial **SLUS-01013**). Complete: all 18 binaries are fully linked. - **Japan** - `SLPS_021.70` (disc serial **SLPS-02170**). Complete: all 18 binaries are fully linked, built from the same C sources as the North American version. Unless a section says otherwise, the build instructions, targets, and file names below refer to the North American version. This is a decompilation project, **not a PC port**. The repository does not include the game executable, overlay binaries, artwork, audio, or other copyrighted game data. You must provide the required files from your own copy of the game. The primary motivation for this project is to preserve the original game's logic and behavior for educational research and potential modding capabilities. ## Fully linked For both versions, every module - the main executable and all 17 overlays - is **fully linked**. A module is fully linked when two conditions hold: 1. The build produces an **ELF whose bytes match the original decompressed file**, and 2. Running the project's compressor on that ELF (stripped to a raw binary) **reproduces an exact replica of the `.BIN` file as it appears on the disc**. In other words, the round-trip `original .BIN -> decompress -> C source -> compile -> ELF -> compress -> .BIN` is bit-identical. The main executable is not compressed, so for `SLUS_010.13` and `SLPS_021.70` condition 1 is the whole check: the linked ELF, converted to a raw binary, equals the disc file. *(Check out the compressor! It's honestly really amazing that it is **bit identical** and kind of extraneous, but cool nonetheless!)* Run `make verify-bins` to check every module, or `make verify-bins VERSION=jp` for the Japanese version. ## Roadmap 1. ✅ **100% matching** - *done.* The main executable (`SLUS_010.13`) and all 17 overlays are fully linked (see above). The Psy-Q SDK libraries are still linked from the original assembly. 2. 🚧 **Cleanup and documentation** - *in progress.* Remove decompilation artifacts and document functionality. 3. ✅ **NTSC-J version** - *done.* The original Japanese release (`SLPS-02170`) builds from the same source tree as the North American one, and all 18 of its binaries are fully linked too. 4. 💤 **Modding and source port** - build on the reconstructed source to make modding practical and to enable ports to other platforms. ## Supported game versions | Item | North America | Japan | |---|---|---| | Status | ✅ Fully linked | ✅ Fully linked | | Disc serial | `SLUS-01013` | `SLPS-02170` | | Main executable | `SLUS_010.13` | `SLPS_021.70` | | Main executable SHA-1 | `d11dfdd50d412ac3fa3e2eb80fbde138da118f27` | `b067188a92e4de9a4db7bb7e5343c757e9884bfa` | | Disc image (`.bin`) SHA-1 | `c1b536c99f0d390584eb30462a7e37f2bbef3902` | `7a314615be8a482cf3f81b4101cc19aaa738f36d` | | Architecture | 32-bit little-endian MIPS / PlayStation | 32-bit little-endian MIPS / PlayStation | The getting-started steps below use the North American version. The Japanese version works the same way, with its files under `disc/jp/` and `VERSION=jp` on the `make` command line. Other regional versions are not currently supported. ## Requirements For the normal build you need: - **Git**, including submodule support. - **Docker** - Docker Desktop on Windows/macOS or Docker Engine on Linux. - A **legally obtained copy of Legend of Mana**, North American or Japanese (see [Supported game versions](#supported-game-versions)). You do not need to install the historical PSX compilers, Psy-Q tools, Python packages, or a MIPS cross-compiler directly on your host. The development container provides them. ## Getting started ### 1. Clone the repository ```bash git clone --recursive https://github.com/celophi/lom-decomp.git cd lom-decomp ``` If you already cloned without submodules: ```bash git submodule update --init --recursive ``` ### 2. Add the original game files Extract the main executable and the game's `BIN` directory from your North American disc/image into `disc/us/`, so the repository contains the following (the Japanese release goes in `disc/jp/` with the same layout): ```text disc/ `-- us/ |-- SLUS_010.13 `-- BIN/ |-- ADDHERO.BIN |-- CARDA.BIN |-- CHECKPS.BIN |-- CLOAD.BIN |-- FIELD.BIN |-- GNAME.BIN |-- GOLEM.BIN |-- GOSUB.BIN |-- GOVER.BIN |-- MENU.BIN |-- MOVIE.BIN |-- NIKI.BIN |-- SHOP.BIN |-- TITLE.BIN |-- WMAP.BIN |-- WSEL.BIN `-- ZUKAN.BIN ``` You do not need to copy the rest of the disc into the repository. To confirm the main executable is the expected version: ```bash sha1sum disc/us/SLUS_010.13 ``` Expected: ```text d11dfdd50d412ac3fa3e2eb80fbde138da118f27 disc/us/SLUS_010.13 ``` The splat configs also contain expected SHA-1 hashes for the overlay files. > `disc/` is gitignored. Never commit original game files. ### 3. Build the historical compiler images The project uses multiple historical GCC variants. Build the four local compiler images using the `old-gcc` submodule: ```bash docker build -t old-gcc/gcc-2.8.0-psx -f tools/external/old-gcc/gcc-2.8.0-psx.Dockerfile tools/external/old-gcc docker build -t old-gcc/gcc-2.7.2-cdk -f tools/external/old-gcc/gcc-2.7.2-cdk.Dockerfile tools/external/old-gcc docker build -t old-gcc/gcc-2.6.0-psx -f tools/external/old-gcc/gcc-2.6.0-psx.Dockerfile tools/external/old-gcc docker build -t old-gcc/gcc-2.7.2-psx-gnu -f dockerfiles/gnu-as.dockerfile tools/external/old-gcc ``` This is normally a one-time setup step. The development Dockerfile uses these local images; no access to a private compiler image is required. ### 4. Build the development container Run this from the repository root using [`dockerfiles/dev.dockerfile`](dockerfiles/dev.dockerfile): ```bash docker build -t lom-dev -f dockerfiles/dev.dockerfile . ``` The image contains the compilers, Psy-Q tooling, MIPS binutils, splat, maspsx, the pinned objdiff CLI, and Python dependencies used by the project. ### 5. Start the container Run this from the repository root. PowerShell, bash, or zsh: ```bash docker run --rm -it -v "${PWD}:/lom" lom-dev ``` Windows Command Prompt (`cmd.exe`): ```bat docker run --rm -it -v "%cd%:/lom" lom-dev ``` The remaining setup commands are run **inside the container**. ### 6. Split the original binaries ```bash make splat ``` This generates local build inputs such as `asm/us/`, `linker/us/`, and extracted assets under `assets/us/`. These files are intentionally not all stored in Git. Run `make splat` again after changing splat configs, segment boundaries, symbol maps, or relocation overrides. ### 7. Build Build the main executable: ```bash make ``` Output: ```text build/us/SLUS_010.13.elf ``` To also produce a flat binary: ```bash make bin ``` Build one registered overlay: ```bash make field make menu make checkps ``` Build all overlays currently registered in `mk/overlay-registry.mk`: ```bash make overlays ``` Build the main executable and all registered overlays: ```bash make everything ``` `mk/overlay-registry.mk` is the authoritative list of overlays currently wired into the linkable build. ## Normal development workflow After the initial setup, you generally do **not** need to clean the project after every edit: ```text edit source on host | v make inside lom-dev | v inspect objdiff / diff output | v edit and repeat ``` The Makefile automatically stages changed inputs before compiling. If the staged copy ever appears stale, run: ```bash make recopy ``` Use `make clean` only when you actually want to remove `build/us/` and the `/staging` copy. ## Why `/staging` exists The repository is mounted into Docker at `/lom`, but historical compilation happens from `/staging`, a native Linux filesystem inside the container. Some legacy 32-bit compiler/preprocessor binaries cannot safely `stat()` files on Windows-backed Docker bind mounts and may fail with: ```text Value too large for defined data type ``` The Makefile solves this by copying required inputs to `/staging` and normalizing text files to LF line endings before compiling. For that reason, do not bypass the build system and invoke the old compiler directly against files under `/lom`. ## Useful Make targets | Target | Purpose | |---|---| | `make` | Build the main `SLUS_010.13` ELF. | | `make bin` | Also produce `build/us/SLUS_010.13.bin`. | | `make ` | Build one registered overlay, such as `make field`. | | `make overlays` | Build all registered overlays. | | `make everything` | Build the main executable and all registered overlays. | | `make splat` | Split the main executable and all overlay configs. | | `make objdiff-objects` | Build target and reconstructed objects for objdiff. | | `make objdiff-config` | Regenerate `objdiff.json`. | | `make progress` | Generate `build/us/progress.json`. | | `make diff-all` | Run objdiff across all configured units. | | `make diff-text` | Generate compact text reports under `build/us/diffs/`. | | `make dump-objs` | Disassemble built objects for code-generation analysis. | | `make validate-assets` | Round-trip and validate format-aware assets. | | `make verify-main` | Check that the linked main executable equals `disc/us/SLUS_010.13` (`verify-slus` is an alias). | | `make verify-bins` | Run `verify-main` and all registered whole-overlay SHA-1 checks. | | `make verify-compressor` | Verify the compressor against the 17 original overlay files of the selected version. | | `make recopy` | Force source/config files to be copied to `/staging` again. | | `make clean` | Remove build output and `/staging`. | Every target builds the North American version by default. Add `VERSION=` to select another release, e.g. `make VERSION=jp`; see [Version layout](#version-layout). ## Matching functions The project uses [objdiff](https://github.com/encounter/objdiff) for local function and object comparison. Build both sides and generate the objdiff config: ```bash make objdiff-objects make objdiff-config ``` Then open objdiff and point it at the repository root. For a command-line workflow: ```bash make diff-all make diff-text ``` The compact reports are written below `build/us/diffs/`. You can also use [decomp.me](https://decomp.me) for collaborative matching. Existing source comments contain links to many decomp.me scratches; preserve those references when editing a function. ## Compiler and assembler toolchains A critical detail of this project is that **not every source file uses the same compiler configuration**. Honestly, It was a lot of "fun" dealing with this. Especially the GNU one let me tell you. The build defines 7 pipeline variants across four historical compiler builds. Compiler and assembler flags live in [`mk/toolchains.mk`](mk/toolchains.mk). Source routing and per-file overrides are in [`mk/main.mk`](mk/main.mk) and [`mk/overlay-registry.mk`](mk/overlay-registry.mk); [`mk/overlays.mk`](mk/overlays.mk) applies the overlay variants. | Pipeline | Compiler flags | Assembly path | |---|---|---| | GCC 2.8.0 G0 (default) | `-O2 -G0 -gcoff -fsigned-char` | maspsx, ASPSX 2.77, expanded division | | GCC 2.8.0 G0, unoptimized | `-O0 -G0 -gcoff -fsigned-char` | maspsx, ASPSX 2.77, expanded division | | GCC 2.8.0 G4 | `-O2 -G4 -gcoff -fsigned-char` | maspsx, ASPSX 2.77, expanded division | | GCC 2.8.0 G4, no division expansion | `-O2 -G4 -gcoff -fsigned-char` | maspsx, ASPSX 2.77, bare division | | GCC 2.7.2 CDK G0 | `-O2 -G0 -msoft-float -gcoff` | maspsx, ASPSX 2.67, expanded division | | GCC 2.7.2 GNU G0 | `-O2 -G0` | Historical GNU `as` with `-O -EL` | | GCC 2.6.0 G0 | `-O2 -G0 -gcoff -msoft-float` | maspsx, ASPSX 2.34, expanded division | ## Copyrighted data and assets Some executable/overlay ranges contain artwork, text, layouts, and other copyrighted data that should not be committed. The project uses a hybrid approach: - understood program data can be represented as typed C; - understood binary formats can use byte-exact extractors/builders; - unknown or creative data can remain as named local `databin`/`rodatabin` assets referenced with `.incbin`. ## Version layout One C source tree builds every regional release. Anything that comes from a particular disc lives in a per-version folder, and `VERSION=` on the `make` command line picks which one to build. | `VERSION` | Release | Status | |---|---|---| | `us` (default) | North America, `SLUS-01013` | Fully linked | | `jp` | Japan, `SLPS-02170` | Fully linked | | Shared by all versions | Per version | |---|---| | `src/`, `include/`, `mk/`, `tools/`, `docs/` | `disc//`, `config//`, and the generated `asm//`, `linker//`, `assets//`, `build//` | Where the code differs between releases, the shared C source uses `#if defined(VERSION_JP)` / `#if defined(VERSION_US)` blocks; the build defines exactly one of them (see [`mk/version.mk`](mk/version.mk) and [`include/version.h`](include/version.h)). Symbol names are shared, but addresses are not, so each version has its own `config//symbols/` files. Each version gets its own objdiff progress report, and CI builds both (`SLUS_010.13_report` and `SLPS_021.70_report`). Every module builds from the shared C sources in both versions. `config/jp/asm_units.txt` can still route a file to assembly for JP only, for example while porting a unit whose code differs, but it's empty now. ## Repository layout ```text lom-decomp/ |-- src/ # Reconstructed C source | |-- overlays/ # Overlay source trees | `-- psyq/ # Reconstructed Psy-Q library code |-- include/ # Project and Psy-Q headers/macros |-- config// # Splat configs, symbols, relocations |-- mk/ # Build rules and toolchain routing |-- tools/ # Decompilation, compiler, diff, and asset tools |-- docs/ # Architecture and matching documentation |-- disc// # Your local original game files (gitignored) |-- assets// # Local/generated asset data where required |-- asm// # Splat-generated target assembly (gitignored) |-- linker// # Splat-generated linker files (gitignored) |-- build// # Objects, ELFs, maps, diffs, and reports |-- dockerfiles/ # Development and CI containers |-- Makefile `-- requirements.txt ``` Useful places to start exploring: - `src/` - reconstructed game and Psy-Q code. - `asm/us/nonmatchings/` and `asm/us/overlays/*/nonmatchings/` - generated target assembly for unmatched functions. - `config/us/symbols/` - known function/global addresses. - `config/us/relocations/` - relocation overrides used when splat needs help reconstructing symbolic references. - `mk/overlay-registry.mk` - overlay source/toolchain assignments. ## Documentation Browse the [documentation index](docs/README.md) for architecture guides and project references. Useful starting points include: - [Tools index](tools/README.md) - asset tools, scene extraction and test commands. ## Troubleshooting **`make splat` reports a missing file or SHA-1 mismatch** Make sure the files are at `disc/us/SLUS_010.13` and `disc/us/BIN/*.BIN` (or `disc/jp/SLPS_021.70` and `disc/jp/BIN/*.BIN` for the Japanese version), without renaming them. **Docker cannot find an `old-gcc/...` image** Initialize the Git submodules and build the four historical compiler images from the setup section before building `lom-dev`. **GCC reports `Value too large for defined data type`** Use the Makefile instead of compiling directly from `/lom`. Run `make recopy` if the staged tree needs to be refreshed. **A symbol/config change is not reflected in generated assembly** Run `make splat` again. Do not edit generated `asm/` or `linker/` files manually. **A function matches with another compiler but not in the project build** Check its routing in `mk/main.mk` or `mk/overlay-registry.mk`. The configured historical toolchain is the authoritative one. **objdiff reports 100%, but whole-overlay verification fails** Check data/rodata jump table or case targets, relocation addends, linker section order, the overlay segment's `align:` key, and generated assets. ## Reverse-engineering provenance This project was created independently by analyzing the publicly released retail version of Legend of Mana and reconstructing its behavior and machine code through disassembly, decompilation, binary comparison, runtime analysis, and publicly available technical documentation and tools. No leaked or otherwise non-public *Legend of Mana* source code, debug symbol files, internal symbol maps, developer documentation, or other confidential materials from Square or Square Enix have been used in the creation of this project. Function names, variable names, data structures, translation-unit boundaries, and other source-level details are reconstructed or inferred from the retail binaries and observed behavior unless otherwise documented. They should not be assumed to be the names or organization used by the original developers. This repository will **never** include leaked source code, private debug symbols, confidential documentation, or other non-public materials from the original game's development. ## Legal See [LICENSE](LICENSE) and [THIRD_PARTY_NOTICES](THIRD_PARTY_NOTICES.md) for licensing details. The MIT License applies to original material authored for this project to the extent that the contributors hold the rights necessary to license that material. It does not grant rights to preexisting copyrighted material originating from the original game or from third-party SDKs, libraries, or development software. This repository is an independent reverse-engineering and preservation project. It is not affiliated with or endorsed by Square, Square Enix, Sony, or any other rights holder. No original game executable, overlay binaries, artwork, audio, or other copyrighted game data should be committed to this repository. You must supply required data from your own legally obtained copy of the game. *Legend of Mana* and related names and assets are the property of their respective owners. ## Thanks Thanks to Squaresoft and everyone who worked on *Legend of Mana*. The art, the music, and all the strange little ideas make this a game worth coming back to. You can tell the team was willing to try things, and that's a big part of what makes it interesting. Figuring out how it all works has been a lot of fun. There's still plenty to understand, but hopefully this project helps preserve that work and gives other people a chance to explore it too. ## Tools and acknowledgements This project builds on tools and research from the wider decompilation community, including: - [splat](https://github.com/ethteck/splat) - [spimdisasm](https://github.com/Decompollaborate/spimdisasm) - [maspsx](https://github.com/mkst/maspsx) - [old-gcc](https://github.com/decompals/old-gcc) - [objdiff](https://github.com/encounter/objdiff) - [decomp-permuter](https://github.com/simonlindholm/decomp-permuter) - [m2c](https://github.com/matt-kempster/m2c) - [decomp.me](https://decomp.me) - [decomp.dev](https://decomp.dev)