Need for Speed: Most Wanted Decompilation [![Build Status]][actions] [![Code Progress]][progress] [![Data Progress]][progress] [![Discord Badge]][discord] ============= [Build Status]: https://github.com/dbalatoni13/nfsmw/actions/workflows/build.yml/badge.svg [actions]: https://github.com/dbalatoni13/nfsmw/actions/workflows/build.yml [Code Progress]: https://decomp.dev/dbalatoni13/nfsmw.svg?mode=shield&measure=code&label=Code [Data Progress]: https://decomp.dev/dbalatoni13/nfsmw.svg?mode=shield&measure=data&label=Data [progress]: https://decomp.dev/dbalatoni13/nfsmw [Discord Badge]: https://img.shields.io/discord/727908905392275526?color=%237289DA&logo=discord&logoColor=%23FFFFFF [discord]: https://discord.gg/hKx3FJJgrV A work-in-progress decompilation of the GameCube, Xbox 360, PS2 and PC versions of Need for Speed: Most Wanted. This repository does **not** contain any game assets or assembly whatsoever. An existing copy of the game is required. Supported versions: - `GOWE69`: Rev 0 (GC USA) - `EUROPEGERMILESTONE`: Oct 21, 2005 prototype (Xbox 360 PAL) - `SLES-53558-A124`: Sep 20, 2005 prototype (Alpha 124) (PS2 PAL) - `SLUS-21351`: Black Edition (PS2 USA) - `SPEED_EXE_1_3`: PC version 1.3 # SAY NO TO SLOP We expect that as we progress on this decompilation, vibecoders will attempt to take what we have and slop out a broken port before us. Please do not give sloppers attention. Any releases and ports will be available [here](https://github.com/RevEngin3) when they are ready. LLMs have been used on this project. Here's why, dispite this, our project isn't slop. - LLMs are **ONLY** used as a rough pass to get code somewhere in the ballpark that we can then reference or to create tooling. All LLM code must go through extensive manual review to clean it up to the point of being usable. **No slop is allowed to be merged into main.** If we accepted slop we could have been "done" months ago. Raw LLM output is unacceptable due to the mistakes it makes, such as: - Manually assigning registers to variables to force a fake match - Accessing struct members by pointer offsets - Replacing *every* string and float value with something completely random - Renaming parameters and variables randomly - Reimplementing the same code multiple times and using include guards to fix the compile errors - Redeclaring every referenced function at the top of each file, often incorrectly - Casting variables to the type they already are - We are matching compiled machine code *and* debug symbols against multiple versions of the game accross multiple platforms, as well as other games in the series with shared code. To match all at once the code *must* be accurate to the original. This project has a large focus on accuracy to what the original code might have been. This includes matching macros, inlines, and static constants stripped from usage sites. - No feature or porting work is to be done until we have a *near-perfect matching* base to work against, and no AI will be used for this work. As long as this canary remains here, **NO AI has been used for any feature or porting work.** # Dependencies ## Windows On Windows, it's **highly recommended** to use native tooling. WSL or msys2 are **not** required. When running under WSL, [objdiff](#diffing) is unable to get filesystem notifications for automatic rebuilds. - Install [Python](https://www.python.org/downloads/) and add it to `%PATH%`. - Also available from the [Windows Store](https://apps.microsoft.com/store/detail/python-311/9NRWMJP3717K). - Download [ninja](https://github.com/ninja-build/ninja/releases) and add it to `%PATH%`. - Quick install via pip: `pip install ninja` ## macOS - Install [ninja](https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages): ```sh brew install ninja ``` [wibo](https://github.com/decompals/wibo), a minimal 32-bit Windows binary wrapper, will be automatically downloaded and used. ## Linux - Install [ninja](https://github.com/ninja-build/ninja/wiki/Pre-built-Ninja-packages). [wibo](https://github.com/decompals/wibo), a minimal 32-bit Windows binary wrapper, will be automatically downloaded and used. ## Dev Container - Install [docker](https://www.docker.com/get-started/) - VSCode - Ensure the VSCode extension [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) is installed - When prompted to "Reopen folder to develop in a container" select "Reopen in Contianer" or run `Dev Containers: Reopen in Container` - Zed - When asked "Would you like to re-open it in a container?" select "Yes, Open in Container" or run `projects: open dev container` Dependencies and extensions will be included automatically Run objdiff by running `objdiff -p .` in the editor's terminal # Building - Clone the repository: ```sh git clone https://github.com/dbalatoni13/nfsmw.git ``` - Install dependencies (PS2 only) ```sh python -m pip install -r requirements.txt ``` - Configure: ```sh python configure.py ``` To use a version other than `GOWE69` (USA), specify it with `--version`, for example `--version EUROPEGERMILESTONE`. - Build: ```sh ninja ``` - Extracting the binaries - GC: Extract `NFSMWRELEASE.ELF`, copy it into `orig/GOWE69`, and convert it into a DOL using the following command: ```sh ./build/tools/dtk elf2dol ./orig/GOWE69/NFSMWRELEASE.ELF ./orig/GOWE69/sys/main.dol ``` - Xbox 360: simply rename `NfsMWEuropeGerMilestone.exe` to `NfsMWEuropeGerMilestone.xex` and copy it to `./orig/EUROPEGERMILESTONE/` - PS2: Copy `NFS.ELF` to `./orig/SLES-53558-A124/` # Diffing Once the initial build succeeds, an `objdiff.json` should exist in the project root. Download the latest release from [encounter/objdiff](https://github.com/encounter/objdiff). Under project settings, set `Project directory`. The configuration should be loaded automatically. Select an object from the left sidebar to begin diffing. Changes to the project will rebuild automatically: changes to source files, headers, `configure.py`, `splits.txt` or `symbols.txt`. ![](assets/objdiff.png) # Contribution ## Ghidra To get a proper Ghidra output, you should either install the necessary extensions or ask me for access to the shared Ghidra project on [decomp.dev](https://ghidra.decomp.dev/). ### Ghidra server [Recommended Ghidra build](https://github.com/RootCubed/ghidra-ci/releases/download/2025-04-25/ghidra_11.4_DEV_20250425.zip) ### Manual setup [Dwarf1 extension to properly load the GC debug info into Ghidra](https://github.com/emoose/ghidra-dwarf1) [PS2 Ghidra extension](https://github.com/chaoticgd/ghidra-emotionengine-reloaded) Unfortunately the vtables can't (currently?) be loaded from the GC ELF into Ghidra, so you'll have to copy them over from the PS2 ELF and adjust the corresponding classes. Nested classes have the same problem and solution. #### Use Deprecated Demangler For Gamecube binaries the standard demangler doesn't work and you have to use the deprecated version. For PS2 binaries the deprecated version gives nicer results. 1. From the Ghidra project window, double-click the program file to open it in the CodeBrowser. 2. In the CodeBrowser, navigate to Analysis > Auto Analyze... (or press Shift + F12). 3. In the Auto Analysis options window, find the "Demangler GNU" analyzer in the list and select it. 4. In the options area (usually on the right side of the window), locate the option named "Use Deprecated Demangler". ## symbols/mw_dwarfdump.nothpp ``` ./build/tools/dtk dwarf dump ./orig/GOWE69/NFSMWRELEASE.ELF -o ./symbols/mw_dwarfdump.nothpp ``` This is the dwarf dump of the whole GC version of the game. The `.nothpp` extension is to make sure that the IDE doesn't parse it on weak laptops. This should be your main source of information. It even shows which inlines a function calls. Namespaces only show up in generics. For regular functions and variables you can search `symbols.txt` for the right name. ## symbols/PS2 This folder contains the debug info dump of PS2 build alpha 124. It is useful for figuring out member visibility and the declaration order of virtual functions. ## symbols/debug_lines.txt GameCube Address -> line mapping ## symbols/file_names.txt GameCube file list. Headers that don't contain any inlines are not listed. ## ProStreet's PDBs Another great source where you can see visibility and namespaces is the PDBs of the Xbox 360 builds of ProStreet. You can use [resym](https://resym.chimpsatsea.com/) to open them up in the browser. ## symbols/vlt.txt This file contains the hashes used in AttribSys. ## symbols/hashes.txt This file contains hashes used in different parts of the game. We are currently using a template solution to calculate the hash at compile time. But as you can see in the file, this is quite verbose, we'll want to find a better solution. ## symbols/bchunks.txt This file contains bChunk chunk IDs. ## Ghidra CLI setup - Run ``` ./build/tools/dtk dwarf dump ./orig/GOWE69/NFSMWRELEASE.ELF -o ./symbols/mw_dwarfdump.nothpp python ./tools/split_dwarf_info.py ./symbols/mw_dwarfdump.nothpp ./symbols/Dwarf ``` - Set up the project and Ghidra as described above (take the Ghidra repo from the decomp.dev server, you'll have to request access). - Import the ELF files from `orig/` into the Ghidra project so the program names stay `NFSMWRELEASE.ELF` and `NFS.ELF`: ```sh ghidra import ./orig/GOWE69/NFSMWRELEASE.ELF ghidra import ./orig/SLES-53558-A124/NFS.ELF ``` - Download [ghidra-cli](https://github.com/akiselev/ghidra-cli) and put it into your path. - Tell ghidra-cli your Ghidra installation's path ``` ghidra config set ghidra_install_dir /ghidra_11.4_DEV_20250425/ghidra_11.4_DEV ``` - Tell ghidra-cli the path to the Ghidra project ``` ghidra config set ghidra_project_dir ``` - Set NeedForSpeed as the default project ``` ghidra config set default_project NeedForSpeed ``` - Set the GC version as the default program ``` ghidra config set default_program NFSMWRELEASE.ELF ``` # Contributors Special thanks to [Brawltendo](https://github.com/Brawltendo) for helping with tooling and letting me use his partial decomp. Special thanks to [r033](https://github.com/r-033) and [Toru the Red Fox](https://github.com/TorutheRedFox) for sharing their knowledge with us. Special thanks to all the people who help us with decomp.me scratches, especially [Roeming](https://github.com/Roeming), [chippy](https://github.com/1superchip) and [Hummer12007](https://github.com/Hummer12007).