This project was 100% vibe coded, mostly with Claude Opus 5.5 and ChatGPT 6.1 Sol. It wasn't actually made by me. AI models wrote the code, the fixes and this README; I only gave directions and tested the results. Expect rough edges and bugs, and read the code before you trust it.
A static recompilation of Trials HD (Xbox 360 / XBLA, title ID 5841095A) to native
Linux x86-64, built with the ReXGlue SDK.
ReXGlue translates the game's PowerPC executable into C++ ahead of time and links it against a native runtime (kernel HLE, Vulkan GPU backend, SDL input/audio).
This repository contains no game code or assets. You must own Trials HD and supply your own copy. The C++ in
generated/is produced on your machine from your owndefault.xexduring the build, and must not be redistributed.
Status: playable with a controller or the keyboard, with unlocked real rendering and the original ~60 Hz simulation. Headless RX 6600 gameplay checks pass at 30, 60 and 120 render FPS — see Performance below. Audio output is untested. Linux (x86-64) is the only supported target for now.
-
Linux x86-64 with an SSE4.2-class CPU (x86-64-v2) and a Vulkan 1.2+ GPU/driver
-
clang (19+), CMake 3.25+, Ninja, Python 3
-
Debian/Ubuntu packages:
sudo apt install clang cmake ninja-build pkg-config python3 \ libx11-xcb-dev libxcb1-dev libwayland-dev libxkbcommon-dev libxext-dev \ libxrandr-dev libxi-dev libxcursor-dev libxfixes-dev libxss-dev \ libasound2-dev libpulse-dev
You need the Trials HD XBLA package from your own console (an STFS/LIVE file, found
under Content/0000000000000000/5841095A/000D0000/ on the Xbox 360 drive).
Extract it into assets/:
python3 tools/stfs_extract.py "/path/to/5841095A/000D0000/<package file>" assetsYou should end up with assets/default.xex, assets/data/*.pak and
assets/package_license.toml. assets/ is gitignored.
The extractor records the license stored in your package header, and the game reports it at runtime exactly as your console would: a package licensed for the full game runs as the full game, a trial package runs as a trial.
git clone --recursive --shallow-submodules https://github.com/snufflyyy/trials-hd-recompiled.git
cd trials-hd-recompiled
cmake --preset linux-amd64-relwithdebinfo
cmake --build out/build/linux-amd64-relwithdebinfo -j$(nproc)
# First build only: that generated the game code; build again to compile it.
cmake --build out/build/linux-amd64-relwithdebinfo -j$(nproc)The first cmake --build runs rexglue codegen on assets/default.xex (a few
seconds) and asks you to build again: CMake can only add the generated sources once
they exist. The second build re-runs CMake by itself and compiles ~110 MB of generated
C++, which takes a while and several GB of RAM. Later builds need a single command.
The default renderer is the SDK's Vulkan backend. A native WebGPU renderer
(librexgpu-webgpu*.so, built on Dawn) can be built alongside it:
# 1. Fetch, build and install the pinned Dawn into out/dawn-install
tools/build_dawn.sh
# 2. Configure with TRIALSHD_WEBGPU enabled
cmake --preset linux-amd64-relwithdebinfo -DTRIALSHD_WEBGPU=ON -DCMAKE_PREFIX_PATH=$PWD/out/dawn-install
cmake --build out/build/linux-amd64-relwithdebinfo -j$(nproc)Select it at launch with --gpu_plugin=webgpu, or with Renderer in the F1 settings
menu (applies after a restart). If WebGPU device initialization fails, trials_hd
restarts itself with the Vulkan renderer. With several GPUs, pick one with
--webgpu_adapter=N.
It renders the whole game, including GPU particles, at any render resolution; on an
RX 6600 it rides at ~190 FPS uncapped at 1280x720. Debugging and profiling switches:
--webgpu_validation=true (Dawn validation, slower), --webgpu_debug_labels=true
(pass and draw labels for RenderDoc), and --webgpu_profile=true (GPU time per pass
category and the 10 most expensive passes, logged once a second).
Run from the build tree (or build the linux-amd64-release preset for a release
build):
./out/build/linux-amd64-relwithdebinfo/trials_hdGame data is found automatically in an assets/ folder next to the executable or in
the current directory. Point elsewhere with --game_data_root=/path/to/extracted.
On a headless machine, run under a virtual X server:
xvfb-run -s "-screen 0 1280x720x24" ./out/build/linux-amd64-relwithdebinfo/trials_hdPlay with an Xbox-style controller (SDL gamepad) or the keyboard; both control player one and work at the same time. Default keys:
| Action | Keys | Controller |
|---|---|---|
| Throttle | Up / W | RT |
| Brake | Down / S | LT |
| Lean back / forward | Left / A, Right / D | Left stick |
| Return to last checkpoint (menus: back) | Backspace | B |
| Restart track | R | Back |
| Pause | Escape | Start |
| Bailout | Y | Y |
| Menu navigation | Arrows / WASD | D-pad |
| Menu select | Return / Space | A |
| X, LB, RB | X, Q, E | X, LB, RB |
Keys can be changed in the settings menu (below), or with the keybind_* settings in
trials_hd.toml. The keyboard can be turned off there too (mnk_mode = false).
Press F1 (or the controller's Guide button, or Back + Start) to open the settings menu. It is navigated with the mouse, the arrow keys / Return / Escape, or the D-pad / left stick, A and B.
Controls come first: the Keyboard switch and two key slots per action. Select a slot, then press the new key (Delete clears the slot, B or selecting the slot again cancels); Reset Keys restores the default layout. Changes apply immediately.
Graphics:
| Setting | What it does |
|---|---|
| Renderer | Vulkan or WebGPU (WebGPU needs the optional plugin above; restart to apply) |
| WebGPU Adapter | GPU used by the WebGPU renderer; Default picks the discrete GPU (restart to apply) |
| Display Mode | Windowed or fullscreen (applies immediately) |
| Resolution | Window size, or the fullscreen display mode; Desktop = borderless at desktop resolution |
| Render Resolution | Internal resolution: 1280x720, 2560x1440 or 3840x2160 (restart to apply) |
| Unlock FPS | Independent real rendering with the original fixed physics tick (on by default; restart to apply) |
| FPS Limit | Unlimited, 240, 165, 144, 120, 90, 60 or 30 real rendered frames per second; independent of physics with Unlock FPS enabled |
| VSync | Tear-free presentation (restart to apply) |
Save writes the settings (keys included) to trials_hd.toml next to the executable. They are
ordinary settings, so they also work on the command line, e.g.
--display_width=1920 --display_height=1080 --draw_resolution_scale_x=2 --draw_resolution_scale_y=2 --present_fps_limit=60 --host_vsync=false.
--fps_unlock=true --present_fps_limit=0 --host_vsync=false enables uncapped
real rendering. With VSync on, rendering is capped at the display's refresh rate
(or the FPS limit, if lower): the presenter shows only the newest frame at each
refresh, so rendering faster would show an uneven subset of frames.
--fps_unlock=false restores the original game loop for comparison.
The patch in src/fps_unlock.cpp replaces the game's combined frame gate and
separates its update-worker and render-worker synchronization. Game updates keep
the original 16,666 µs fixed step, including the original five-step hitch
limit; the physics timestep is not divided by the render rate. No optical-flow
frames, framebuffer interpolation, or duplicated presentations are used.
Above 60 FPS (or uncapped), the shared normal/batched world draw paths interpolate
rigid models, animated matrix palettes, and the camera between fixed ticks.
Only temporary shader-upload inputs change; authoritative model matrices are not
modified and camera/renderer inputs are restored after each draw. This adds up to
one simulation tick of visual latency. Caps of 60 FPS or below keep original poses.
The game draws the track through the same camera with more than one view per
frame, so each view keeps its own history. Shadow-map cameras stay at their tick
pose (the game samples shadows through light matrices computed per tick), while
the models drawn into them are interpolated. Scene changes, missed ticks, and
translation jumps over two units snap rather than blend. Long stalls beyond the
five-step catch-up budget can still lose simulation time, as in the original game.
Particles, effects and 2D UI still move at the 60 Hz tick rate.
The render resolution is an integer multiple of the game's 1280x720 frame. A true internal 1920x1080 option is not implemented: the game can render a smaller base frame (960x540 at 2x), but its menus and HUD are laid out in 1280x720 pixels and come out cropped and oversized. For a 1080p screen, pick the 1080p display resolution with 2560x1440 rendering (supersampled) or 1280x720 (upscaled).
The main menu is the game's own. "Return to Game Library" is replaced by EXIT GAME,
which quits the app (src/exit_button.cpp). Achievements opens the
host achievements overlay. Leaderboards and Download Content require unavailable
Xbox LIVE services and retain the game's offline / not-signed-in behavior. The
"Offline warning" message box the game shows before a track (scores can't be saved
to Xbox LIVE leaderboards) is skipped (src/offline_warning.cpp).
With no audio device the game continues with silent audio. With several GPUs, pick
one with --vulkan_device=N (the device list is printed at startup).
readback_memexport defaults to false in this port. Trials HD's memexport passes
(GPU point-list draws used by its effects) are only consumed by later GPU work; reading
them back into guest memory forced a full CPU/GPU sync per draw and held gameplay at
15–30 FPS. Disabling it avoids that readback penalty. Set readback_memexport = true in
trials_hd.toml to restore the SDK default.
Verified on Linux with an RX 6600, headless Vulkan, host VSync off, and a
75-second scripted keyboard ride (menus, riding, pause/resume, checkpoint and
restart on Middle Name: Danger; --log_guest_fps=true --log_simulation_rate=true
log both rates once per second):
| Render resolution | Render limit | Real rendered FPS | Fixed updates/sec |
|---|---|---|---|
| 1280x720 | 60 | 60.0 | 59.95 |
| 1280x720 | 120 | 120.0 | 60.01 |
| 1280x720 | Unlimited | 183.4 (GPU-bound) | 59.98 |
| 2560x1440 | 120 | 76.5 (GPU-bound) | 60.00 |
| 2560x1440 | Original loop (--fps_unlock=false) |
59.8 | 60 |
Above 60 FPS every run has distinct model and camera poses within a fixed tick. The 120 FPS limit needs the 1x render resolution on this GPU.
These are the last 15 logged samples, with positive game draws and resolves in every sample. Gameplay captures show normal race-timer progression, riding, explosions and crashes; an original-loop 60-FPS run provides the baseline. This is a one-track smoke test, not bit-identical replay or full-track coverage. Audio uses the silent fallback in this environment. Startup heap-release warnings also occur with the original loop; no guest access violations occurred in these runs.
| Path | What |
|---|---|
trials_hd_manifest.toml |
Codegen manifest |
config/codegen.toml |
Codegen settings (CRT setjmp/longjmp addresses) |
config/functions.toml |
Function starts the analyzer misses (addresses only) |
src/ |
Host-side app code: asset path lookup, license from your package, GPU plugin default, audio fallback, fixed-tick FPS unlock, GPU-wait sleeping, settings menu, keyboard layout, Exit Game button |
src/gpu_webgpu/ |
Optional WebGPU renderer plugin (Dawn): Xenos shader translation to WGSL, EDRAM render target emulation, resolves, textures |
generated/rexglue.cmake |
SDK build glue (from rexglue init) |
generated/default/ |
Recompiled game code; generated at build time, gitignored |
thirdparty/rexglue-sdk |
ReXGlue SDK (git submodule) |
patches/rexglue-sdk/ |
Local SDK changes (one patch), applied automatically at configure time: rendering fixes (16-bit integer resolves, texture exponent bias), render target transfer elision, small-resolve readback, host FPS cap + guest FPS logging, keyboard keystrokes for menus, data race fixes, guest-memory shm cleanup, Vulkan headless surface |
tools/stfs_extract.py |
Extracts your game files (and license) from Xbox 360 STFS (LIVE/PIRS/CON) packages |
tools/build_dawn.sh |
Builds the pinned Dawn for the WebGPU renderer plugin into out/dawn-install |
Trials HD is © RedLynx / Ubisoft. This project is not affiliated with or endorsed by them or by Microsoft. It contains no copyrighted game material; you need a legally obtained copy of the game to use it.
The code in this repository is released under the MIT License. The ReXGlue
SDK (thirdparty/rexglue-sdk) and the changes to it in patches/rexglue-sdk/ fall
under the SDK's own license (BSD 3-Clause).
0 comments
log in to comment.