Open-source project
n64decomp/mk64 avatar
n64decomp/mk64

mk64 rebuilds Mario Kart 64 from source, and makes you supply the assets

A speedy Mario Kart 64 decompilation, brought to you by the cousin of a tame racing driver.

1,315 stars138 forksCLicense varies

At a glance

What is it?
mk64 is a decompilation of Mario Kart 64 in C and assembly that reproduces three retail ROMs, the US, EUR 1.0 and EUR 1.1, byte for byte by checksum, and it contains no assets at all, so a build starts by extracting them from a copy of the game you already own. The build is driven by a Makefile with an SGI compiler option, a per region VERSION flag and a DEBUG switch, and the repository also carries the decompilation tooling itself.
Who is it for?
mk64 suits a developer who wants to read how a 1997 Nintendo 64 racing game was actually built, and who is prepared to buy the game, install a MIPS toolchain and spend time in a decompilation loop. It does not suit someone who wants a playable ROM without the original cartridge, and it is not a project to fork for commercial work while the licence is unclassified.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Yes. The repository last received commits 68 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

No assets in the repository, on purpose

The constraint that defines the whole project is in two sentences of the README. This repository does not contain assets, and compiling requires asset extraction from a prior copy of the game.

That is the standard arrangement for a decompilation, and stating it plainly is what makes the project usable. A decompilation is a reconstruction of the source code that produced a binary, and the assets, the textures, the audio, the course data and the fonts are copyrighted content that the project has no right to redistribute. So they come from the copy of the game you own, and the build extracts them.

The stated purpose is historical and educational. The README says the decompilation pursues historical and educational elements within the game, found via taking it apart and putting it back together, and that the inspiration comes not only from the game's hardware and technology but from its effects on the cultures and families of nearly every nationality. That is an unusual thing to see stated plainly, and it explains the tone of a project that is otherwise a very serious piece of reverse engineering.

The audience is therefore a reader who wants to understand how the game works, and a modder, since the project also has a documentation section describing the code base, its concepts, its features and modding. The engine knowledge here is the point, not the executable.

One practical note: the licence field of the repository is not classified, so anyone planning to redistribute builds or derived work has a question to ask the maintainers rather than a licence to read.

Three retail revisions, three checksums, one target at a time

The README lists the ROM outputs the project supports and builds, and the SHA-1 checksum of each. The US build is mk64.us.z64 with checksum 579c48e211ae952530ffc8738709f078d5dd215e, the European 1.0 build is mk64.eu.v10.z64 with a729039453210b84f17019dda3f248d5888f7690, and the European 1.1 build is mk64.eu.v11.z64 with f6b5f519dd57ea59e9f013cc64816e9d273b2329.

The checksums are the acceptance criterion of the whole project. A decompilation is not finished until the rebuilt binary matches the original byte for byte, and the way that is checked is by hashing the output and comparing it with the known checksum of the retail ROM. The presence of the checksums in the README, and the sha1 files in the repository root, means you can verify a build yourself without trusting the project's build server.

The three revisions are the 1997 North American release, the European 1.0 release and the European 1.1 release, and the Makefile sets GRUCODE to f3dex_old for all three.

The build system selects the revision with a single variable, and the Makefile warns to run make clean before changing versions, which is the kind of instruction that exists because the build does not do a clean rebuild by itself.

The Makefile is the interface, and it has four switches

The build is driven by make, and the options are documented inside the Makefile itself, which is the more honest place for them.

The first is COMPILER, which defaults to ido. The comment explains that ido uses the SGI IRIS Development Option compiler, which is used to build an original matching N64 ROM, and the alternative is gcc, the GNU C Compiler. This is the heart of the matching problem. A decompilation has to produce the same machine code the original developers produced, and the original was built with a SGI toolchain, so the SGI compiler reproduces the original's code generation, while the GNU compiler produces equivalent but different code. The GCC path is for ports and for people who do not have the SGI tools, and it is not the path that gets you a matching checksum.

The second is VERSION, which defaults to us and accepts us, eu.v10 and eu.v11, each adding its own preprocessor defines.

The third is DEBUG, defaulting to 0, which adds the debug tools, adds the crash screen enhancement and activates debug mode, and which the Makefile says requires a clean first.

The fourth is AVOID_UB, defaulting to 0, described as avoiding undefined behaviour and enabling shiftability when making changes. The README repeats the important part of this for users: adjustments to the game require compiling with AVOID_UB=1 to enable shiftability, and DEBUG=1 enables the game's debug mode. In other words, if you want to move something in the game, you are no longer producing a matching ROM, and the build has to be told to tolerate that.

The quick start itself is short. The submodules come first, either as an update of an existing clone or as a recursive clone:

bash
git clone https://github.com/n64decomp/mk64 --recurse-submodules

and then the three make steps, building the tools, extracting the assets and building the game:

bash
make -C tools -j
make assets -j
make -j

The repository carries the decompilation tooling, not just the game

A decompilation project is two projects: the game, and the process of getting the game to match. The repository layout shows both, and the second half is the more unusual part.

The tooling lives in the root. There is m2c_helper.sh, which supports the m2c decompiler, first-diff.py to find the first differing instruction between two builds, a diff/ directory with diff_settings.py to configure the comparison, progress.py to track how much of the ROM is matching, addr_to_sym.py to map addresses to symbol names, rename_sym.sh to apply a rename across the tree, python_convert.py, and undefined_syms.txt as the list of symbols that are not yet resolved. The permute/ directory and permuter_settings.toml are for a permutation search over candidate implementations, and the models/ directory holds the data the search works from.

There is also import/, which brings in the original code where the original code could be used, enhancements/ for the modifications that the project adds on top, assets.json describing the assets, config.yml for the build configuration, mk64.ld as the linker script, and the sha1 files for the three target revisions.

The tools/ directory is separate because it has to be built first, which is why the quick start runs make -C tools before anything else.

For a reader who wants to understand the method, this is the most valuable part of the repository. A decompilation is not a single act of typing out C, it is an iterative loop of hypothesising an implementation, compiling, diffing the machine code against the original, and adjusting. The diff tooling is the instrument for that loop, and it is why the project has a first-diff script rather than only source code.

The source tree is organised by the game's own subsystems

The project structure section is a map of the game rather than a map of a C project, and that is the clearest statement of what the work involved.

At the top there is asm/ for the handwritten assembly code, the ROM header and the boot sequence, with non_matchings/ holding assembly for sections that are not yet matched and os/ holding the handwritten libultra assembly. There is src/ for the C source, and inside it the subsystems the game is made of: actors/ with the individual actors split out from other files, audio/ with sample tables and audio code, data/ with the miscellaneous data referenced from other C files, debug/ with custom debug code, ending/ with the podium ceremony and credits code, os/ with the libultra C code, and racing/ with the race and game engine code.

Then the content directories. courses/ holds the course data, geography, display lists and staff ghosts, textures/ holds the texture data and bitmaps with subdirectories for textures common to many courses, course specific textures, the crash screen font image, raw textures, standalone whole textures, the startup logo reflection map and the trophy ceremony cutscene textures. data/ holds the miscellaneous data, text, audio banks and instrument sets, music/ holds the sequences, assets/ holds textures, include/ holds the header files, build/ is the output directory, and docs/ holds the build guides.

The split between actors/ and racing/ is the one that tells you something about the game: the actors are the individual characters, each in their own file, and the racing engine is separate from them, which is a much tidier decomposition than a single enormous source file would have been.

The docs/ directory also carries the build guides, and the Doxygen site at n64decomp.github.io/mk64 covers the code base, its concepts, its features and modding, with doxygen-awesome-css/ holding the styling for the generated site.

A Docker image that ships the MIPS toolchain

The Dockerfile gives a convenient way to build without setting up the toolchain on your machine, and its package list tells you what the build actually needs.

The image is ubuntu:24.04, and it installs binutils-mips-linux-gnu, which is the MIPS assembler and linker for the Nintendo 64's processor, along with build-essential, cmake, git, libaudiofile-dev, libcapstone-dev, pkg-config, python3 and zlib1g-dev.

The default command is not a build but a usage message:

bash
docker run --rm -v ${PWD}:/mk64 mk64 make

with the container's command echoing the usage line and pointing back at the repository README. So the intended workflow is to mount your checkout at /mk64 and run make inside the container.

That is a sensible arrangement for a project whose build depends on a cross toolchain and a set of specific native libraries, and it is also a reminder of what the local build otherwise demands. The full build instructions are in the API documentation at n64decomp.github.io/mk64/compiling.html, and the README points there rather than duplicating them.

For the documentation itself, the site is generated with Doxygen and the theme directory in the repository shows the project generates its own styled site rather than using the default. You can build it locally with make doc, and the output lands in docs/html where you open index.html.

What is still unfinished, and how contributions are handled

The Current State section is short and the two things it names are the real state of the project.

The first is that the code base may change significantly over time as naming and documentation continue. For a decompilation that is expected, since the symbols are still being identified and renamed, but it means a fork will drift, and it means that following the project is the only practical way to keep up.

The second is a concrete unfinished piece of work. Some menu textures are compressed using a format called tkmk00, and a byte matching compressor and decompressor does not yet exist. That is an unusually specific gap, and it is the kind that blocks an exact build for a specific part of the interface, which is presumably why it is called out rather than buried in a list of known issues.

The contribution rules are short. Pull requests are welcome, and for major changes you are asked to discuss in the Discord first. Before submitting, run make format to make sure the code meets the project's coding standards, and if a piece of code needs to avoid formatting, place // clang-format off before it and // clang-format on after. The repository carries .clang-format and .clang-tidy for those rules, plus an .editorconfig and a .gitattributes.

On maintenance, the repository is not archived, the last push was on 2026-08-16, and there are no GitHub releases, which fits a project whose output is a source tree rather than a binary distribution. The README also carries a Linux compile badge for the build workflow and a progress badge pointing at the total progress graphic.

Editorial conclusion

mk64 suits a developer who wants to read how a 1997 Nintendo 64 racing game was actually built, and who is prepared to buy the game, install a MIPS toolchain and spend time in a decompilation loop. It does not suit someone who wants a playable ROM without the original cartridge, and it is not a project to fork for commercial work while the licence is unclassified. Verify first that your own copy is one of the three supported revisions and matches the checksum the README lists, decide whether you want the matching IDO compiler or the GCC path, and expect the source tree to keep changing, since the project says the code base may change significantly over time as naming and documentation continue.

Frequently asked questions

Does the mk64 repository include the Mario Kart 64 assets?

No. The README states that the repository does not contain assets and that compiling requires asset extraction from a prior copy of the game, so you need your own copy of the cartridge to build it.

Which versions of Mario Kart 64 can mk64 build?

Three, each with a published SHA-1 checksum: the US build mk64.us.z64, the European 1.0 build mk64.eu.v10.z64 and the European 1.1 build mk64.eu.v11.z64. The VERSION variable selects the revision and the Makefile asks you to run make clean before changing it.

What is the difference between the ido and gcc compiler options in mk64?

The Makefile documents that ido uses the SGI IRIS Development Option compiler, which is used to build an original matching N64 ROM, while gcc uses the GNU C Compiler, which is the option used for ports. The COMPILER variable defaults to ido.

How do I build mk64 from a fresh clone?

Clone the repository recursively or run git submodule update --init --recursive, then run make -C tools -j to build the tools, make assets -j to extract the assets, and make -j to build the game. A Dockerfile is provided for building in a container with the MIPS toolchain.

What is still missing from the mk64 decompilation?

The README notes that some menu textures are compressed using a format called tkmk00 and that a byte matching compressor and decompressor does not yet exist, and it warns that the code base may change significantly over time as naming and documentation continue.

Official sources

  1. Issues
  2. n64decomp/mk64 on GitHub
  3. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/n64decomp-mk64.svg)](https://hysenlabs.com/projects/n64decomp-mk64)

Was this useful?