Root-caused on Tomba 1: overlay_autocompile_cmd's bare 'python' resolved
through PATH to the devkitPro MSYS2/Cygwin python (usr/bin precedes the
Windows install), which segfaults (exit 0x0B00, signal 11) under the
kill-on-close job spawn BEFORE writing a byte — so every compile failed
with '(no output captured)' and nothing pointed at the cause; the title
silently ran interpreted.
At configure time, resolve the command's first token exactly as
cmd.exe /C will (SearchPathA with .exe), print the binding once, and
warn loudly when msys-2.0.dll/cygwin1.dll sits beside the resolved
interpreter, recommending the Windows launcher form 'py -3 ...'.
Also fail loudly up front when the token doesn't resolve at all.
Doc'd in ASYNC_OVERLAY_COMPILE.md. Title game.toml fixes (python ->
py -3) ship separately in each game repo.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Salvaged from PR #13 (feat/launcher-stb-revamp, commit 2ba52e97) by
NyperYuhgard. Master fatal-halts on any DMA start for channel 5; this
moves words to/from RAM directly (device side unemulated: RAM-to-device
reads-and-discards, device-to-RAM zero-fills) and completes the
transfer with the normal IRQ path.
Note: zero-filling from an unemulated expansion device is weaker than
the fail-closed psx_fatal_halt default the other channels follow; kept
as submitted because the contributor exercised it on a real title.
Trimmed from the original hunk: unused DMA_PIO_CYCLES_PER_WORD macro
and mdec_finish_async_{in,out}_transfer wrappers nothing references.
Co-Authored-By: NyperYuhgard <noreply@github.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Salvaged from PR #13 (feat/launcher-stb-revamp, commit 9fd2876c) by
NyperYuhgard. Adds to the dirty-RAM interpreter:
- LoadAddressError/StoreAddressError delivery for misaligned LH/LW/LHU/
SH/SW and misaligned JR/JALR targets, via a new interp_exception()
that performs the architectural COP0 sequence (BadVAddr, Cause ExcCode,
SR mode-stack push, EPC, BEV-selected vector).
- COP0 Cause write protection on MTC0/CTC0.
- CFC0/CTC0 aliases (identical to MFC0/MTC0 on R3000A).
Two deviations from the original:
- The original masked Cause writes to bits [15:10] (backwards vs
hardware); corrected to the writable software-interrupt bits [9:8]
per psx-spx / PCSX-Redux, which also un-deadens the psxTestSWInts
recheck below it.
- The original's post-RFE immediate psx_check_interrupts recheck is
dropped: master already delivers post-RFE interrupts through
psx_rfe_mark_escape() and re-checking inside the handler window is
the documented dbg-build boot-wedge livelock.
Co-Authored-By: NyperYuhgard <noreply@github.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
copy_directory merges, so a mod package deleted from source survived in the
build output indefinitely -- it kept showing on the Mods page and inflated
the release packagers' catalog assertions (Tomba 2 found 7 manifests where 5
exist). Remove the staged tree first; this runs before the game's own
POST_BUILD copy, so game and framework catalogs both land on a clean slate.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Loading acceleration is a property of the emulated hardware, not of any
particular disc, so having each title carry its own copy of the same two
manifests and plugins was duplication waiting to drift.
- target game_id "*" now matches every game. An empty target list still
matches nothing, so a malformed manifest fails loudly as before.
- mods/builtin/packages ships psx.enhancement.fast-loading and
psx.enhancement.cd-speed: independent host-pacing and CD-timing mods,
each a free integer multiplier (1..1024) plus a tickbox that uses
disabled_by to grey out the field it overrides.
- runtime/src/mod_builtin_speed.c registers both activation plugins.
- runtime.cmake stages mods/builtin into every game's output beside its own
catalog, before the game's own copy, so a title can still override an id.
Titles adopting these should delete their per-game copies; keeping both
would show two of each mod.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Players asked for CD speeds beyond the curated 2x/4x list and for the two
loading accelerators to stop being one control (GH TombaRecomp#5: a
speedrunner wants shorter loads but NOT the host speed-up, which runs past
the loading screen and breaks a skip).
Three changes, all general:
- psx_mod_set_disc_speed accepted only divisor 2 or 4. That was policy,
not safety: cdrom.c divides the sector delay and floors the result at
CDROM_MIN_DELAY, and apply_speed leaves XA streaming at authentic
timing, so no positive divisor can produce a zero-delay storm or
pitch-shift FMV audio. Accept 1..PSX_MOD_DISC_SPEED_MAX and let the
mod's description carry the risk warning.
- psx_mod_set_load_acceleration accepted 2..16. Host pacing cannot desync
the guest at all (every frame, CD deadline, interrupt and callback still
happens), so the ceiling was arbitrary. Accept 1..PSX_MOD_LOAD_ACCEL_MAX,
with 1 meaning authentic and 0 still meaning uncapped.
- New option key disabled_by: an option names a BOOLEAN sibling that
overrides it, so "tick Instant and the speed box stops mattering" needs
no second widget type. The parser rejects a dangling, self-referential
or non-boolean reference rather than leaving a control silently always
enabled, and the provider resolves the link against the live selection
so the launcher and the plugins can never disagree about whether a
control counts.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tools/package_release.ps1 packages this framework's BIOS-only runtime, and
each game repo carries its own Windows packager. There was no macOS
equivalent at either level, so the first macOS release (Crash Bandicoot) was
assembled by hand and TombaRecomp already carries a private copy of most of
this logic. Add the generic one here so titles configure rather than
duplicate it.
Takes an already-built runtime plus the per-game identity and produces a
signed, relocatable .app in a staged release folder, archived as
<package>-<version>-macos-<arch>.zip with a .sha256 sidecar — the naming the
game repos' Windows releases already use. It does not build; the game's
build script builds, then calls this.
Release gates, porting the intent of the Windows packager to macOS:
- self-containment via otool (only OS libraries or bundled Frameworks)
- no baked absolute BIOS path, and no build-machine home path. The Windows
gate keys on a drive letter; the macOS tell is a path rooted under
/Users, /Volumes and friends. Matching bare "/...SCPH*.BIN" instead would
flag both the correct relative default "bios/SCPH1001.BIN" and the usage
string "/path/to/SCPH1001.BIN" in every binary.
- the 512 KiB OpenBIOS image and its MIT notice are present
- every --require-mod package is present, so a title cannot ship a launcher
whose Mods page silently vanished
- no disc / retail BIOS / memory-card / generated / local-state files
- saves/ ships empty
- the .app carries a valid signature
The mod catalog is taken from the repo's reviewed source, never the build
tree: a developer build dir accumulates mods/state.toml with enhancements
switched on, which would silently flip a title's default presentation for
every player. Packaging from a build tree without --mods-src is refused.
Bundle layout: staged data lives in Contents/Resources with symlinks from
Contents/MacOS. The runtime resolves bios/, assets/ and mods/ beside the
real executable, but codesign treats anything non-executable under
Contents/MacOS as nested code and rejects the whole bundle —
mods/packages/<pkg>/<version>/ parses as a versioned bundle. codesign seals
a symlink without descending, so this signs cleanly and path resolution is
unchanged. Signing is deliberately not --deep for the same reason.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SDL_main.h is a single-header implementation in SDL3. Include it only from the translation unit that owns main so MinGW emits one WinMain instead of one per SDL-using source.
Extract the useful diagnostic from PR #13 into an opt-in, title-agnostic boot
write-range trace with TCP-queryable context.
Co-authored-by: NyperYuhgard <93950153+NyperYuhgard@users.noreply.github.com>
Both targets are defined unconditionally and every build produces them, but
neither had an add_test() entry, so neither could run and neither could fail.
This is the same gap the codegen tests below them had before they were wired
in, and docs/TESTING.md states the rule directly: "an unregistered test cannot
fail, and a test that cannot fail is not a test."
Both were run manually before being registered and both pass. They are
self-contained -- no BIOS profile, no generated C, no psxrecomp-game -- so
unlike the neighbouring tests they need no WORKING_DIRECTORY.
l2_structural_test -> L2 structural: 239/239 ok
reachable_discovery_test -> ALL PASS
ctest -N goes 33 -> 35, so the three places that state the count are updated
to match. Note they said 29, which was already stale before this change.
This fix is due to games like Tekken 1, which contains multiple discs (multiple .bin files). The problem was that the current code caused the music to repeat several times without counting each second and never allowed the music to continue, both in menus and during gameplay, and also when we paused the game, this repetition continued and did not stop...
(I would appreciate it if you could review and analyze the code. My intention is to keep it updated and improve it over time.)
This is my current possible fix:
`cdda_playing` -> "Is the music already playing?" If the game sends a "Play" command, but the music is paused, we allow the command to pass normally so that the music starts.
`requested_track == 0` -> "Does the game want to play the same track?" When Tekken 1 enters "spam" mode, it sends the pure "Play" command (0x03), without specifying a new track (which in the engine code translates to `requested_track = 0`). If you request track 4, we will allow the command to pass. Since it asks for "0", we know it's just redundancy for a song that's already playing.
`iso_handle` -> "Is there a CD inserted?" This ensures the engine doesn't try to check for phantom tracks and won't crash if there's no .cue file loaded. `iso_track_count(iso_handle) > 1` -> "Does the CD have audio tracks?" The PlayStation game format dictates that Track 1 always contains the game data. If the CD has more than one track, it means it has audio tracks (CDDA).