Project Tourline Prologue

Project Tourline Prologue is a native PC port of Gran Turismo 5 Prologue built using ps3recomp.

README

Gran Turismo 5 Prologue Recompiled

The demo that was better than most finished games.

In 2007 Polyphony Digital shipped a prologue — a paid, standalone slice of a game that would not arrive for another three and a half years. Six tracks, seventy-one cars, and a rendering pipeline that made a 2006 console look like it had no business being that fast. It ran at 1080p. In 2008. People bought a demo and did not feel short-changed, which is not a sentence anybody gets to write twice.

Then it went away. GT5 Prologue was delisted, its online services were shut down in 2013, and the PS3 store that sold it is on life support. What is left is a 1.9 GB package sitting on people's hard drives with no hardware that will outlive them.

Why This Exists

Gran Turismo 5 Prologue Recompiled is a native PC port built with ps3recomp — a static recompilation toolchain that translates PS3 PowerPC binaries into C, then compiles that into an x86-64 executable. No emulator at runtime. No dynamic translation. The game's own code, compiled for your machine.

This one is a step up from every ps3recomp target so far. GT5P is a full retail engine: 42,316 lifted PPU functions, 439 imported system calls across 26 modules, an RSX pipeline built by the studio that cared most about it, and Polyphony's own packed filesystem holding 1.8 GB of assets. Tokyo Jungle, the previous most complex target, is 7,924 functions.

Status

This section is upstream's PSN build (NPUA80075), as last measured on 2026-09-28. This fork's disc build (BCUS98158) is much further along: it runs from the menus into a race. See This fork: the disc build.

NPUA80075: pre-alpha. It boots and loads its own content. It does not draw a frame yet.

AreaState
PKG / SELF decryptDone — RAP-derived key, validated byte for byte
PPU lift42,316 functions, clean compile; one post-lift patch (setjmp_patch.py)
SPU12 images lifted and registered; SPURS job chains dispatch with 0 misses
BootDeterministic: 116 PDIPFS assets, 8 of 8 runs
GraphicsD3D12 up; tiles, Zcull, display buffers and one prepare-flip. No command buffer submitted, no frame
Audio / input / menusNot reached

Latest phase (2026-09-28): rebuilding on current ps3recomp master took the boot from a random 7–108 assets to a fixed 116, and past the heap spin that held it since August. Main now waits somewhere new, not yet characterised. The full investigation, phase by phase, is in docs/progress.md; what comes next is in ROADMAP.md; versions in CHANGELOG.md.

This fork: the disc build

F1 now offers Background pipeline creation and Prepare race pipelines, both enabled by default after restart. Race loading prepares previously learned states; unexpected driver pipelines build in the background. With Preload known shaders enabled, startup preparation runs alongside the opening movie, prioritizing live draws and learned menu states. First visits can still introduce new states. See shader preparation and Phase 99 for validation and limits.

The rear-view depth-clear fix remains enabled. Tunnel lighting, black barriers, road/racing-line issues and close Golf rim verification remain open. Material source-range experiments stay opt-in because they can streak the road.

This is a fork of sp00nznet/GT5P. Upstream targets the US PSN release, NPUA80075 v02.00. This fork adds a second target, the US disc release, BCUS98158 v03.00, which is a different executable: more code, more imports, and a single GT.VOL volume in place of the PDIPFS tree. Everything above and below this section still describes NPUA80075 unless it says otherwise.

Disc build status (2026-10-20, Phase 93): playable from the menus into a race. It boots to the main menu, the dealerships draw, and Arcade reaches Daytona's Race Mode page with an animated pit crew. A Single Race loads and drives. Dealerships run at about 22 to 30 fps; steady 60 fps in races remains open. The main race-start freeze and the 1080p menu's black tree rectangles have fixes; intermittent start stalls remain. MSAA depth now reaches depth of field, the crowd has coloured spectators, and race textures have more cache capacity. High Speed Ring's road filtering and cars appearing over the bridge are fixed, and native pipeline compilation is cached between sessions. Distant scenery now uses its mip chain to reduce grain, and loading-screen car names no longer lose glyph parts or show solid blocks. Road normal-map samples now honour signed expansion, and compressed scenery mip chains stop at their valid shorter-axis limit. RSX_TEX_EXPAND=0 and RSX_BC_MIP_LIMIT=0 restore those two faults for comparison. The opening logos also show at 1080p; SCREEN_AS_TEX=1 restores the old size-only frame-copy substitution that hid them. Dark-region flicker in the menus/showroom also has a fix: dither textures on upper units now use the correct pixel coordinates (RSX_UNNORM_HI=0 to compare). The reported dark High Speed Ring water matches RPCS3 in the user's comparison; its appearance is unchanged. The missing 1080p race-map outline is also fixed: MAIN texture uploads no longer sample overlapping LOCAL render targets (RSX_RT_LOCATION=0 restores the old lookup). Missing replay controls and the reported dark tunnel remain under investigation. Runtime log Auto now stays quiet for IDE/redirected launches too, removing per-job logging that overloaded the sound job chain. Debug reports still work with this setting; Runtime log On explicitly enables expensive tracing. Guest PPU threads now default to separate physical cores (PS3_PPU_CORES=3 restores previous placement), improving measured High Speed Ring throughput. A callback/condition lost-wake defect also has a regression-tested fix. The captured loading/driving freeze has a further fix: an incorrect SPU argument enabled profiling writes over the render-job queue. Correcting it preserves job pointers; a driven two-lap High Speed Ring run reached results without the long pause sequence (SPURS_PM_ARG64=0 restores the defect). On supported CPUs, three busy audio images now use native fused arithmetic, reducing measured sound-worker CPU from 92% to 78% of a core in a High Speed Ring check. CPU/OS detection keeps the portable path on other machines; GT5P_AUDIO_NATIVE=0 restores it for comparison. Remaining audio underruns and stable 60 fps remain open. The rest of this section and AGENTS.md ("Start here") give the details and what is open.

For a bug report, first enable F1 Settings → DEBUG → Debug export (Off by default). When the fault occurs, open F3 → Export debug report, or press F8 while F3 is visible. Attach the ZIP from logs/debug/; the F3 panel also has Open reports folder. The option applies immediately. It retains recent logs only while enabled, so turn it on before reproducing the problem. Reports include settings/build details, frame history, thread/FIFO/SPU state and a best-effort game-window screenshot. While enabled, they also retain up to eight state samples taken during hitches over 250 ms (hitches.txt), so a report after recovery can include the stall itself. A blocked renderer may prevent the screenshot; the rest of the report can still save. The intermittent High Speed Ring grid-preview corruption remains under investigation.

To prepare previously seen shaders before playing, enable F1 Settings → VIDEO → Preload known shaders, then restart. It defaults Off. This build first learns pipeline records during normal play; later launches prepare up to 4,096 recorded pipelines before guest execution, with progress in the window title. Keep Shader cache enabled. New tracks, cars and effects can still compile later: this does not discover every possible shader in the game. Startup takes longer and retains extra pipeline objects in memory. The existing asynchronous option is now named Background shader compilation. The London exports also show separate 1–2-second waits for guest command production with no shader compile work active; startup preload does not fix that unresolved stall.

An offline game-shader compiler can now extract fragment programs from the executable and unpacked assets, then prepare their base and learned render-state variants before play. It uses the normal cache and compiler. This is broader than replaying known pipelines; vertex programs, generated programs, unsupported records and unseen states remain open.

Experimental Windows Vulkan support is assessed in docs/plan-vulkan.md. The runtime has a Linux Vulkan backend to build on; Vulkan is not currently available as a GT5P renderer.

AreaNPUA80075 v02.00BCUS98158 v03.00
DecryptRAP-derived key, ps3sceRPCS3 --decrypt, no RAP needed
PPU lift42,316 functions50,065 functions, clean compile
Imports439 across 26 modules565 across 31 modules
SPU12 imagesPolicy module, 12 job images and 7 job bodies (9,115 functions), 0 dispatch misses
Boot116 PDIPFS assets, 8 of 8 runsMain menu in about 14 s
GraphicsNot reachedD3D12: menus, dealer showroom, pit, race; MSAA, shadow maps, sRGB, cube maps
Audio, input, videoNot reachedMenu music; pads through SDL2, XInput or the keyboard; the opening movie through FFmpeg

Settings (resolution, MSAA, vsync and more) are in gt5p_settings.toml at the root and on an in-game page (F1). F3 shows a profiler.

The table above this section is upstream's and still describes NPUA80075.

What the disc build needs that upstream does not:

  • Per-build constants. Every address and boot setting that belongs to one executable is in config/targets.json, read by the scripts through scripts/target.py and by CMake. Select a build with GT5P_TARGET, or by writing its title id to input/target; the default is NPUA80075, so upstream's flow is unchanged.
  • Pinned ps3recomp runtime. This branch uses upstream v0.18.0 (4e8f3ca8) plus the GT5P compatibility patch in patches/ps3recomp-4e8f3ca8-local.patch. Run python scripts/setup_runtime.py to prepare its isolated checkout. The integration notes describe the retained title-specific renderer, SPURS/codec adaptations, regeneration and rollback. The old 5244db9 patch and shared checkout are preserved for older branches.

Building the disc build, from a dump of your own disc:

python scripts/setup_runtime.py
T=../ps3recomp-gt5p-v018/tools
rpcs3 --decrypt <dump>/PS3_GAME/USRDIR/EMAIN.SELF     # writes EMAIN.elf beside it
cp <dump>/PS3_GAME/USRDIR/EMAIN.elf input/EMAIN.ELF
mkdir -p input/pkg && cp -r <dump>/PS3_GAME/PARAM.SFO <dump>/PS3_GAME/USRDIR input/pkg/
echo BCUS98158 > input/target

python scripts/jumptables.py --write        # config/extra_seeds_bcus98158.json
python $T/find_functions.py input/EMAIN.ELF --json --seed-json config/extra_seeds_bcus98158.json --output analysis/functions.json
python -c "import json; f=json.load(open('analysis/functions.json')); json.dump([x for x in f if int(x['start'],16) < 0xE3EC24], open('analysis/functions_code.json','w'))"
PYTHONPATH=$T python scripts/gen_hle_stubs.py input/EMAIN.ELF analysis/hle_stubs.json
python $T/ppu_lifter.py input/EMAIN.ELF --functions analysis/functions_code.json --output generated --code-end 0xE3EC24 --header-name ppu_recomp.h --source-name ppu_recomp.c --hle-stubs analysis/hle_stubs.json
python scripts/setjmp_patch.py
python scripts/spin_patch.py
python scripts/ui_mask_patch.py
python $T/gen_hle_nids.py --all --out src/gen/ppu_hle_nids.cpp
scripts/build.cmd -DGT5P_TARGET=BCUS98158

The SPU registry for the disc build is committed (src/gen/spu_workloads_bcus98158.c), but the lifted SPU sources are not, as with everything under generated/. To produce them, build once, run with SPU_DUMP_MISS=analysis/spu/dump to capture the job images, then run python scripts/lift_spu_jobs.py and rebuild. The job bodies come straight from the executable (wws_job_bodies in config/targets.json). Re-run both lifts whenever ps3recomp's lifters change: Phases 69 and 72 fixed lifter bugs that only a re-lift picks up.

Optional run-time pieces, fetched into third_party/ (gitignored) and copied beside the exe by the build:

  • scripts\get_sdl2.ps1: controllers through SDL2;
  • scripts\get_ffmpeg.ps1: the opening movie.

Then run play.cmd.

Tools added for this work:

CommandWhat it does
python scripts/soak.py 8 408 headless boots of 40 s; one line of counts per run. Works for either build
python scripts/boot.py 40 NAME=VALUEOne boot with a log summary; extra arguments set the game's environment
scripts\test\*.ps1Drive the game by milestones to a page (dealer, pit, race) and shoot or measure it; see AGENTS.md
scripts\diag\*.pyLog readers, call-count instrumenters for lifted code, RenderDoc scripts
python scripts/threads.pyWhat each guest thread was last doing, from a boot log
python scripts/cdis.py 0xADDRFull disassembly of a function
python scripts/find_setjmp.pyLocate the guest setjmp/longjmp in a build where they are unknown
python scripts/rpcs3log.py RPCS3.logSummarise an RPCS3 log for comparison against a port run

The investigation is Phases 17 to 76 of docs/progress.md (newest first), and AGENTS.md is the working brief for the disc build. Its "Start here" section is the current state.

Screenshots

None are committed: they are pictures of the game's own assets. The disc build presents frames, from the menus to a race.

Getting Started

You need your own copy of the game: the NPUA80075 PSN package and its .rap. Nothing from the game is in this repository, and generated source is never distributed — you lift it yourself, locally, into the gitignored generated/.

Prerequisites: Windows 10/11 x64, Visual Studio 2022 (C++ workload), LLVM 18+ (clang-cl), CMake 3.20+, Ninja, Python 3.10+, ps3sce or scetool, and a ps3recomp checkout next to this repo at ../ps3recomp-gt5p-v018, prepared by python scripts/setup_runtime.py.

  1. pip install -r requirements.txt
  2. Unpack the package: python ../ps3recomp-gt5p-v018/tools/pkg_extract.py your.pkg input/pkg
  3. Decrypt the game SELF:
    MI=$(python scripts/self_metainfo.py input/pkg/USRDIR/EMAIN.SELF your.rap)
    ps3sce -m $MI -d input/pkg/USRDIR/EMAIN.SELF input/EMAIN.ELF
    
  4. Analyse, lift and patch — see Building from source.
  5. scripts/build.cmd
  6. PS3_HEADLESS=1 PS3_VERBOSE=0 build/gt5p.exe

Expected today: the log shows [cellFs] Open(path=...PDIPFS...) lines — 116 distinct files — SPURS job-chain traffic, then the boot waits. No window content yet.

Usage

CommandWhat it does
build/gt5p.exeBoot with a D3D12 window
PS3_HEADLESS=1 build/gt5p.exeNo window; vblank/flip clock and FIFO drain still run. Use over RDP
bash scripts/soak.sh 8 408 headless boots of 40 s; prints unique PDIPFS assets per run
GT5P_MAINSTACK=30 build/gt5p.exeAfter 30 s, print where the main thread is

Always set PS3_VERBOSE=0 when a number matters: verbose logging serialises every guest thread on one stderr lock and changes what the title does.

Settings. Press F1 in the game (or the PS button on a PlayStation controller) for the settings page: arrows or the d-pad to choose, Left/Right or Cross to change, Esc or Circle to close. Changes are saved at once; vsync and the keyboard-as-controller switch apply immediately, the rest after a restart, which the page offers. The settings live in gt5p_settings.toml beside the game (the folder with input\EMAIN.ELF), written on the first start with a comment on each: resolution (720p or 1080p), antialiasing, vsync, the shader cache, the shader indicator, controllers and the keyboard. You can also edit it by hand and restart.

Controllers can be disconnected and reconnected while playing; SDL device discovery runs independently of the game's input calls. Windows audio follows the default output within about half a second and retries if an output becomes unavailable. Diagnostic overrides: PAD_HOTPLUG=0 uses polling on the calling thread; AUDIO_HOTPLUG=0 keeps the initially opened output.

While the game builds shaders the picture stops for a moment (several seconds the first time a screen is seen); a "Compiling shaders" badge in the corner says so. It can be turned off on the settings page. An environment variable for the same switch (shown in the file's comments) wins over it; delete the file to get the defaults back. GT5P_SETTINGS=<path> names another file, and GT5P_NO_SETTINGS=1 ignores it, as scripts/boot.py and scripts/soak.py do.

Warming race shaders. Run scripts/test/race.ps1 -Track HighSpeedRing -Tag warm-hsr -Seconds 60 in PowerShell to load the pit and race and populate the local caches before playing. -Track Daytona selects Daytona instead; append GT5P_RESOLUTION=1080p RSX_MSAA=2 for that configuration. The helper closes the game when finished. It warms materials seen during the run; other cars, views or tracks can still need new shaders on their first appearance. Add -IntroShots to save the race-start transition once per second for graphics checks; these shots finish before fps sampling begins. -GridIdleSeconds 180 or -PitIdleSeconds 180 adds a three-minute wait on that page with periodic window shots. The helper rejects FPS sampling with no actual presents; inspect the shots and racing timer to confirm race entry. Both shader bytecode and driver-compiled pipelines are cached locally. Driver updates invalidate pipeline blobs safely; rejected entries are rebuilt. RSX_PSO_DISK=0 disables the driver cache, and disabling the shader cache in settings disables both. Keep shadercache/; do not download another machine's pipeline blobs. Repeated bytecode used by pipeline variants is also retained in memory, up to 64 MiB (RSX_DXBC_MEM=0 disables that layer). Loading a saved native pipeline does not count as compiling work on the badge.

Movies. The opening movie is decoded with FFmpeg, loaded at run time. Run scripts\get_ffmpeg.ps1 once (it fetches a pinned LGPL build into third_party\ffmpeg), then rebuild; without it movies play black and silent. The movie itself is part of the game's TV content, which the title installs to its hard-disk folder (gamedata\dev_hdd0\game\BCUS98158\USRDIR\grim\file\contents). This port does not yet perform that install; copy it from an RPCS3 install of the same game.

Building from source

T=../ps3recomp-gt5p-v018/tools
python $T/elf_parser.py     input/EMAIN.ELF --imports > analysis/imports.json
python $T/find_functions.py input/EMAIN.ELF --json     --seed-json config/extra_seeds.json --output analysis/functions.json
# drop the .rodata find_functions mistook for code
python -c "import json; f=json.load(open('analysis/functions.json'));   json.dump([x for x in f if int(x['start'],16) < 0xBE0AF4], open('analysis/functions_code.json','w'))"
python $T/ppu_lifter.py input/EMAIN.ELF --functions analysis/functions_code.json     --output generated --code-end 0xBE0AF4 --header-name ppu_recomp.h     --source-name ppu_recomp.c --hle-stubs analysis/hle_stubs.json
python scripts/setjmp_patch.py              # required after every lift
python $T/gen_hle_nids.py --all --out src/gen