# N64Recomp: static recompilation of N64 binaries into C

> N64Recomp turns a MIPS N64 binary plus an ELF symbol list into C functions you compile yourself. It is a recompiler, not an emulator, and the runtime is a separate repository.

**N64Recomp/N64Recomp** — Tool to statically recompile N64 games into native executables

- Repository: https://github.com/N64Recomp/N64Recomp
- Stars: 8,174 · Forks: 451
- Language: C++
- License: MIT
- Published: 2026-09-22 · Updated: 2026-09-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/n64recomp-n64recomp

## What N64Recomp actually produces

The README opens with a precise claim: N64: Recompiled is a tool to statically recompile N64 binaries into C code that can be compiled for any platform. That sentence is the whole scope. The output is C source, not a runnable program. You still need a C compiler, and you still need a runtime that supplies the macros and hardware behaviour the generated code calls into.

The stated audiences are ports and tools, plus any context where you want part of an N64 binary to run standalone. The README also frames simulation speed as a motivation: static recompilation can simulate behaviour significantly faster than interpreters or dynamic recompilation. That is a design claim about the approach, not a number, and the repository does not publish a benchmark.

The project is not the first of its kind, and the README says so. It cites jamulator for NES binaries and ido-static-recomp, which recompiles the SGI IRIX IDO compiler on modern systems for matching decompilation, as its main inspiration. So the honest framing is: this is the N64 instance of a known technique, with the extra work of handling overlays and jump tables.

## Instruction-by-instruction translation and the LOOKUP_FUNC escape hatch

The core loop is literal. The recompiler takes the binary plus a list of symbols and metadata, splits it into functions, and emits one C function per input function, named from the metadata. Instructions are processed one at a time and the corresponding C is written as each one is handled. The README gives `addiu $r4, $r4, 0x20` as the example, and says it becomes `ctx->r4 = ADD32(ctx->r4, 0X20);`. Registers live in a context struct, and arithmetic goes through macros the runtime defines.

Control flow is where the interesting decisions are. `jal` becomes a direct function call. Unconditional jumps and branches that can be identified as tail-call optimizations also become function calls. Branch delay slots are handled by duplicating instructions as needed. A `jr` instruction becomes a switch-case when the recompiler can tell it is being used with a jump table.

Indirect calls cannot be resolved statically, so they become lookups. `jalr $25` is emitted as `LOOKUP_FUNC(ctx->r25)(rdram, ctx);`, and the runtime is expected to keep a table of which sections are loaded and at which addresses. That is the seam between the recompiler and whatever runs the output, and it is the part you have to implement or borrow.

Output granularity is currently one file per function. The README acknowledges the cost: an option to group functions into fewer output files may come later, which should improve build times by reducing file I/O. Until then, expect a large translation unit count and a build that spends real time in the compiler.

## Relocatable overlays and the RELOC_HI16 macros

N64 games lean on overlays, and the README says both statically linked and relocatable overlays are handled. For relocatable ones, the tool rewrites supported instructions that carry relocation data (`lui`, `addiu`, loads and stores) so the runtime can patch the immediate at load time.

The README's worked example: `lui $24, 0x80C0` in a section starting at `0x80BFA100`, with a relocation against a symbol at `0x80BFA730`, is emitted as `ctx->r24 = S32(RELOC_HI16(1754, 0X630) << 16);`, where 1754 is the section index. The runtime implements `RELOC_HI16` and `RELOC_LO16` to adjust the immediate based on where the section actually landed.

This is a clean split of responsibility, but it means the generated code is only half a program. If your runtime does not implement those macros correctly, relocated instructions read the wrong address and the failure appears far from its cause. TLB mapping relocations are listed as future work, with the stated goal of running most TLB-mapped code without a penalty on every RAM access.

## Configuring the recompiler with a toml file and an ELF

The recompiler takes a toml path as its first argument. That file holds input and output paths, and optionally stubs out functions, skips recompilation of specific functions, and patches single instructions in the target binary. The README states that hooks via `[[patches.func]]` and `[[patches.hook]]` are planned but currently unimplemented, so do not design around them.

The README also states that documentation on every option is not currently available, and points to an example toml in the Zelda 64: Recompiled project. That is the practical starting point, and it is a fair criticism of the project: the canonical configuration reference lives in a downstream repository.

The metadata requirement is the harder constraint. Currently the only way to supply symbols is by passing an ELF file, and the README says the easiest way to get one is to set up a disassembly or decompilation of the target binary. A custom metadata format to bypass that is listed as future work. There is no documented path from a raw ROM to a working build.

Building the recompiler itself uses CMake, since `CMakeLists.txt` sits at the repository root alongside `src/`, `include/` and `lib/`.

```bash
git clone https://github.com/N64Recomp/N64Recomp.git
cd N64Recomp
cmake -B build
cmake --build build
```

The repository layout also shows `RSPRecomp/`, `LiveRecomp/`, `OfflineModRecomp/`, `RecompModMerger/` and `RecompModTool/` as separate top-level directories, which tells you the recompiler is one component of a larger toolset rather than a single binary.

## Single file output mode and patching a shipped port

Single file output mode is set through an option in the configuration toml. Instead of one file per function, every function from the provided ELF lands in one output file. The stated purpose is compiling patched versions of functions from the target binary.

The mechanism relies on ordinary linker behaviour. The README notes that linkers such as ld, lld and MSVC's link.exe pull a symbol from a static library only if it was not already found in an earlier input file. So if you hand the linker your recompiled patches before the original recompiler output, your versions win and the rest of the program comes from the original output. That is a genuinely useful trick for mods: you recompile only what you changed and keep the rest byte-identical in behaviour.

It also explains the `RecompModTool/` and `RecompModMerger/` directories and the Mod Tool release from 2025-05-04. The project is not only about producing a port from scratch; it also supports the aftermarket of modifying one.

Where this is the wrong tool: if your goal is to understand a binary rather than run it, a disassembler or a decompiler answers different questions. And if you only need to execute an existing ROM, an emulator does that today with no ELF, no toml and no runtime work.

## Toolchain assumptions that will bite you

The README is unusually direct about compiler compatibility. The recompiler has mostly been tested on binaries built with old MIPS compilers such as mips gcc 2.7.2 and IDO, and with modern clang targeting mips. Modern mips gcc may trip up the recompiler because of certain optimizations it can perform, though the README suggests those cases can probably be avoided by setting specific compilation flags. Note the hedging: probably, and specific flags that are not enumerated here.

That is the sharpest limitation in the document. The tool's correctness depends on the shape of the code the original compiler emitted, which is exactly why the project expects you to start from a matching decompilation or disassembly rather than an arbitrary binary. If you are working from a build whose compiler you cannot identify, you are in the untested region.

The output side is more forgiving: the README says the recompiler output can be compiled with any C compiler, tested with msvc, gcc and clang. So the risk sits on the input, not the output.

Maintenance is worth noting plainly. The last push to the default branch was on 2026-05-27, and the repository is not archived. The most recent release listed is the Mod Tool from 2025-05-04. There is no published roadmap with dates, and the README's Planned Features section is a list of intentions rather than a schedule.

## N64Recomp versus decompilation, and where the runtime comes from

The obvious alternative is decompilation, the hand-written route that produces readable C matching the original binary. The difference is in what you get and what it costs. A decompilation gives source a human can edit, rename and reason about, and it is the basis for the matching builds that N64Recomp itself expects as input. It takes years of volunteer effort per game. N64Recomp gives you generated C that mirrors instructions one for one, which is fast to produce and unpleasant to read, and it requires the decompilation or disassembly to exist anyway for symbols.

So the two are not really competitors. Decompilation produces the metadata; N64Recomp consumes it. The README's own reference point, ido-static-recomp, uses the same technique for a different purpose: recompiling a compiler rather than a game.

For actually running the output you need a runtime, and this repository does not contain one. The README points to N64ModernRuntime, and to Zelda 64: Recompiled as a project where the whole pipeline is visible in action. That is where you go to see what a complete integration looks like, including the example toml. Treating N64Recomp as a standalone product will lead to confusion; treating it as the front half of a pipeline is accurate.

Licensing is MIT for this repository. That covers the recompiler source, not the N64 binaries you feed it, and not the runtimes or game code in other repositories, which carry their own terms. Whether you can distribute a port built this way depends on the rights to the underlying game, which is a question for a lawyer rather than a README.

## Conclusion

Adopt N64Recomp if you already have a disassembly or decompilation of the target binary and can supply an ELF, because the README states that is currently the only way to provide the required metadata. Do not adopt it if you want to drop a ROM into a window and play: there is no runtime in this repository, and the README points to N64ModernRuntime and Zelda 64: Recompiled for that. Before starting, verify that your toolchain matches one the README lists as tested (mips gcc 2.7.2, IDO, or modern clang targeting mips), and check the example toml in Zelda 64: Recompiled for the option names, since the README says documentation on every option is not currently available.

## FAQ

### How do I use N64Recomp?

You build the recompiler with CMake, then run it with a toml configuration file as the first argument. That toml sets input and output paths and can stub, skip or patch functions, and the metadata comes from an ELF file, which the README says is currently the only supported way to provide it.

### What is the difference between N64 recompilation and N64 decompilation?

Decompilation produces human-readable C source that matches the original binary and takes years of manual work. N64Recomp instead emits C that translates each MIPS instruction literally, and it expects a disassembly or decompilation to already exist so it can read the symbol metadata from an ELF.

### Is N64Recomp an emulator?

No. It is a static recompiler that turns an N64 binary into C code, which you then compile with any C compiler. To run that output you need a runtime, and the README points to N64ModernRuntime for that.

### Can I use N64Recomp on a ROM directly?

The README states that currently the only way to provide the required metadata is by passing an ELF file, and that the easiest way to get one is to set up a disassembly or decompilation of the target binary. A custom metadata format to bypass that is listed as future work.

### Which compilers were the input binaries built with?

The README says the recompiler has mostly been tested on binaries built with old MIPS compilers such as mips gcc 2.7.2 and IDO, plus modern clang targeting mips. Modern mips gcc may trip it up due to certain optimizations, which the README suggests can probably be avoided with specific compilation flags.

## Sources

- [Issues](https://github.com/N64Recomp/N64Recomp/issues)
- [License: MIT](https://github.com/N64Recomp/N64Recomp/blob/main/LICENSE)
- [N64Recomp/N64Recomp on GitHub](https://github.com/N64Recomp/N64Recomp)
- [README](https://github.com/N64Recomp/N64Recomp/blob/main/README.md)
- [Releases](https://github.com/N64Recomp/N64Recomp/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/n64recomp-n64recomp
