Skip to content
antoxa2584xPublic

About

Need For Speed Underground 2 Xbox Original port for Nintendo Switch

Resources

Stars

115 stars

Watchers

10 watching

Forks

Latest commit

 

History

30 Commits

Folders and files

Repository files navigation

Need for Speed: Underground 2

Xbox static recompilation for Nintendo Switch and Linux

The original Xbox (NTSC-U) release of Need for Speed: Underground 2, lifted instruction by instruction to C and running natively, with no emulator.

Switch Linux Vulkan OpenGL Version

Features · Playing on Switch · Building · Configuration · Status


Important

No game data is included. You need your own copy of the Xbox disc, extracted (default.xbe, NFSUNDER/, ...).

✨ Features

  • 🏁 Native code: the whole game runs as recompiled C, built with xboxrecomp
  • 🎮 Two renderers: Vulkan (NVK on Switch) and OpenGL, with render scaling up to 4x
  • 📺 Widescreen 16:9 by default, using the game's own wide mode
  • 🪞 Car reflections (Vulkan), switchable in the game's Options → Video → Car Reflections; Off also skips the six reflection passes the game draws every race frame
  • 🔍 Resolution Scale in Options → Video (Vulkan): 1x, 1.5x (default), 2x, 2.5x, applied at once
  • 🖼️ Cleaner picture (Vulkan): mipmaps with trilinear and anisotropic filtering, mipmapped car reflections, FXAA, square pixels in 16:9 races (1280x720 at 1.5x); Anti-Aliasing, Anisotropic (Off-16x) and Square Pixels in Options → Video
  • 🎬 Full-screen movies decoded with FFmpeg (VP6)
  • 🔊 Audio through an emulated Xbox APU, with 5.1 downmixed to stereo
  • 👥 Two-player split screen, with Joy-Con pairs or one sideways Joy-Con per player
  • 📳 Rumble on Switch HD rumble and SDL controllers, strength set in Options → Gameplay → Vibration (Off, 10%-100%)
  • 🔤 Switch wording in the menus (Start → +, no Xbox Live or hard-disk names)
  • 💾 Saves stay next to the game in game/UDATA; the Options → Audio volumes (the Xbox game never saved them) are kept in nfsu2x_options.txt

🕹️ Playing on Switch

  1. Copy the NRO to sdmc:/switch/nfsu2x/ (nfsu2x-vulkan.nro for the Vulkan build).
  2. Copy the extracted disc (not the ISO) to sdmc:/switch/nfsu2x/game/.
  3. Start it with title takeover: hold R while launching any game. Applet mode leaves too little memory.
sdmc:/switch/nfsu2x/
├── nfsu2x-vulkan.nro
├── nfsu2x_env.txt      optional settings, KEY=VALUE per line
├── nfsu2x_options.txt  written by the in-game options the port adds and
│                       the Audio volumes
└── game/
    ├── default.xbe
    └── ...

Buttons map by label (Switch A = Xbox A). Settings go in nfsu2x_env.txt (see Configuration) and the log is written to the same folder.

🛠️ Building

1. Lift the XBE to C
# Writes NFSU2_GEN_DIR (default /root/nfsu2x/gen)
NFSU2_XBE=/path/to/game/default.xbe NFSU2_GEN_DIR=/path/to/gen tools/regen.sh
2a. Linux (SDL2 + OpenGL or Vulkan)
cmake -S . -B build -G Ninja -DNFSU2_GEN_DIR=/path/to/gen   # add -DNFSU2_VULKAN=ON for Vulkan
cmake --build build
NFSU2_GAME_DIR=/path/to/game build/nfsu2_recomp
2b. Nintendo Switch (devkitA64, switch-sdl2, switch-mesa)
NFSU2_GEN_DIR=/path/to/gen NFSU2_GAME_SRC=/path/to/game switch/build.sh
# Vulkan build (needs mesa-switch NVK and glslang for Switch)
VULKAN=1 JOBS=6 NFSU2_GEN_DIR=/path/to/gen switch/build.sh

Project layout

Path What
src/main.c boot and runtime defaults
src/recomp_manual.c hand-written overrides of lifted functions
src/switch_nx.c Switch log device, env file, exception handler, loading screen
config/seed_functions.json entry points the static pass cannot see
xboxrecomp/ the toolkit (MIT), vendored with this port's changes: NV2A renderers (Vulkan, OpenGL), SDL audio, Switch platform layer, translator fixes
tools/regen.sh XBE → lifted C (gen/, never committed)
switch/build.sh Switch NRO build + SD-card staging

⚙️ Configuration

On Linux these are ordinary environment variables. On the Switch they go in sdmc:/switch/nfsu2x/nfsu2x_env.txt, one KEY=VALUE per line (# starts a comment). Anything left out runs at its default, which is the fastest normal configuration. Most of the list is for debugging, so the sections below are folded.

# sdmc:/switch/nfsu2x/nfsu2x_env.txt
RECOMP_GL_SCALE=1.5
RECOMP_WIDESCREEN=0
Build and code generation
Variable Meaning
XBOXRECOMP_DIR Toolkit to build or regenerate with (default: the vendored xboxrecomp/).
NFSU2_XBE default.xbe to lift (tools/regen.sh).
NFSU2_GEN_DIR Directory for the lifted C (default /root/nfsu2x/gen).
LIFT_ONLY=1 regen.sh: skip disasm / function id / ABI analysis and only re-lift (enough after translator or recomp_manual.c changes; seed changes need the full run).
RECOMP_LEAF_LOCALS=0 Translator: keep MMX leaf-function registers in globals instead of C locals.
RECOMP_REG_LOCALS=0 Translator: keep eax..edi/esp in globals instead of C locals.
RECOMP_X87_LOCALS=0 Translator: keep the x87 stack top in its global instead of a local.
NFSU2_GAME_SRC switch/build.sh: extracted disc to stage (default /root/nfsu2x/game).
SD_ROOT switch/build.sh: staging SD-card root (default <repo>/switch_sd).
BUILD_DIR switch/build.sh: build directory (default /root/nfsu2x/build-switch, -vk with VULKAN=1).
JOBS switch/build.sh: parallel compile jobs (6 fits -O2 in RAM).
VULKAN=1 switch/build.sh: build the Vulkan renderer (nfsu2x-vulkan.nro).
NVK_SDK, GLSLANG_DIR switch/build.sh with VULKAN=1: mesa-switch NVK install and Switch glslang.
FFMPEG_DIR switch/build.sh: LGPL VP6-only FFmpeg for the movies (tools/build_ffmpeg_vp6.sh).
Game and host
Variable Meaning
NFSU2_GAME_DIR Extracted disc (Linux; default ./game). The Switch always uses sdmc:/switch/nfsu2x/game/.
NFSU2_GL=0 Use the executor's CPU renderer instead of the GPU renderer.
NFSU2_APU=0 With RECOMP_AC97_READY=plain: no emulated APU (no sound).
NFSU2_SIM_STEPS Longest game-time step per frame, in 1/60 s (default 6 = 100 ms; 3 = the original 50 ms cap, which slows races below 20 fps).
NFSU2_NATIVE_VP6 Movie decoder: 1 = FFmpeg (default when built with it), 0 = the lifted decoder, 2 = both and compare.
NFSU2_MOVIE_FLASH=1 Keep the opening trailer's white flash frames (shown black by default).
NFSU2_SWITCH_TEXT Switch wording in the menus (Start → +, no Xbox Live/hard-disk names): 1 on (Switch default), 0 off.
NFSU2_EXIT_TRACE=1 Print the guest state at exit.
RECOMP_WIDESCREEN=0 Tell the game the TV is 4:3 (default 16:9).
RECOMP_CMDLINE Command line handed to the title (e.g. +map intro).
HOME, XDG_DATA_HOME Linux: fallback save directory ($XDG_DATA_HOME/xboxrecomp, else ~/.local/share/xboxrecomp) when none is configured. NFSU2's own saves go to <game>/UDATA.
Switch (Horizon)
Variable Meaning
NFSU2_NO_LOG=1 / NFSU2_LOG=0 No log file, no [perf] reports, no profiler.
NFSU2_LOG_SYNC=1 Write every log line to the card at once (slow; for hangs).
NFSU2_LOADER=0 No loading screen (logo + bar) before the first frame.
RECOMP_NX_PROFILE Sampling profiler into prof.bin: 1 = busy threads, 2 = every thread above 2%.
RECOMP_NX_SHM=1 Guest RAM from shared memory instead of code memory.
RECOMP_NX_AUDIO_PRIO=0 Leave the APU and SDL audio threads at priority 59 (default: above the game threads).
RECOMP_NX_GUEST_RT=1 Run the time-critical guest thread (EA mixer) at a real-time host priority.
RECOMP_NX_JOYCON=single One sideways Joy-Con per player.
RECOMP_NX_JOYCON_ROTATE=0 Don't rotate the sideways Joy-Con stick.
RECOMP_GUEST_ONE_CORE 1 = pin guest threads to one core, 2 = guest threads only (interrupts float).
Scheduling and kernel
Variable Meaning
RECOMP_GIL=0 No guest lock (guest threads run in parallel; races the title's streaming).
RECOMP_GIL_EAGER=1 Hand the guest lock over by guest priority (main thread and time-critical threads pre-empt).
RECOMP_KERNEL_FAST=0 Release the guest lock in every kernel call, even the cheap time queries and IRQL changes.
RECOMP_FRAME_LAG=1 Wait on the previous frame's fence instead of the current one (game and GPU work overlap).
RECOMP_VBLANK Vblank interrupt for D3D (default 1).
RECOMP_WORKERS=inline Run the title's worker routines inline instead of on threads.
RECOMP_ASYNC_IO=1 Asynchronous NtReadFile.
RECOMP_CS_MODE=single Every guest critical section behind one recursive lock.
RECOMP_NATIVE=0 Use the lifted culling functions instead of the native C versions.
RECOMP_NATIVE_CHECK=1 Run both and report any difference.
RECOMP_QUIET 1 = no periodic kernel/GPU summaries (Switch default), 0 = show them.
RECOMP_NO_LOG=1 No xbox_kernel.log.
XBOX_LOG_LEVEL Kernel log level (0 errors … trace).
RECOMP_KERNEL_LOG_BUDGET Kernel calls logged before the log goes quiet.
Graphics (GL and Vulkan renderers)
Variable Meaning
RECOMP_PB_EXEC Execute the NV2A pushbuffer (default 1; the title draws through it).
RECOMP_GL_SCALE Render resolution multiple, 0.5..4 (fractions allowed). Vulkan: only until Options → Video → Resolution Scale is changed (default there 1.5x).
RECOMP_GL_THREAD=1 GL renderer: GL calls on their own thread.
RECOMP_GL_DIRECT=0 Convert every vertex to float4 instead of uploading it as stored.
RECOMP_GL_DXT=0 Decode DXT textures on the CPU instead of uploading them compressed.
RECOMP_GL_SHARED_Z=0 One depth buffer per colour surface (old behaviour; lights show through walls).
RECOMP_GL_TEX_MB Texture cache budget in MB (least recently used go first).
RECOMP_PROG_CACHE Shader/pipeline cache file (progcache.bin), =0 off.
RECOMP_VP=0 No vertex programs (their batches are skipped).
RECOMP_GL_DUMP=<prefix>[,every] Write presented frames as BMP (every N frames, default 60).
RECOMP_GL_WATCH=<hex va> Log draws into or sampling that address, with pixel read-backs.
RECOMP_GL_TRACE=1 Shader sources and compile/link errors.
RECOMP_GL_FINISH=1 Wait for the GPU after every operation (finds the call that hangs it).
RECOMP_FPS_LOG=1 Presented frames per 10 s.
RECOMP_VK_MIPS=0 Vulkan: no mipmaps (distant textures shimmer, the old look).
RECOMP_VK_ANISO Vulkan: anisotropic filtering, 1..16 (default 16; 1 = off).
RECOMP_VK_LOD_BIAS Vulkan: mip bias, negative = sharper (default -0.25).
RECOMP_VK_FXAA=0 Vulkan: no FXAA (plain scaled blit to the screen).
RECOMP_VK_SQUARE=0 Vulkan: in 16:9, no extra 4/3 columns for square pixels (races 960x720 instead of 1280x720 at 1.5x).
RECOMP_GL_SCALE_X Vulkan: a different horizontal scale (absolute; Resolution Scale keeps the ratio).
RECOMP_VK_CUBE=0 Vulkan: no cube maps (car reflections off), whatever Options → Video says.
RECOMP_VK_HEADLESS=1 Vulkan on Linux: no window (also implied by SDL_VIDEODRIVER=offscreen).
RECOMP_VK_VALIDATION=1 Vulkan on Linux: Khronos validation layer.
RECOMP_VK_TRACE=<n> Vulkan: name every step of the first n draws/clears/flips.
RECOMP_TEX_STATS=1 List each texture format and size decoded.
RECOMP_TEX_STATE=1, RECOMP_TEX_DUMP=<prefix>, RECOMP_TEX_DUMP_EVERY=<n> Texture register state; dump textures to BMP.
RECOMP_FB_DUMP=<prefix>, RECOMP_FB_WINDOW=1, RECOMP_FB_VA=<va>, RECOMP_FB_WINDOW_DUMP_EVERY=<n> Framebuffer window / dumps (CPU presenter).
RECOMP_FFP_TRACE, RECOMP_SKIP_TRACE, RECOMP_TRACE_FLIP=<n>, RECOMP_PB_EXEC_VERBOSE, RECOMP_PB_UNHANDLED_ALL, RECOMP_PB_SCAN, RECOMP_NV2A_TRACE, RECOMP_RASTER_TEST, RECOMP_FIND_NAN, RECOMP_FIND_QUAD Pushbuffer / NV2A debugging traces.
RECOMP_FMV_HOST, RECOMP_FMV_DUMP=<prefix> Host-side movie player (off) and movie frame dumps.
Audio
Variable Meaning
RECOMP_AUDIO=0 No host audio device (the APU then paces by wall clock).
RECOMP_AUDIO_VOLUME Master volume 0..100 (default 50; output is soft-limited to −6 dBFS).
RECOMP_AUDIO_BLOCKS Host queue depth in 256-sample blocks (default 8, Switch 12).
RECOMP_AUDIO_DUMP=<path>,<start s>,<secs> Record the exact PCM sent to the device (48 kHz s16 stereo), written once full.
RECOMP_APU_VOICE_DUMP=<voice hex>,<path>,<start s>,<secs> Record one APU voice before/after its filter (float32) and log its registers once a second (v0F4 = menu music).
RECOMP_AC97_READY AC97 codec presence for DirectSound (default plain).
RECOMP_APU_MIXDOWN_ALL=0 Mix only bins 0/1 to stereo instead of every bin.
RECOMP_APU_DSP_ACK=<addr,...>, RECOMP_DSP_ACK=<addr,...> Acknowledge DSP command doorbells at these guest addresses.
RECOMP_APU_TRACE=1 Front-end methods and a per-second voice summary (disturbs the audio itself).
RECOMP_APU_RING_STATS=1 Stale ring-voice reads every 10 s (Linux; always on in the Switch [perf] report).
Input
Variable Meaning
RECOMP_USB Emulated OHCI USB with the pads (default 1).
RECOMP_USB_PADS=1 Only one pad (default two).
RECOMP_USB_PORT, RECOMP_USB_PORT2 Root port of pad 1 / pad 2 (0-based).
RECOMP_USB_HC, RECOMP_USB_NDP Host controller for the pads and its number of ports.
RECOMP_PAD_LAYOUT=position Map buttons by position (Xbox layout) instead of by label.
RECOMP_PAD_SCRIPT, RECOMP_PAD2_SCRIPT Timed presses, e.g. 4000:start:300,9000:a:200 (ms from the first pad read).
RECOMP_PAD_PRESS=<mask> Press these buttons periodically.
RECOMP_KEYBOARD=1 Keyboard as a pad (Linux).
RECOMP_RUMBLE=0 No rumble.
RECOMP_RUMBLE_TRACE=1, RECOMP_INPUT_DIAG=1, RECOMP_KEY_TRACE=1, RECOMP_USB_TRACE=1 Input debugging traces.
Debugging
Variable Meaning
RECOMP_WATCHDOG_SECS=<s> Snapshot the main guest thread after s seconds without progress (and exit).
RECOMP_WATCHDOG_KEEP=1 Keep running after the watchdog snapshot.
RECOMP_WATCH=<addr>, RECOMP_WATCH_RAW=1 Name the guest code that changes a guest dword.
RECOMP_KERNEL_WATCH=<addr>, RECOMP_KERNEL_WATCH_ALL=1 Sample a guest dword around every kernel call.
RECOMP_PEEK=<addr,...>, RECOMP_PEEK_CHAIN=<addr,off,...> Print guest dwords / follow a pointer chain periodically.
RECOMP_POKE=<addr:val,...> Write guest dwords at boot.
RECOMP_TRAP_NULL=1 Fault on guest null-page access.
RECOMP_UNIMPL_TRAP=1 Abort at the first unimplemented instruction.
RECOMP_IRQL_TRACE=1 Log the first IRQL transitions.
RECOMP_CS_WATCH=<va>, RECOMP_CS_TRACE_CRT=1 Critical-section tracing.
RECOMP_TRACE_ARGS=<n>, RECOMP_TRACE_DEREF=1, RECOMP_TRACE_BUDGET, RECOMP_TRACE_PROFILE=1 Function-entry traces (functions chosen at regen) and a call profile.
RECOMP_FORCE_RETURN Read but currently has no effect.
## 🚦 Status
Platform State
🐧 Linux Boot, movies, profile, Main Menu, Quick Race and Career, with audio
🎮 Switch hardware Boot, movies, profile load/create, Main Menu, races

Still open: cube maps on OpenGL (Vulkan has them: car reflections), bump/dot-product texture modes, fixed-function lighting.

📚 More

Need for Speed and Underground are trademarks of Electronic Arts. This is an unofficial fan project, not affiliated with or endorsed by EA or Microsoft.

About

Need For Speed Underground 2 Xbox Original port for Nintendo Switch

Resources

Stars

115 stars

Watchers

10 watching

Forks

Releases

Packages

Contributors

Languages