Splatterhouse (Xbox 360)
Splatterhouse (2010)
OriginXbox 360
TechnologyStatic recompilation (native C/C++)
Available forWindows
View official projectAbout the project
Splatterhouse (2010) es un videojuego de acci�n y aventura publicado por Bandai Namco para Xbox 360, lanzado en 2010.
Official project information
Splatterhouse PC
A native PC port of the 2010 Xbox 360 game Splatterhouse, made by statically recompiling the game's own PowerPC executable into C++ and running it on a reimplementation of the Xbox 360 platform. The game's own code does all the work. This repository supplies the console around it.
No game code or assets are included. You build the port from your own retail disc image: the build script extracts it, decrypts the executable and recompiles it on your machine. Everything derived from the disc stays in gitignored directories and is never uploaded.
- The game
- Status
- Quick start
- How it works
- Building in detail
- Running
- Repository layout
- Credits and license
The game
Splatterhouse (2010) is a reboot of Namco's 1988 arcade horror beat 'em up, released for Xbox 360 and PlayStation 3 by Namco Bandai Games. College student Rick Taylor is left dying in the mansion of Dr. Henry West. A Mayan blood mask, the Terror Mask, binds itself to him and turns him into a hulking brawler. He then fights through the mansion's horrors to rescue his girlfriend Jennifer. The game is a gory, heavy-metal third-person brawler with side-scrolling sections that nod to the original arcade game.
Under the hood it is built on licensed middleware. Static analysis of the executable found:
| Layer | Middleware |
|---|---|
| Engine, scene graph, rendering | Emergent Gamebryo 2.5 |
| Physics | Havok 5.5 |
| Menus and HUD (Flash / ActionScript 2) | Scaleform GFx 2.2 |
| Audio engine | Audiokinetic Wwise, XMA-compressed audio |
| Video | RAD Bink |
| AI navigation | BabelFlux NavPower |
The port runs all of it as the game shipped it: the Bink decoder, the Scaleform ActionScript VM and Wwise are the game's own recompiled code, not replacements.
Status
- Boots through the intro videos, the start screen, save-data setup and the main menu, with video and audio, all driven by the game's own code, and on into Story Mode.
- Level 1 (the mansion) is playable with a gamepad or the keyboard.
- Frame rate: the game paces itself at its own 30 fps cap; heavy combat can still dip below it.
SPLAT_MAX_FPS=60is an opt-in unlock; some game systems are tied to the original pacing and misbehave at 60 fps. - Later levels are untested.
- Linux only for now. On Windows the build runs up to the recompilation step; the runtime still uses Linux APIs.
Quick start
You need Linux on x86-64 with an AVX2 CPU (Haswell / Zen or newer), a Vulkan GPU, about 6 GB of free disk space, and an ISO dump of the retail disc.
1. Install the build tools (Ubuntu / Debian; other distributions are listed below):
sudo apt install clang cmake ninja-build git ca-certificates pkg-config python3 python3-venv \
libsdl2-dev libvulkan-dev libshaderc-dev libavcodec-dev libavutil-dev
2. Clone and build:
git clone https://github.com/mattfj10/splatterhouse-PC.git
cd splatterhouse-PC
python3 tools/build_from_iso.py /path/to/Splatterhouse.iso
3. Play:
dist/run.sh
The build takes a few minutes. It checks your tools first and prints the exact install command
for anything missing. When it finishes, dist/ is a self-contained, runnable copy of the game.
How it works
Emulation vs. static recompilation
An emulator like Xenia reads the game's PowerPC machine code while the game runs and translates it on the fly, using an interpreter or a JIT. Static recompilation does that translation once, ahead of time: every function in the executable becomes a C++ function, and a normal C++ compiler turns them into a native x86-64 program. At run time no PowerPC code is decoded. What runs is ordinary compiled code that behaves instruction for instruction like the original.
The recompiled code still expects to be on an Xbox 360. It calls the console's kernel, its GPU writes into GPU memory and it expects hardware interrupts. So the second half of the project is a runtime: a host implementation of those platform services. This is the same model Unleashed Recompiled uses for Sonic Unleashed.
The build pipeline
retail ISO
│ verify-iso SHA-256 against the known retail dump
▼
XDVDFS disc image ──extract──▶ extracted/ game files (5.1 GB)
│ default.xex encrypted, compressed executable
▼
decrypt (tools/xex_decrypt.py)
│ XEX2 container → AES-decrypt + LZX-decompress → flat PowerPC image
▼
work/default.exe loaded at guest address 0x82000000
│
▼
recompile (tools/recompile.py → XenonRecomp + recomp/*.toml + patches/)
│ every guest function → C++; kernel imports → stub table
▼
work/xenon/ppc/*.cpp ~230 MB of generated C++
│
▼
build (CMake + clang + ninja)
│ generated game code + runtime/ → one native executable
▼
dist/splatterhouse + run.sh
- XEX decryption. Xbox 360 executables ship as XEX2 containers: a header plus an
AES-encrypted, LZX-compressed PE image.
tools/xex_decrypt.pyunwraps it. It uses the publicly known retail key, the same one Xenia and other XEX tools use, and a Python port of libmspack's LZX decoder. - Recompilation. XenonRecomp is cloned at a
pinned commit and patched with
patches/xenonrecomp-s96-consolidated.patch. Each patch fixes a code-generation bug this game exposed, for example update-form indexed loads and stores that emitted no code.recomp/splatterhouse.tomlandrecomp/switch_table_overrides.tomltell it what static analysis cannot work out alone: function boundaries and the targets of indirectbctrjump tables. - No game code is committed. Steps 2–4 run on your machine from your disc. Their outputs
(
extracted/,work/,dist/) are gitignored.
The runtime
The runtime (runtime/) is the "console" the recompiled game runs on. It follows one rule:
the game makes every decision. The runtime answers the way the hardware and kernel would
have. It never drives the game's menus, picks its assets or draws its UI on the game's behalf.
| Subsystem | What the game expects | What the runtime does |
|---|---|---|
Memory (main.cpp, kernel/phys_layout.h) | A 32-bit address space with the executable at 0x82000000, and 512 MB of physical RAM visible through several aliased windows (cached, write-combined, physical) | Reserves 4 GiB of host address space and maps the image. Backs physical RAM with one memfd mapped at every alias, so a write through one window is visible through the others, as on the console. |
CPU and threads (kernel/xboxkrnl_sync.cpp) | Six hardware threads, ExCreateThread, kernel events, semaphores, mutants, critical sections, DPCs and timers | Each guest thread is a host thread. The kernel synchronisation objects follow the console's semantics, including thread affinity and the guest timebase (mftb, 50 MHz from the host clock). |
Kernel and system (kernel/xboxkrnl_*.cpp, kernel/xam.cpp) | xboxkrnl.exe and xam.xex imports: file I/O, memory allocation, clocks, the user profile, content and save devices, the system UI | High-level emulation (HLE) of each import the game uses. File I/O serves the extracted disc files; saves go to work/xcontent/. |
GPU (kernel/xboxkrnl_video.cpp, gpu/) | The Xenos GPU: a PM4 command ring, register writes, shader microcode, a 10 MB EDRAM render target with resolve copies, and vblank and command-completion interrupts | A command processor reads the game's own ring buffer and runs its draws on Vulkan. Xenos shader microcode is translated to GLSL (gpu/xenos_shader.cpp) and compiled to SPIR-V with shaderc. Textures are untiled and converted from Xenos formats. EDRAM resolves become render-to-texture copies. Vblank and completion interrupts are delivered through the callback the game registered (VdSetGraphicsInterruptCallback). |
Audio (kernel/xma_decoder.cpp, kernel/xboxkrnl_audio.cpp) | The XMA hardware decoder, driven through context structures and MMIO registers, and the audio driver's render callback | Models the XMA decoder hardware with libavcodec's XMA2 decoder. Wwise (game code) fills the contexts and mixes the result; the output goes to SDL audio. |
Input (gpu/renderer.cpp, kernel/xam.cpp) | XamInputGetState (the console's XInput) | SDL game controller and keyboard events, delivered through the XInput API. |
| Video | Nothing special: Bink is game code | The game's own Bink decoder produces every video frame; the runtime only presents what the GPU draws. |
Where Xenia (and others) come in
Xenia, the open-source Xbox 360 emulator, is the
reference for how the console behaves. The runtime is not built on Xenia's code base, but much of
it follows Xenia's documented semantics: kernel import behaviour, GPU register meanings,
texture and vertex formats and their tiling, culling and depth-bias rules, the XMA context layout.
Where an implementation follows Xenia closely, its BSD-3 notice applies (see
THIRD_PARTY_NOTICES.md).
- The shader decoder is adapted from XenosRecomp's (the shader recompiler of Unleashed Recompiled). XenosRecomp builds on Xenia's microcode definitions. Here it emits GLSL for Vulkan instead of HLSL.
- ReXGlue SDK (derived from Xenia) was studied as a reference for interrupt delivery in a recompilation runtime.
- XenonRecomp itself comes from the Unleashed Recompiled authors.
The difference from an emulator: Xenia must emulate everything generically for every game, while this port runs one game's precompiled code natively. Where something goes wrong, the fix goes into a faithful platform primitive, never into patching the game.
Building in detail
Requirements
-
Linux on x86-64 with an AVX2-capable CPU (Haswell / Zen or newer).
-
A Vulkan-capable GPU and driver (Mesa for AMD/Intel, or NVIDIA's driver).
-
The build tools from your distribution's packages (below), or, alternatively, Nix.
-
Network access for the first build. The recompiler source is cloned from GitHub at a pinned commit. Two Python packages are installed from PyPI into a private environment if your Python does not already have them.
-
About 6 GB of free disk space on top of the ISO: 5.1 GB of extracted game files plus ~0.5 GB of generated C++ and build tree. The output directory hard-links the game files instead of copying them.
-
A dump of the retail disc as an ISO. The script checks it against the image the port is developed against:
Size 7838695424 bytes SHA-256 303fa98211064e024fcc829be287270ec9e18f5586a71168e8493aea00ecb1c7Executable title ID 4E4D07F0, media ID6668E457, XEX version 3, region-freeOther dumps or revisions stop with a warning.
--forcecontinues anyway, but the recompiler configuration hard-codes addresses of this exact executable.
Build without Nix
This is the standard path.
-
Install the build tools from your distribution:
Distribution Command Debian / Ubuntu sudo apt install clang cmake ninja-build git ca-certificates pkg-config python3 python3-venv libsdl2-dev libvulkan-dev libshaderc-dev libavcodec-dev libavutil-devFedora sudo dnf install clang cmake ninja-build git pkgconf-pkg-config python3 SDL2-devel vulkan-loader-devel vulkan-headers libshaderc-devel ffmpeg-free-develArch Linux sudo pacman -S --needed clang cmake ninja git pkgconf python sdl2 vulkan-headers vulkan-icd-loader shaderc ffmpegopenSUSE sudo zypper install clang gcc-c++ cmake ninja git pkgconf-pkg-config python3 SDL2-devel vulkan-devel shaderc-devel ffmpeg-7-libavcodec-devel ffmpeg-7-libavutil-develHow each line was tested:
- Ubuntu 24.04: a complete build and boot in a clean
ubuntu:24.04container. - Fedora 44, Arch, openSUSE Tumbleweed: checked in clean containers, not by a full build. Every package installs, every pkg-config module and header is found, and clang++ compiles and links a C++20 program against them.
- Audio: sound needs libavcodec's XMA2 decoder. Debian/Ubuntu's and Arch's FFmpeg
have it. Fedora's
ffmpeg-freeand openSUSE'sffmpeg-7do not: the game builds and runs, but silently. Use RPM Fusion'sffmpegor Packman'sffmpeg-7there. The build script checks for the decoder and warns.
- Ubuntu 24.04: a complete build and boot in a clean
-
From the repository root:
python3 tools/build_from_iso.py /path/to/Splatterhouse.iso
Pre-flight check. Before building, the script checks:
- every tool and library (via pkg-config);
- the C++ compiler, by compiling and linking a test program;
- the XMA2 decoder.
Whatever is missing is listed in a table with the exact install command for your distribution.
Python. Python 3.11 or newer runs the build steps. Python 3.9 can start the script, which then
switches to a python3.11+ on PATH by itself. The script needs pycryptodome and capstone. It
uses your Python's copies if present; otherwise it creates a private environment work/pyenv and
pip-installs pinned versions there. Nothing is installed system-wide.
Disc extraction. extract-xiso is not needed. The built-in tools/xiso.py produces exactly
the same files; python3 tools/test_xiso.py --compare <iso> checks that against an installed
extract-xiso.
The script runs these steps, with progress output and a per-step log in work/build-logs/:
- verify-iso: SHA-256 of the image against the known retail dump.
- extract: the disc's game partition →
extracted/(extract-xisowhen it is onPATH, elsetools/xiso.py). - decrypt:
default.xex→ decrypted, decompressed imagework/default.exe. - recompile: build the patched XenonRecomp, translate the game's code into
work/xenon/ppc/*.cpp, generate the kernel-import stubs. - symbols: the (optional) guest symbol table.
- build: Release build of
runtime/with clang + ninja. This is the long step: the translated game is ~230 MB of C++. - assemble: a ready-to-run directory,
dist/by default.
A failing step stops the build with BUILD FAILED in step '<step>' and the path of its log.
A full run takes a few minutes on a 16-thread desktop: 2m17s measured with the Nix toolchain, about half of it the compile. The build is resumable. Re-running skips every step whose outputs are already up to date, so after an interruption or a source change, just run it again.
| Option | |
|---|---|
--out <dir> | where to put the runnable game (default dist/) |
--clean | redo every step from scratch (saves are kept) |
--force | continue with an ISO that does not match the known hash |
-j N | parallel compile jobs (default: all CPUs) |
--nix | use the pinned Nix toolchain instead of the system's tools (below) |
--no-nix | never use Nix, even when tools are missing (stop with the install command instead) |
Other entry points:
python3 tools/build_from_iso.py --dry-run-platform linuxruns only the tool check and prints the table.python3 tools/recompile.pyruns step 4 on its own. It needsextracted/andwork/default.exefrom steps 2–3.python3 tools/test_build_pipeline.pyandpython3 tools/test_xiso.pyunit-test the build script's platform layer and the disc extractor.
Build with Nix
This is the alternative path, and the one the maintainer uses. flake.nix and flake.lock pin
the whole toolchain (clang, cmake, ninja, SDL, FFmpeg, Vulkan loader, shaderc, extract-xiso, and
Python with pycryptodome and capstone), so nothing else needs to be installed. It needs
Nix; the script enables the nix-command flakes features itself.
python3 tools/build_from_iso.py --nix /path/to/Splatterhouse.iso
- What
--nixdoes: re-runs the script insidenix develop .#build. Onlyflake.nix/flake.lockare staged intowork/nix-flake, so the repository and the ISO are never copied into the Nix store. - The automatic fallback: without
--nix, Nix is used only when some system tool is missing and Nix is installed. The script says when this happens;--no-nixturns it off. - Linking: the binary then links the Nix store's libraries, registered as GC roots under
<out>/.gcroots. - GPU drivers: on distributions other than NixOS, Nix-built programs sometimes cannot find the host's GPU driver. A wrapper such as nixGL solves that, or build without Nix.
bash tools/build_from_iso.sh … still works: it is a thin wrapper that runs the Python script.
Windows
Not finished yet. py tools\build_from_iso.py <iso> runs steps 1–5 on Windows (verify,
extract, decrypt, recompile, symbols; XenonRecomp supports Windows). It then stops before the
runtime build, because runtime/ still uses Linux APIs: memfd, mmap placement, sigaction,
prctl, /proc.
Tools. There is no Nix on Windows. The tools come from PATH, or from these variables set to
the full path of each .exe: CMAKE, NINJA, CC/CXX (clang-cl or clang), EXTRACT_XISO
and GIT.
- Run the script from an x64 Native Tools Command Prompt for VS, so clang finds the MSVC linker and the Windows SDK.
- Needed: CMake, Ninja, LLVM/clang, Git and Python 3.11+. pycryptodome and capstone are
pip-installed into
work\pyenvwhen missing. extract-xiso is optional. - The runtime will also need the Vulkan SDK (headers, loader, shaderc), SDL2 and the FFmpeg development libraries.
Missing tools are listed in a table with where to get them.
python3 tools/build_from_iso.py --dry-run-platform windows prints that table on any OS without
building anything. The output launcher on Windows is run.bat.
Running
dist/run.sh
What dist/ contains:
- the executable
splatterhouse; work/default.exe, the decrypted game executable it loads;extracted/, the game files;run.sh, which changes into that directory and starts the game.
No arguments or environment variables are needed. Saves are written to dist/work/xcontent/.
Esc or closing the window quits.
Controls
A gamepad works out of the box (SDL game-controller mapping, Xbox layout). Keyboard:
| Key | Xbox 360 button |
|---|---|
| Enter | START |
| Space / Z | A |
| X / Backspace | B |
| C | X |
| V | Y |
| Arrow keys | D-pad |
| Q / E | LB / RB |
| Tab | BACK |
The keyboard has no analog-stick or trigger mapping, so a gamepad is recommended for play.
Run-config options
None is needed to play; dist/run.sh --help lists all of them. The ones worth knowing:
SPLAT_MAX_FPS=60: opt-in enhancement that lifts the game's own 30 fps cap. Known issue: some game systems are tied to the original pacing and misbehave at 60 fps. Default: off (the game paces itself at 30 fps, as on the console).SPLAT_FPS_CAP=<fps>: an extra host-side main-loop sleep cap (default off).
Example: SPLAT_MAX_FPS=60 dist/run.sh.
Repository layout
| Path | Contents |
|---|---|
tools/build_from_iso.py | The end-to-end build (ISO in, dist/ out). |
tools/recompile.py | The recompilation step on its own. |
tools/build_platform.py | The build's OS layer: tool discovery and checks, distribution install commands, the Nix choice, work/pyenv, launcher, links. |
tools/xiso.py | Built-in Xbox / Xbox 360 disc (XDVDFS) extractor. |
tools/xex_decrypt.py, tools/xex_info.py | XEX2 decryption and inspection. |
recomp/ | XenonRecomp configuration: function boundaries, jump-table overrides. |
patches/ | Fixes to XenonRecomp's code generation. |
runtime/main.cpp | Loader: guest address space, image mapping, entry point. |
runtime/kernel/ | xboxkrnl and XAM HLE, XMA decoder, audio driver. |
runtime/gpu/ | Xenos command processing → Vulkan renderer, shader translator. |
runtime/debug_hooks.cpp | Per-function overrides and run-config options. |
docs/ | Design notes and investigations (below). |
flake.nix | Nix toolchain. nix develop gives the full reverse-engineering environment; nix develop .#build gives the lean build environment. |
Useful starting points in docs/:
subsystem-map.md: the game's subsystems;gpu-design.mdandtexture-rt-design.md: the renderer;audio-hle.md: audio;jumptables.md: recompiler jump tables;scheduler-model.md: thread scheduling;frontend-authentic.md: the session-by-session development log;TODO.md: open work.
Credits and license
This project's code is MIT-licensed (see LICENSE). Third-party notices are in
THIRD_PARTY_NOTICES.md.
Thanks to:
- the Xenia project, whose emulator and research are the reference for Xbox 360 hardware and kernel behaviour;
- hedge-dev's XenonRecomp and XenosRecomp, and Unleashed Recompiled, which proved this approach;
- the ReXGlue SDK;
- extract-xiso;
- libmspack.
Splatterhouse is a trademark of Bandai Namco Entertainment. This project is not affiliated with or endorsed by Bandai Namco Entertainment or Microsoft. You need your own copy of the game.
