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 PAL)SLUS-21351: Black Edition (PS2 USA)SPEED_EXE_1_3: PC version 1.3
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 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.
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.
-
Install docker
-
VSCode
- Ensure the VSCode extension Dev 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
- When asked "Would you like to re-open it in a container?" select "Yes, Open in Container" or run
Dependencies and extensions will be included automatically
Run objdiff by running objdiff -p . in the editor's terminal
-
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/
-
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 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
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.
Special thanks to all the people who help us with decomp.me scratches, especially Roeming, chippy and Hummer12007.
