Wild 9
OriginPlayStation
TechnologyNative C/C++ port (engine reimplementation)
Available forWindows, Linux, macOS
View official projectAbout the project
Wild 9 es un videojuego de acción y aventura publicado por Interplay Entertainment en 1998.
2 versions of Wild 9
You're viewing: Wild 9
Official project information
Wild 9 Decompilation
Matching decompilation of Wild 9 for the Sony PlayStation 1, NTSC-U executable SLUS_004.25
Overview
This project reconstructs the NTSC-U Wild 9 PlayStation 1 executable as matching C source and MIPS R3000A assembly. The primary goal is a byte-for-byte matching rebuild of the original executable (SLUS_004.25).
The codebase is structured around progressive decompilation. Matched functions are written as standalone C files under src/functions/ and promoted into src/main.c, replacing the corresponding INCLUDE_ASM disassembly entries. Unmatched code remains as generated assembly slices. Each promoted function is independently compiled, linked, and matched against the original binary before being patched into the final rebuilt executable.
Useful links
Project status
Work in progress. See the decomp.dev progress report for current coverage.
This repository is not a playable version of Wild 9. It contains the decompilation sources, build scripts, tools, and generated code required to reproduce the original executable from a legally obtained copy of the game. The original game executable and other copyrighted game assets are not included in this repository.
For a playable native PC recompilation of Wild 9, see Wild 9 Recomp.
Features
- Byte-for-byte matching rebuild against NTSC-U executable
SLUS_004.25. - Splat-based executable splitting and disassembly generation.
- Automated per-function assembly splitting (
split_800_functions.py) intoasm/nonmatchings/main/. - GCC 2.8.1 PSX compiler toolchain with
maspsx(ASPSX 2.77) assembly translation. - Isolated GNU binutils 2.42 environment targeting
mipsel-linux-gnu. - In-tree toolchain download and verification (
make toolchain). - Per-function standalone compilation, linking, and byte-exact verification.
- Automated function promotion workflow integrating matching C code into the main translation unit.
- Submodule integration for decompilation tools including
asm-differ,decomp-permuter,m2c,m2ctx,maspsx, andmipsmatch.
Requirements
The build and matching toolchain targets x86_64 Linux.
Host packages:
# Debian or Ubuntu
sudo apt install git make python3 python3-pip python3-venv curl binutils zstd file clang-format
# Arch Linux, Manjaro, or CachyOS
sudo pacman -S git make python python-pip curl binutils zstd file clang
Target game data:
A legally obtained original NTSC-U Wild 9 PlayStation executable must be placed at:
iso/us/SLUS_004.25
Expected file identity:
Size: 425984 bytes
SHA-1: 1dac75ee3a0ef5a62e025a85183076fa1a6cf824
SHA-256: ab280169ed52906589b8353a00c5cb4e9851da21d1023c98569de51958a44012
The original executable is not included in this repository.
Setup
1. Clone the repository
Clone the repository with all Git submodules:
git clone --recursive https://github.com/plinkr/Wild9-Decompilation.git
cd Wild9-Decompilation
If the repository was cloned without --recursive, initialize the submodules:
make tools
2. Python environment
Create the Python virtual environment and install pinned dependencies:
make install
This installs splat64 and spimdisasm into .venv.
Whenever you execute Python scripts or tools directly without Make, you must run them within this virtual environment. You can either activate the virtual environment in your current shell:
source .venv/bin/activate
python scripts/extract_psx_exe.py ...
Or invoke the virtual environment Python interpreter directly without activating:
.venv/bin/python scripts/extract_psx_exe.py ...
3. Toolchain installation
The matching build requires GCC 2.8.1 PSX and GNU binutils 2.42 (mipsel-linux-gnu). Both are installed into tools/toolchain/ without modifying system packages or requiring root access.
Install both automatically using the Makefile:
make toolchain
This downloads the precompiled GCC 2.8.1 PSX archive (decompals/old-gcc release 0.17) and the Ubuntu Noble binutils 2.42 packages, validates their SHA-256 checksums, and extracts them into tools/toolchain/.
To verify that the in-tree toolchain is present:
make check-toolchain
If you prefer to download and unpack the packages manually, see docs/TOOLCHAIN.md.
4. Load the environment (required for commands without Make)
The Makefile automatically exports the private binutils PATH and LD_LIBRARY_PATH to all recipes, so executing make works without any manual environment setup.
However, to execute any commands without Make in your shell (such as mipsel-linux-gnu-as, mipsel-linux-gnu-ld, mipsel-linux-gnu-objcopy, or standalone compilation scripts), you must first load the binutils environment:
source scripts/binutils-env.sh
5. Install the original executable
Wild 9 (NTSC-U) ships as SLUS_004.25. You need this executable in iso/us/ before building.
Extract it from your own Wild 9 disc image
If you have a Redump-style dump of Wild 9 (.cue + .bin, or a bare .bin), use the helper script in this repo:
.venv/bin/python scripts/extract_psx_exe.py "/path/to/Wild 9 (USA).cue"
The script reads the filesystem inside the dump, follows SYSTEM.CNF to the boot executable, and writes SLUS_004.25 to the current directory. Then move it into place:
mkdir -p iso/us
mv SLUS_004.25 iso/us/SLUS_004.25
You can also pass the output path directly:
.venv/bin/python scripts/extract_psx_exe.py "/path/to/Wild 9 (USA).bin" iso/us/SLUS_004.25
Or copy it manually
If you already have SLUS_004.25 extracted (for example from DuckStation's disc browser), just place it at:
mkdir -p iso/us
cp /path/to/SLUS_004.25 iso/us/SLUS_004.25
Either way, the final layout must be:
iso/us/SLUS_004.25
6. Verify the toolchain
Verify the installed tools:
make versions
make check-toolchain
Build
Splat disassembly and per-function splitting
Disassemble the original binary and generate per-function assembly:
make split
Splat disassembles the main code segment into a single file, asm/800.s, but does not split it into functions. make split runs both stages:
- Splat disassembles
iso/us/SLUS_004.25intoasm/800.sand data files. scripts/split_800_functions.pywrites each function fromasm/800.stoasm/nonmatchings/main/func_XXXXXXXX.s.
On a clean checkout, make (or make all) runs both stages automatically before building.
Re-splitting functions without re-running Splat
If asm/800.s already exists and you only want to regenerate the per-function files under asm/nonmatchings/main/:
make split-functions
You can also run the script directly using the virtual environment:
.venv/bin/python scripts/split_800_functions.py asm/800.s asm/nonmatchings/main
Recreating src/main.c (opt-in)
The repository provides a script to regenerate src/main.c with an INCLUDE_ASM stub for every function found in asm/800.s:
make generate-includes
Or run the script directly using the virtual environment:
.venv/bin/python scripts/generate_include_asm.py asm/800.s src/main.c
This is opt-in because src/main.c is the primary translation unit tracked in git. As functions are decompiled and matched, their INCLUDE_ASM calls are replaced with #include "functions/func_XXXXXXXX.c". If make split or make build regenerated src/main.c automatically, it would wipe out all promoted C functions. Only run make generate-includes if you are bootstrapping from scratch or intentionally resetting the translation unit.
Building and verifying
Build and verify the baseline all-assembly reference:
make original-verify
Build the target executable:
make build
Verify that the built executable matches the original:
make verify
The build process starts from the byte-exact reference and replaces promoted functions defined in src/main.c. Each promoted function is matched independently before its bytes are patched into the final executable.
Matching
Each decompiled function lives in its own file under:
src/functions/func_XXXXXXXX.c
To build and compare a standalone function against the original binary:
make match FUNCTION=func_XXXXXXXX
For example:
make match FUNCTION=func_800101A4
A successful match prints:
func_800101A4: MATCH
The standalone matching pipeline is:
src/functions/func_XXXXXXXX.c
-> C preprocessor (CPP)
-> GCC 2.8.1 PSX compiler
-> scripts/prepare_function_asm.py (small-data handling and ASPSX
delay-slot store preparation via scripts/aspsx_delay_slot.py)
-> maspsx (ASPSX 2.77 translation)
-> GNU assembler 2.42 (mipsel-linux-gnu-as)
-> scripts/link_function.py (standalone link at original address)
-> scripts/match_function.py (byte comparison against original binary)
The project does not modify the tools/maspsx submodule. scripts/prepare_function_asm.py handles small-data preparation before invoking maspsx, and scripts/aspsx_delay_slot.py restores the ASPSX expansion of bare macro stores that belong in a jump delay slot (lui $at,%hi(sym) hoisted before the jump, sw $reg,%lo(sym)($at) in the slot). The delay-slot rewrite only applies where the reference assembly proves the store is a delay-slot instruction.
Per-function compiler flags
Some parts of the original executable were compiled with different flags. A function that needs one declares it in its own C file, on a line of its own:
// CFLAGS: -fno-delayed-branch
The %.gcc.s rule reads that line and appends the flags to the compiler command. Only flags listed in FUNCTION_CFLAGS_ALLOWED in the Makefile are accepted; anything else stops the build. The directive applies to the standalone pipeline (make match, make build, make verify), which produces the patched executable. src/main.c (make c) is one translation unit and ignores it.
Known case: -fno-delayed-branch leaves the jr $ra delay slot as an explicit nop, which GCC 2.8.1 at -O2 would otherwise fill. Most functions in 0x80057xxx-0x80067xxx need it.
Known case: -mno-split-addresses makes GCC emit address macros such as lbu $2,sym($3) instead of an explicit lui %hi / addiu %lo pair. The assembler then expands them through $at (lui $at,%hi(sym) / addu $at,$at,$3 / lbu $2,%lo(sym)($at)). Some Sony library code in the same region was built this way, for example func_80058CC4 (toupper) and func_80058CF8 (tolower). Neighboring functions use the split form, so declare this flag per function and never apply it globally.
Use the directive only when the C is already correct and the flag is the sole remaining difference, verified by matching with and without it.
Development Workflow
1. Decompiling a function
Find an unpromoted function in src/main.c. Unpromoted functions appear as:
INCLUDE_ASM("asm/nonmatchings/main", func_XXXXXXXX);
The disassembled assembly generated by make split is located at:
asm/nonmatchings/main/func_XXXXXXXX.s
If asm/nonmatchings/main/ is missing, run make split-functions to regenerate it from asm/800.s.
Create a standalone C source file:
$EDITOR src/functions/func_XXXXXXXX.c
Write the C implementation using fixed-width types from include/types.h and shared declarations from include/functions.h, include/globals.h, and include/decomp.h.
2. Testing and matching
Compile and compare the function:
make match FUNCTION=func_XXXXXXXX
Iterate on the C code until make match reports MATCH. Decompilation helper submodules under tools/ (such as asm-differ or decomp-permuter) can be used to analyze differences.
3. Promoting a matched function
Once make match succeeds, promote the function:
make promote FUNCTION=func_XXXXXXXX
make promote performs the following steps:
- Verifies the standalone match with
make match. - Replaces the
INCLUDE_ASM("asm/nonmatchings/main", func_XXXXXXXX);line insrc/main.cwith#include "functions/func_XXXXXXXX.c". - Rebuilds the executable with
make build. - Runs
make verifyto ensure the entire binary matches byte-for-byte. - If verification fails, it automatically restores
src/main.cfrom backup.
4. Formatting
Format C source and header files:
make format
This applies the .clang-format rules and prepends the project SPDX license identifier to any C or header file that lacks it:
// SPDX-License-Identifier: AGPL-3.0-or-later
Do not alter code purely for formatting if the change modifies the generated assembly. Refer to docs/STYLE.md for coding and formatting conventions.
Project Structure
src/
main.c
Main translation unit containing promoted C includes and remaining INCLUDE_ASM macros.
functions/
Standalone C source files, one per decompiled function.
include/
types.h
Fixed-width integer types (u8, s8, u16, s16, u32, s32, bool).
functions.h
Shared function declarations.
globals.h
Shared game global variable declarations.
decomp.h
Decompilation helpers, hardware structures, and macros.
include_asm.h
INCLUDE_ASM and INCLUDE_RODATA macro definitions.
macro.inc
Assembler macros.
labels.inc
Assembler label declarations.
gte_macros.inc
PlayStation Geometry Transformation Engine (GTE) assembly macros.
config/slus_004.25/
splat.yaml
Splat disassembly and section split configuration.
toolchain.json
Compiler and assembler flags and tool versions.
SLUS_004.25.sha1
SHA-1 checksum of the original executable.
SLUS_004.25.sha256
SHA-256 checksum of the original executable.
symbols/
slus_004.25.txt
Hardware register mappings, BIOS symbols, and known addresses.
scripts/
binutils-env.sh
Sets PATH and LD_LIBRARY_PATH for the private binutils 2.42 environment.
extract_psx_exe.py
Extracts the boot executable from a .cue/.bin disc image.
split_800_functions.py
Splits asm/800.s into per-function assembly files in asm/nonmatchings/main/.
generate_include_asm.py
Generates src/main.c with INCLUDE_ASM stubs from asm/800.s (opt-in).
match_function.py
Compares standalone function binary against the original executable.
link_function.py
Links a standalone function object at its original address.
prepare_function_asm.py
Prepares standalone GCC assembly output before passing to maspsx.
prepare_main_asm.py
Prepares main translation unit assembly output.
aspsx_delay_slot.py
ASPSX-compatible expansion of bare macro stores that belong in a jump
delay slot (used by the two prepare scripts above).
promote_function.py
Replaces INCLUDE_ASM entries with C includes in src/main.c.
normalize_splat_config.py
Normalizes generated Splat split configuration.
inspect_psx_exe.py
Parses and displays PlayStation executable header information.
check_prototypes.py
Verifies header prototypes match C function signatures.
sort_declarations.py
Sorts declarations in header files.
generate_objdiff_config.py
Generates the objdiff project configuration used for progress reports.
docs/
STYLE.md
C style guide, naming conventions, and matching guidelines.
TOOLCHAIN.md
Detailed toolchain setup instructions and package references.
REPOSITORY.md
Repository clean-room reverse engineering policy.
tools/
Git submodules for decompilation tooling and local toolchain files.
Generated assembly (asm/), per-function assembly (asm/nonmatchings/main/), linker scripts, object files, and build outputs (build/) are ignored by Git.
Useful Commands
make help # Show available Makefile targets
make tools # Initialize the Git submodules
make install # Create .venv and install pinned Python dependencies
make toolchain # Install GCC 2.8.1 PSX and binutils 2.42 in-tree
make check-toolchain # Report whether the in-tree toolchain is present
make versions # Print Python, splat, and spimdisasm versions
make info # Display PS-X EXE header fields
make fingerprint # Record SHA-1 and SHA-256 of the target binary
make split # Run Splat and regenerate per-function ASM
make split-functions # Regenerate asm/nonmatchings/main/ from asm/800.s
make generate-includes # Regenerate src/main.c INCLUDE_ASM stubs
make original-verify # Build and verify the baseline assembly reference
make build # Build the final executable with promoted functions
make verify # Compare build/SLUS_004.25 with the original
make match FUNCTION=func_XXXXXXXX # Build and match a single function
make promote FUNCTION=func_XXXXXXXX # Promote a matched function in src/main.c
make check-prototypes # Verify header prototypes against C definitions
make sort-declarations # Sort declarations in functions.h and globals.h
make objdiff-report # Write .decomp/report.json (needs objdiff-cli)
make format # Format C/header files with clang-format
make clean # Remove build artifacts and nonmatchings
make distclean # Remove build artifacts and Python virtualenv
Contributing
- Keep one decompiled function per file under
src/functions/func_XXXXXXXX.c. - Keep promoted functions in original address order in
src/main.c. - Follow the conventions in
docs/STYLE.md. - Before submitting a pull request, run:
make format make verify - Do not commit original game executables, disc images, extracted assets, generated assembly, object files, or other build artifacts.
License
Project source code, scripts, configuration, and documentation are licensed under the GNU Affero General Public License version 3. See LICENSE.md.
The Wild 9 executable, disc data, artwork, audio, and other original game assets are copyrighted by their respective rights holders and are not licensed by this project.
