Emit a shared <stem>_decls.h (runtime extern prologue, static-inline
unaligned helpers, all func + alias-body forward decls) and distribute
function bodies across N ~40k-line <stem>_full_NN.c shards instead of one
monolithic <stem>_full.c. Overlapping-alias host bodies become externally
linked so shard bucketing has no atomicity constraint. runtime.cmake's
GAME_GENERATED_FULL_C is now multi-value (single path still works, so
other games are unaffected until they regen + glob).
Tomba: SCUS_942.36_full.c 41MB single TU -> 36 shards; compile (-O3)
4m35s -> 51s (-j16, ~5.4x). Generated function bytes verified byte-identical
(6245/6245 functions).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the Ape Escape literals in the recomp-ui launcher block with
config-driven values so every PSX title can consume the shared seam:
- gi.name/gi.region <- game.toml [game] name + new optional region key,
falling back to serial-prefix derivation (SCUS/SLUS/LSP -> USA,
SCES/SLES -> Europe, SCPS/SLPS/SLPM -> Japan; unknown -> no badge)
- gi.num_players <- new optional [game] players key (default 1)
- gi.widescreen_supported <- ws_offered (same source as the RmlUi path)
- Localization parity: language_labels/num_languages from
[runtime].languages, language_index matched from the resolved language,
and the chosen language written back to settings on Launch (mirrors
launcher.cpp's bounds-checked write-back)
config_loader: GameConfig gains region (string) + players (int, default 1),
parsed from [game]; existing configs without the keys are unchanged
(recompiler_patch_test 11/11, defaults probe verified against Ape's toml).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace manual gi identity/caps setup with launcher_profile_apply("psx",
&gi), which bundles theme/platform/rom_noun/pad-mode caps + all has_*
deep-settings flags as one unit. Ape Escape now overrides only what the
profile can't know (name, region, pad-mode lock state, aspect mask,
disc-verify off).
Map PSXRecompV4::UserSettings <-> RecompLauncherCSettings both ways for
the deep fields the profile now exposes: window_width, renderer,
supersampling, antialiasing, texture_filter, screen_kind,
frame_interpolation(+fps), spu_hq, auto_skip_fmv, turbo_loads, and
bios_path (string, snprintf-guarded). Fix the pre-existing
texture_filter write-back, which read the legacy ls.linear_filter bool
even though has_texture_filter=1 makes the launcher edit ls.texture_filter
directly — it now reads the right field.
Verified via build-recompui + LNG_SCRIPT screenshots: Settings now shows
the full PSX surface (Window size/Renderer/Supersampling/Aspect/Texture
filtering/Antialiasing/Screen model/Frame interpolation/Skip FMVs/Turbo
loads/Fullscreen, SPU high-quality/Analog deadzone, and a SYSTEM/BIOS
row with Browse), not the old pad-mode+aspect-only surface.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extends the RECOMP_LAUNCHER block in runtime/src/main.cpp to pass the
new RecompLauncherCSettings.pad_mode[2]/aspect_index and
RecompLauncherCGameInfo.pad_mode_supported/pad_mode_selectable/
allow_hybrid/locked_pad_mode/lock_device/aspect_mask fields, sourced
from the same GameConfig-derived locals (ctrl_lock_mode,
ctrl_allow_hybrid, ctrl_lock_device, p1_mode, ws_offered,
ws_ultrawide_offered) the existing RmlUi launcher path already uses.
- ls.pad_mode[0/1] seeded from seed.p1_mode/p2_mode; ls.aspect_index
derived from seed.aspect_num/aspect_den (4:3/16:9/21:9).
- gi.pad_mode_supported = 1 (PSX always has pad modes);
gi.pad_mode_selectable = !ctrl_lock_mode; gi.allow_hybrid =
ctrl_allow_hybrid; gi.locked_pad_mode = p1_mode; gi.lock_device =
ctrl_lock_device; gi.aspect_mask = 0x1 | (ws_offered<<1) |
(ws_ultrawide_offered<<2).
- On LAUNCH, ls.pad_mode[]/aspect_index are copied back into
seed.p1_mode/p2_mode/aspect_num/aspect_den so the launcher's choice
persists to settings.toml exactly like the RmlUi path.
Verified against the real Ape Escape game.toml: aspect_mask=0x7
(4:3+16:9+21:9, offer_ultrawide=true) and pad_mode=[1,1] (analog,
matching default_mode="analog") both flow through correctly. The pad-
mode selector itself renders hidden/locked to Analog in the built
launcher, because Ape Escape's [controller] block sets lock_mode =
true (intentional: it's a dual-analog-only title) -- gi.pad_mode_selectable
correctly evaluates to 0 for this game, same as the RmlUi path's
ginfo.lock_mode already did.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When built with the recomp-ui submodule (RECOMP_LAUNCHER defined), run the shared
recomp_launcher_run_window() instead of the RmlUi psx_launcher::run(); RmlUi stays
the default when the define is absent. Maps UserSettings <-> RecompLauncherCSettings.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The runtime's autocompile spawns `cmd.exe /C python tools/compile_overlays.py
...`; whichever python PATH resolves runs it. On a machine where that is an
MSYS/Cygwin-flavored interpreter (hit live 2026-07-15: devkitPro's MSYS2
python), every gcc shard compile died exit-1 with EMPTY stderr, zero shards
built — and before the observability suite this looked identical to a clean
run.
Root cause (proven by A/B test): _toolchain_env prepended the toolchain dir
to the child PATH as a Windows path ('C:/msys64/mingw64/bin') — under an MSYS
python, PATH is a ':'-separated POSIX list, the drive colon reads as a
separator, and the whole child PATH is mangled, so cc1/as/ld/collect2 lose
their DLLs and die before printing anything. POSIX-prepend rc 0,
Windows-prepend rc 1, same compile.
- _toolchain_env: prepend the toolchain dir in the RUNNING interpreter's
path flavor (X:/... -> /x/... when os.sep == '/').
- native_path (new single chokepoint for compiler-bound paths): absolute,
interpreter-flavor. Documents why flavors must NOT be translated here:
the msys runtime translates POSIX args at the exec boundary with its own
mount table (verified via gcc -v), while a PATH-resolved foreign cygpath
(e.g. Git-for-Windows') corrupts them.
- compile_dll: when the compiler produces no output at all, say so
("gcc likely failed to launch", with the resolved toolchain dir) instead
of printing an empty COMPILE ERROR.
Verified under the exact failing interpreter (devkitPro MSYS2 python,
Tomba SCUS-94236): before = 0 ok / all failed, empty errors; after =
17/17 shards ok, exit 0; the live runtime's next autocompile run loaded
cg5 shards ("-> 122 candidates") and the cache-hash-mismatch banner cleared.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The tier mis-compiled and has been hard-gated off since 2026-06-25; overlay
gaps fall to gcc/tcc shards and the dirty-RAM interpreter, unchanged.
Re-do on master of the removal validated on the stale branch (6da8c02),
reconciled with everything master grew since the fork:
- Delete overlay_sljit.{c,h}, overlay_compile_worker.{c,h}, lib/sljit,
runtime/tests/sljit_*.c, and the runtime.cmake sources/include-dirs.
- Strip the sljit provider/backend from code_provider.{c,h},
overlay_backend.{c,h} ("sljit" in overlay_backend/PSX_OVERLAY_BACKEND now
degrades to auto so stale configs still boot), main.cpp, debug_server.c
(sljit_status/sljit_async/sljit_try TCP commands), overlay_loader.{c,h}
(live policy, JIT-on-miss, persisted-shard cache, async worker glue,
psx_sljit_call/cop2/memx host helpers — reachable only via the removed
cpu->sljit_helpers table), and cpu_state.h (SLJIT_HLP_* enum + the
sljit_helpers[] member; CPUState layout change reshards via the cg tag).
- Master-only entanglement resolved before deletion: 28165d6's Einhander WS
player-X bound emit existed in three paths (gcc code_generator, sljit,
dirty_ram_interp). The gcc shard link contract now forwards
psx_ws_player_x_bound (ABI v13, previous commit), so deleting the sljit
emit path loses no widescreen behavior; ffc6688's game-dispatch block
inside the dead psx_sljit_call helper goes with it.
- Reword comments that described sljit as a live backend (gpu.{c,h},
ws_*_detect.h, cosim_state.c, dirty_ram_interp.c, autocompile.h,
boot_state.h, config_loader.h, compile_overlays.py, and the CPS marker
comments the recompiler emits).
recompiler psxrecomp-game builds clean; game runtimes rebuilt + boot-verified
separately.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
28165d6 (Einhander native-wide player bounds) rewrites configured
[widescreen] signed_x_bound LUI sites into psx_ws_player_x_bound(vanilla)
calls in code_generator.cpp — and that site loop runs in overlay_mode too, so
the call lands in overlay-resident code. But the shard link contract never
forwarded the symbol: any shard containing a bound site fails to LINK
(undefined psx_ws_player_x_bound) and that variant runs interpreted forever —
the same silent class as v10 gte_read_ctrl and v12 psx_rfe_mark_escape (now
loud under the shard observability suite, but still a coverage hole).
The clamp computes against the host's live widescreen state (gpu.c), so a
local copy can't work: add the ws_player_x_bound callback (appended last),
the preamble forwarder (identity fallback => correct 4:3 on a host that
predates it), and the loader init wiring. Bump the ABI to 13 so stale DLL
mixes are rejected and regenerate.
This also unblocks removing the sljit tier: sljit's emit_ws_player_x_bound
was the only other native path carrying the clamp; with the gcc shard path
forwarding it (and dirty_ram_interp already handling the site), the feature
no longer depends on sljit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Completes 5b8098a (fail-closed lifecycle guards on the dispatch/is_candidate/
call_native entry points) at the root: overlay_find_by_range() and
lazy_has_exact_entry() are the loops that actually spin when the loader is
inactive — s_range_page_head[]/s_lazy_entry_head[] get their -1 empty
sentinels only from overlay_loader_init(), which runs solely under
overlay_cache=true (main.cpp). With overlay off they stay zero-initialized
(0 == a valid index), so any unguarded caller traverses a garbage index and
never terminates. That wedged every overlay-off + CPS game at boot before any
game code ran (found via Ape Escape, the only overlay-off title).
All present callers are already guarded; putting the guards inside the
traversal functions makes them safe by construction for any future caller.
Also records the CPS-wedge root cause on the dispatch guard.
Re-land of the overlay-off boot-wedge fix from 6da8c02 (stale branch), reduced
to the parts master's independent 5b8098a port did not already cover.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Re-land of the shard-observability suite from 6da8c02 (stale branch), merged
against current master (keeps master's overlay_ext() platform suffixes,
recompiler_patch hash sources, and the unconditional v12 rfe forwarder).
- compile_overlays.py: ShardStats tallies every shard outcome — per-shard
"SHARD FAIL [class]" lines, a SHARD BUILD SUMMARY, a machine-readable
"PSX_SHARD_RESULT ok=N failed=M skipped=K" line, and a non-zero exit when
any shard that should have built failed (previously a run that built zero
shards exited 0, indistinguishable from a clean run).
- New --check preflight: build every shard into a throwaway dir and report
pass/fail — answers "would this game's shards compile against the current
recompiler + headers?" without a runtime.
- Retain audit-failed interior-fragment C (<phys>_<interior>_fragment_FAILED.c)
for triage, plus a persistent doomed-interior memo (interior_fail_memo.txt)
keyed (phys,interior,region_crc) so autocompile stops re-walking
data-as-code fragments every cycle (~90x off the V8 fragment pass).
- Extract DISPATCH_PREAMBLE to runtime/include/overlay_dispatch_preamble.c.inc
(single source of truth for the shard link contract, loaded by
load_dispatch_preamble(); missing file is a hard error).
- codegen_hash_sources.cmake: fold the shard COMPILE surface (overlay_api.h,
cpu_state.h, the .inc) into the cg tag so header/ABI/link-contract drift
reshards same-tree instead of silently breaking every new shard compile.
- autocompile.c: parse PSX_SHARD_RESULT from the child-output tail, expose
shard_ok/shard_fail/shard_skipped/shard_fail_total over autocompile_status;
ALWAYS rescan on completion so shards that did build load even when the
run also reported failures.
Live proof on the stale branch: Tomba's first autocompile surfaced 9 shard
link failures (undefined psx_rfe_mark_escape — since fixed by v12) and V8
surfaced 91 benign data-as-code fragment failures, all previously silent.
Codegen hash changes => every title reshards once on next build (expected;
loader rejects/regenerates stale shards).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Port the reusable Gaussian interpolation slice from PR #16 while keeping its reverb engine on a separate branch.
Co-authored-by: Martin Penkava <mpenkava1337@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Apply the PS1 1023x511 size rule to every polygon triangle, line, and polyline continuation before widescreen transforms while preserving textured-command state latching.
Co-authored-by: Martin Penkava <mpenkava1337@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>