th06: Byte-for-Byte Decompilation of Touhou Embodiment of Scarlet Devil
Up front: I didn’t write this, I forked it. th06 is the reverse-engineering project by the happyhavoc (GensokyoClub) crew for Touhou Embodiment of Scarlet Devil (東方紅魔郷 ~ the Embodiment of Scarlet Devil 1.02h). The goal is to reconstruct ZUN’s original C++ source bit for bit. I grabbed it purely because it sits right at the intersection of three things I care about: low-level, reverse engineering, and old games.
What’s tempting is the match decompilation approach. It’s not about understanding the logic and rewriting something roughly equivalent. It requires that the machine code your C++ produces, once run through that era’s compiler, be byte-for-byte identical to the original 東方紅魔郷.exe. So it’s not just restoring logic, you also have to reproduce the compiler’s quirks: the order variables get pushed onto the stack, its habits in register allocation. Way more interesting than just cracking a game, it forces you to understand things down to the bone.
What gets me most is the obsessiveness of the toolchain. The whole build is Visual Studio 2002 plus DirectX 8.0, and even those are old versions salvaged off the Web Archive. The reason is straightforward: ZUN used this exact setup back then, and if you want the bytes to line up you have to use the same blade. On Linux/macOS you run it through wine, and on macOS they recommend CrossOver to work around CL.EXE’s heap issues. python scripts/create_devenv.py pulls down and configures the compiler, libraries, and tools, then python3 ./scripts/build.py generates build.ninja and hands it off to ninja to compile.
The reverse-engineering half uses Ghidra. The RE results live in the th06-re repo and sync nightly from their Ghidra Server. The actual work runs through objdiff, which breaks the original exe into individual objects. When you finish rewriting a function it tells you on the spot where it differs from the original: a sea of red means you haven’t matched it, all green is what counts. To find something to do, look in config/stubbed.csv, which is full of temporarily stubbed functions. Pick one, copy the declaration into the corresponding cpp, and grind through it against the Ghidra decompilation output. This feedback loop is genuinely clever.
There’s one detail that’s very telling: to make modern MSVC7 reproduce that era’s variable layout, the project ports in a dedicated var_order pragma. This kind of little wheel built for hardcore compatibility is exactly the charm of match decomp. It also has a portable branch that moves the game onto Linux and modern Windows, so the byproduct of the decompilation can turn into an actually playable port. That line is worth following too.
For now I haven’t submitted any reconstruction code, I’m just reading it as reverse-engineering practice. Parking it here for the record, for the day I get the itch to pick a stubbed function and match it.
- Repo: https://github.com/dwgx/th06 (see happyhavoc/th06 for the original project)