A work-in-progress decompilation of the GameCube, Xbox 360 and PS2 versions of Need for Speed: Most Wanted. The focus is currently on the GameCube version.
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)
On Windows, it's highly recommended to use native tooling. WSL or msys2 are not required.
When running under WSL, objdiff is unable to get filesystem notifications for automatic rebuilds.
- Install Python and add it to
%PATH%.- Also available from the Windows Store.
- Download ninja and add it to
%PATH%.- Quick install via pip:
pip install ninja
- Quick install via pip:
-
Install ninja:
brew install ninja
wibo, a minimal 32-bit Windows binary wrapper, will be automatically downloaded and used.
- Install ninja.
wibo, a minimal 32-bit Windows binary wrapper, will be automatically downloaded and used.
-
Clone the repository:
git clone https://github.com/dbalatoni13/nfsmw.git
-
Install dependencies (PS2 only)
python -m pip install -r requirements.txt
-
Configure:
python configure.py
To use a version other than
GOWE69(USA), specify it with--version, for example--version EUROPEGERMILESTONE. -
Build:
ninja
-
Extracting the binaries
-
GC: Extract
NFSMWRELEASE.ELF, copy it intoorig/GOWE69, and convert it into a DOL using the following command:./build/tools/dtk elf2dol ./orig/GOWE69/NFSMWRELEASE.ELF ./orig/GOWE69/sys/main.dol
-
Xbox 360: simply rename
NfsMWEuropeGerMilestone.exetoNfsMWEuropeGerMilestone.xexand copy it to./orig/EUROPEGERMILESTONE/ -
PS2: Copy
NFS.ELFto./orig/SLES-53558-A124/
-
-
Sharing large assets across git worktrees
If you use multiple git worktrees, you can deduplicate the large immutable inputs and downloaded tool binaries while keeping each worktree's generated build files separate:
python tools/share_worktree_assets.py link --all
This shares the ignored debug/tool assets under the git common directory, including extracted
orig/*contents,symbols/*, and downloaded tool binaries underbuild/. It intentionally does not sharebuild.ninja,objdiff.json,compile_commands.json, or per-worktree object outputs.After creating a fresh worktree, bootstrap its local generated files with:
python tools/share_worktree_assets.py bootstrap
bootstraplinks the shared assets for the current worktree, runsconfigure.py, generates the local split config when needed, and rerunsconfigure.pyso fresh worktrees end up withbuild.ninja,objdiff.json, andcompile_commands.jsonwithout manual copying.
Once the initial build succeeds, an objdiff.json should exist in the project root.
Download the latest release from 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.
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.
Dwarf1 extension to properly load the GC debug info into Ghidra
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.
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.
- From the Ghidra project window, double-click the program file to open it in the CodeBrowser.
- In the CodeBrowser, navigate to Analysis > Auto Analyze... (or press Shift + F12).
- In the Auto Analysis options window, find the "Demangler GNU" analyzer in the list and select it.
- In the options area (usually on the right side of the window), locate the option named "Use Deprecated Demangler".
./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.
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.
GameCube Address -> line mapping
GameCube file list. Headers that don't contain any inlines are not listed.
Another great source where you can see visibility and namespaces is the PDBs of the Xbox 360 builds of ProStreet. You can use resym to open them up in the browser.
This folder contains AttribSys classes which were automatically generated by tools/attrib_generator.py of which we haven't been made sure yet that they can be included (perhaps they use structs that require imports).
This file contains the hashes used in AttribSys.
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.
This file contains bChunk chunk IDs.
-
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 stayNFSMWRELEASE.ELFandNFS.ELF:ghidra import ./orig/GOWE69/NFSMWRELEASE.ELF ghidra import ./orig/SLES-53558-A124/NFS.ELF
-
Download ghidra-cli and put it into your path.
-
Tell ghidra-cli your Ghidra installation's path
ghidra config set ghidra_install_dir <YOUR_PATH>/ghidra_11.4_DEV_20250425/ghidra_11.4_DEV -
Tell ghidra-cli the path to the Ghidra project
ghidra config set ghidra_project_dir <PATH> -
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
Just tell your favourite clanker to reference AGENTS.md to decompile a translation unit of your choice, for example main/Speed/Indep/SourceLists/zEAXSound.
When introducing or forward-declaring a type, preserve the original class / struct
kind. Check existing headers first with python tools/find-symbol.py <TypeName>, then use
GC Dwarf and PS2 type info when the real declaration is missing or incomplete.
Preserve real member names, types, order, and offset comments too. For recovered game
types, do not invent pad, unk, or field_XXXX members to force a guessed layout; use
the debug data and leave a short TODO when a field is still unresolved.
If a project type already has a header in src/, include that header instead of adding a
local forward declaration.
The repo ships with a decomp-aware style helper:
python tools/code_style.py audit --base origin/mainUse audit to classify branch changes into safer vs match-sensitive buckets and to flag repo-specific issues such as jumbo include spacing, stray top-level declarations in SourceLists files, touched class / struct declarations that disagree with known headers or the PS2 visibility rule, touched project forward declarations that should be replaced by real includes, touched type members that look like invented padding or placeholder names, and touched style-guide issues that clang-format cannot fix for you (using namespace, NULL, bad cast spacing, or missing EA_PRAGMA_ONCE_SUPPORTED guard blocks).
Repeated findings are grouped by file so large branch audits stay readable.
Useful focused passes:
python tools/code_style.py audit --base origin/main --category safe-cpp
python tools/code_style.py audit --base origin/main --category match-sensitive-cpp
python tools/code_style.py format --check --base origin/main --category safe-cppIf you have clang-format installed locally, you can also use:
python tools/code_style.py format --check --base origin/main
python tools/code_style.py format --check src/Speed/Indep/Src/Frontend/FEManager.cppThe formatter wrapper targets eligible changed C/C++ files by default, including match-sensitive code. If you want a smaller focused pass, restrict it with --category safe-cpp, which currently maps to src/Speed/Indep/Src/Frontend/ and src/Speed/Indep/Src/FEng/.
format --check now distinguishes whitespace-only formatter deltas from other non-whitespace output changes.
Files that use the repo's initializer-list guard comments (//) are formatter targets too. If a formatting pass touches match-sensitive code, rebuild and verify the affected unit afterwards instead of assuming the change is automatically byte-stable.
For declaration-kind checks, header declarations are treated as the repo source of truth; otherwise the helper falls back to the PS2 dump rule (public: / private: / protected: means class, no visibility labels means struct).
clang-format is optional. Recommended installs:
- macOS:
brew install clang-format - Linux:
sudo apt install clang-format - Windows:
winget install LLVM.LLVM
If your binary lives outside PATH, set CLANG_FORMAT to the executable path before running tools/code_style.py format.
Special thanks to Brawltendo for helping with tooling and letting me use his partial decomp.
Special thanks to r033 and Toru the Red Fox for sharing their knowledge with us.
