Commit Graph
423 Commits
Author SHA1 Message Date
Henrik Rydgård 5c84203bbe Merge pull request #22512 from hrydgard/inet-select-wait
sceNetInet: Wait in emulated time instead of blocking the host
2026-10-27 22:17:24 +01:00
Henrik RydgårdandClaude Opus 5.5 6056ec419a Headless: Run several instances that talk to each other over ad hoc
--instance=N sets PPSSPP_ID, so each instance gets its own 127.0.0.N address
and MAC; headless left it at 0, which made its address 127.0.0.0.
--realtime keeps emulated time from running ahead of the wall clock. Only
display flips paced headless, and a test that doesn't draw never flips, so
two instances waiting for each other over the network timed out at once.
--adhoc-server runs the built-in ad hoc server.

Tools/adhoc_multi.py runs a multi-console pspautotest (X_host, X_client,
X_client2, ...) on one instance per role, with the built-in server or, with
--relay, a local aemu_postoffice in relay mode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 17:13:45 +01:00
Henrik RydgårdandClaude Opus 5.5 4e156e8945 Headless: Act as the only instance
Headless doesn't join the instance counter, so PPSSPP_ID stayed 0 and the
localhost IP derived from it came out as 127.0.0.0, which macOS won't bind.
Every sceNetInetBind failed with EADDRNOTAVAIL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 17:02:03 +01:00
Henrik RydgårdandClaude Opus 5.5 fb371af6bb PSAR: Unpack the 1.00 updater, and the pre-decrypted 3.60 archive
The 1.00 updater's DATA.PSAR is a version 1 archive, the only one there is,
and its blocks carry tag 0x06000000, which the PRX decrypter had no key for.
1.50's mesg_led.prx keeps its keys raw in a table after their tags, the
0x0E000000 one we already use among them, and the 0x06000000 one there
decrypts all of 1.00 (with KIRK code 0x49, following the other tags). Its
first record has no version text either, so the version now comes from the
firmware's own version.txt when that's missing.

Fix a zlib usage bug.

Headless --unpack-updater also exited 0 when every file failed, so now it
fails if any did.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-25 19:00:48 +01:00
Henrik RydgårdandClaude Opus 5.5 b547e01c9d VSH: Boot games the XMB starts like any other game launch
A disc game or memory stick EBOOT started from the XMB used to be swapped in under the XMB's session,
so it ran without its PARAM.SFO, compat flags, game config, window title, savestates and so on.
Much of that is decided once, at boot (the memory map, the GPU backend, the shader cache), so
sceKernelLoadExec now asks the frontend for a reboot into the disc image or EBOOT (PSP_RequestBoot)
and stops. The app and headless boot it like a game from the game list.

The game keeps the XMB's launch args (CoreParameter::launchedFromVSH), so exiting boots the XMB
again, with the disc that's in the drive and those args, and the XMB comes back where it was without
auto-starting the disc. Homebrew keeps the disc that was in the drive. A disc whose executables are
all zeroes (the EUR Video Demo Disc's "game") still loads in place and fails back to the XMB.

In headless, the run carries on through the reboot, starting stopped under the debugger like the
first boot, since the reboot loses a pending cpu.runUntilTime.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 13:36:28 +02:00
Henrik RydgårdandClaude Opus 5.5 56c76ace35 thin3d: Swapchain objects, and multiple swapchains on Metal
The backbuffer is now a Swapchain, from GetPrimarySwapchain(), and
callers bind, measure and read it back through that instead of nullptr.
nullptr still means it too. Backends with one swapchain map the primary
to their old backbuffer path.

On Metal, CreateSwapchain() adds more, each with its own layer, depth
buffer and drawable. Passes merge per swapchain, and Present() presents
every drawable acquired that frame. The backbuffer is just the first
of them. Nothing creates a second one yet.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 18:42:53 +02:00
Henrik RydgårdandClaude Opus 5.5 edac5b813f Headless: Run the firmware's own modules under --vsh
Headless forces every library onto our HLE for anything that isn't a disc,
so pspautotests run without a firmware dump, and a --vsh boot fell under
that too. The VSH is firmware, and gets the same modules as in the app:
with our HLE sceMpeg, the exports of the real mpeg_vsh.prx were dropped and
the video player called into stubs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 17:32:43 +02:00
Henrik RydgårdandClaude Opus 5.5 6e3994aac0 CmdLine: Drop the legacy cpu core flags, use --cpu=
-i, -j, -r, -J, --ir and --jit-ir are gone. -r was also --root's short form,
and --root won, so -r took the next argument as the root path and nothing
booted (test.py -r failed every test).

-i, -j and -J now fail with an error pointing at --cpu= instead of quietly
running on the default core. Docs and test.py updated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 16:05:55 +02:00
Francisco José García García 4ed0bfd08a Headless: Scripted button presses and timed screenshots
--press=SECONDS:MASK[:DURATION],... holds sceCtrl buttons at emulated
times, and --screenshot-at=SECONDS:FILE,... saves screenshots along the way,
to drive a game past its menus in a run. With presses, the run loop steps
1/60 of a second at a time so short ones land.
2026-10-21 08:58:10 +02:00
Henrik RydgårdandClaude Opus 5.5 52afb9a677 Headless: Count --timeout-emulated from when --state has loaded
The game boots until the queued state is read, decompressed and applied, which takes a varying amount
of host time, and those boot frames counted towards the timeout. So the same state ended on different
frames depending on the host, for example while texture replacements competed for the IO threads.
After the load the emulation is identical: syscall traces of Fate/Extra CCC with no texture pack, and
with one loading slowly or instantly, are byte for byte the same.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 11:22:31 +02:00
Henrik RydgårdandClaude Opus 5.5 9dd6d2de10 Headless: Always initialize networking
Without WSAStartup, every sceNetInet socket call failed on Windows with
WSANOTINITIALISED, so no inet test could run there.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 08:59:03 +02:00
Henrik Rydgård e2e0e0c748 Merge pull request #22666 from hrydgard/savestate-fixes
Savestate fixes: rewind ring, slot operations, load failure messages
2026-10-19 18:46:20 +02:00
Henrik RydgårdandClaude Opus 5.5 6163bf5911 SaveState: Show which PPSSPP version saved a state when loading it fails
The version comes from the file header and goes in the failure message's
metadata line. A state from another version is the usual reason a load
fails. Headless prints it too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 16:28:47 +02:00
Henrik RydgårdandClaude Opus 5.5 9ddfcca670 Firmware install: Fewer and parallel file operations into the staging directory
Through Android's storage framework, every exists check, create and open is a round trip to the
storage provider, and writing each file took four of them (a check of its directory, a check of
the file, the create, the open). That made a 6.61 install take around 30 s on a phone, against a
fraction of a second of decrypting and inflating.

The install unpacks into a staging directory it has just cleared, so nothing in it can exist yet.
With the new outputDirIsEmpty option, each directory is created once and each file is created and
opened without checking for it first (File::WriteDataToNewFile), on the IO threads while decoding
goes on. A name that turns up twice still goes the careful way, overwriting the first. The swap
into place at the end is unchanged.

Headless --unpack-updater uses the same path when the output directory doesn't exist yet. Its
output is identical either way. With SIMULATE_SLOW_IO, unpacking the fonts went from 17 s to 7.5 s,
most of what's left being the fixed cost of opening the updater.

The UI's install already runs on a thread pool IO thread, so writes queued on the same pool
could stall behind other long IO tasks, and the pool drops queued tasks unrun at teardown,
which would have left the install waiting forever on its in-flight count. Eight threads owned by
the writer (attached for JNI) avoid both, and headless no longer has to start the pool for it.

Also compare file names case-insensitively when looking for repeats, since the storage usually
is case-insensitive and a second create there makes a renamed copy.

Checked with ThreadSanitizer, and on a read-only destination (every file fails, no hang).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 16:06:47 +02:00
Henrik RydgårdandClaude Opus 5.5 b2fc4a80fe Metal: Leave the backend out of the libretro cores
They get their graphics context from the frontend, so they compiled and
linked the Metal backend without being able to use it. Adds
PPSSPP_API_METAL, like PPSSPP_API_D3D11, for the code that only exists
with it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:05:14 +02:00
Henrik RydgårdandClaude Opus 5.5 8d7ce57fa8 Metal: Hook up the backend on Mac, with the null GPU for now
Selectable with --graphics=metal, the backend choice in settings and the
Mac menu bar. SDL creates a Metal view for it, and headless renders
offscreen. Games run on the null GPU until there's a GPU/Metal.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 12:02:22 +02:00
Henrik RydgårdandClaude Opus 5.5 d68d45a441 Headless: Default to the app's memory stick on Mac and Linux
Headless used ~/.ppsspp, which nothing else uses, while the app uses
~/.config/ppsspp (or $XDG_CONFIG_HOME/ppsspp on Linux). The derivation
is now shared, and the app's default is unchanged. Windows and iOS keep
their own.

test.py and frametests.py pass a memory stick next to the binary, as
Windows headless already defaulted to, so the tests still don't write
into the user's memory stick or see its firmware.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 12:37:04 +02:00
Henrik RydgårdandClaude Opus 5.5 57cc1a6cb7 Headless: Run on iOS as an app, and frametests.py --ios
iOS only runs app bundles, so with HEADLESS=ON the iOS build makes
PPSSPPHeadless an app (org.ppsspp.headless) that starts UIKit and runs
the headless main on a thread with the arguments devicectl launched it
with. Vulkan only, and the IR interpreter since there's no JIT.

frametests.py --ios installs it, copies over the dumps the device lacks,
and compares each render with the host's Vulkan one like --adb does.
Each devicectl call costs a second or two, so the new --screenshot-dir
lets one launch render a batch of dumps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 11:11:31 +02:00
Henrik Rydgård 0a9b57371f Delete the spline quality setting 2026-10-16 13:27:26 +02:00
Henrik RydgårdandClaude Opus 5.5 1d7487e5a7 GPU: Add a null GPU, and headless --graphics=null to use it
GPU_Null derives straight from GPUCommon and has no thin3d context, framebuffer
manager, texture cache or shaders. It runs display lists with the flow control,
bounding boxes, matrix loads (the hardware backends'), immediate vertices and
block transfers of the others, moves the vertex and index pointers past each
draw and curve, and charges the per-vertex and fill cost the software renderer
does. A DrawEngineCommon that queues nothing provides the vertex decoders.

It's for measuring the CPU emulation. From savestates, God of War, Tekken 6,
Wipeout Pure and LocoRoco flip the same frames in 10 emulated seconds as with
Vulkan and software, in 44-77% fewer instructions than Vulkan.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 01:12:38 +02:00
Henrik RydgårdandClaude Opus 5.5 c308ab1b99 Headless: Decode vertices with the JIT where the host can, like the app
Game runs in headless measured the C++ vertex decoder, which the app only
uses where it can't JIT. pspautotests and the frametests pass with the
JIT decoders. --vertex-jit=false brings back the C++ decoder.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-15 09:55:35 +02:00
Henrik RydgårdandClaude Opus 5.5 edef36d97e Load game patches installed on a PSP, given its Fuse ID
- Fixes #12343

Killzone: Liberation's Chapter 5 DLC is an installer that the PSP turns
into ms0:/PSP/GAME/<id>/KZL.PRX, which the game loads with
sceKernelLoadModuleDNAS. That file is a PGD around a type 3 PRX (decrypt
mode 18), both keyed to the console that installed it. Its Fuse ID
gives everything needed:

- libkirk: the per-console key derivation, and KIRK commands 3, 5 and 8
  built on it (KIRK ROM per uofw, key mesh per psdevwiki).
- PrxDecrypter: type 3, which is type 5 with the first step through
  KIRK command 3 (JPCSP's PRX.java), and the DNAS PGD unwrap.
- sceKernelLoadModuleDNAS uses the new FuseID setting (or --fuse-id).
  Without one, or with the wrong one, the game falls back to its stock
  module and an OSD message says why.
- Headless --re-decrypt takes --re-decrypt-key and --fuse-id, to decrypt
  such a file offline.

Verified with a patch installed on a real PSP: the v1.20 module loads,
starts and reads CHAPTER5.BIN. Type 5 output is unchanged (an NPJH50521
PBOOT.PBP decrypts byte-identically). How to install one over PSPLink is
in docs/dnas-patches.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 18:10:08 +02:00
Henrik Rydgård 5ca12f0050 Merge pull request #22554 from hrydgard/cpu-equiv-2
CPU equivalence 2/6: Run every core in the guest's rounding mode and flush-to-zero
2026-10-14 10:56:15 +02:00
Henrik RydgårdandClaude Opus 5.5 4f47475a69 Headless: Support --appendconfig
Merges the settings in an ini file over headless's own defaults, for runs that
need something like JitDisableFlags. Unlike in the app, the result is never
saved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 09:26:29 +02:00
Henrik RydgårdandClaude Opus 5.5 48f709574f Headless: --disable-gpu-features=dual-source-blend
Mali-G71 and PowerVR GE8320 Vulkan lack it, and without it WipEout Pure,
Juiced 2 and 16131 UCES00001 render like they do there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 01:33:32 +02:00
Henrik RydgårdandClaude Opus 5.5 fbe1224fed Headless: --disable-gpu-features=framebuffer-fetch
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 01:33:32 +02:00
Henrik RydgårdandClaude Opus 5.5 7c118f2ee3 Headless: Add --hardware-transform and --disable-gpu-features
--disable-gpu-features=clip-distance,cull-distance,depth-clamp acts as if the
GPU lacked those, to test the fallbacks for old Mali on any machine. It
replaces the simulateOldMali constant. --hardware-transform=false runs every
draw through software transform.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:44:34 +02:00
Henrik RydgårdandClaude Opus 5.5 c7d60a5cfb Headless: Vulkan and OpenGL ES on Android, and frametests.py --adb
The Android headless build only did software rendering. Vulkan takes the
offscreen path desktop headless already uses, and OpenGL ES renders into an
EGL pbuffer, so both run from adb shell with no window. Assets are found next
to the executable, like on desktop, instead of in an APK path that no longer
exists.

frametests.py --adb pushes the binary, assets and dumps to a device, runs each
dump there with Vulkan and GLES, and compares the screenshots with the host's
Vulkan render using Tools/image_compare.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:44:34 +02:00
Henrik RydgårdandClaude Opus 5.5 8662d85d70 Headless: add --frame-stats, flips per emulated second after a state load
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:52:46 +02:00
Henrik Rydgård c51daff839 Merge pull request #22498 from hrydgard/sce-audio-reschedule-fix
sceAudio rescheduling fix
2026-10-11 09:22:18 +02:00
Henrik RydgårdandClaude Opus 5.5 a4678112c4 Headless: Don't call __StartLogAudio on mobile builds
The audio dumping code is compiled out with MOBILE_DEVICE, which broke the
Android headless link.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 01:25:33 +02:00
Henrik RydgårdandClaude Opus 5.5 80e346d802 Headless: Honor --nand
ApplyToConfig() set nandRootDirectory from it, but headless then reset it to
the memstick's PSP/NAND unconditionally, so --nand did nothing there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 20:33:34 +02:00
Henrik RydgårdandClaude Opus 5.5 3dddacbfe0 Headless: Add --dump-audio=FILE.wav to record the mixed audio output
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 20:04:37 +02:00
Henrik RydgårdandClaude Opus 5.5 6cf4872e78 Android: Hand microphone input over on the CPU thread, like the other hosts
The Oboe input callback used to call Microphone::addAudioData directly,
writing PSP memory from the audio thread. Now it only pushes samples
into a SampleRing, and pollRecording drains it on the CPU thread.
Starting and stopping still go through Java, which asks for permission.

Every host with a microphone polls now, so sceUsbMic drops what only the
push path needed: QueueBuf's recursive mutex, restarting the mic on a
device change, and the startMic parameter nobody read. Also removes the
unused System_AudioRecordingState and Microphone::isNeedInput.

The Java microphone_command branch didn't return true, so every command
logged "processCommand failed".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 18:49:21 +02:00
Henrik Rydgård bd3e55a104 Merge pull request #22487 from hrydgard/android-lifecycle-fixes
Android lifecycle fixes, file sharing support (unused)
2026-10-10 16:43:58 +02:00
Henrik RydgårdandClaude Opus 5.5 43bd05cd70 Headless: Say so when a game fails to boot, and exit with an error
Outside --compare, a boot failure printed nothing (only a GitHub Actions
annotation) and exited 0, which looks like a run that did nothing. Seen as
rare silent zero-output runs in headless_bench.py.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 15:53:36 +02:00
Henrik RydgårdandClaude Opus 5.5 a89fdb2c9a SoftGPU: Remove the pixel and sampler JITs
The C++ paths draw and sample four pixels at a time in vector lanes,
while a JIT function only does one, and with a JIT function in use the
span and quad paths were skipped. On x86 the JITs had become slower:
Wipeout took 13% more wall time and 25% more CPU with them, God of War
3% more. They were also a second implementation of every GE rule, and
ARM never had them.

The SoftwareRendererJit setting goes with them. RasterizerRegCache.h
keeps only the types the pixel and sampler functions take, as
RasterizerTypes.h.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-10 13:44:48 +02:00
Henrik RydgårdandClaude Opus 5.5 d857a57a25 Headless: Save screenshots at the render scale, and compare at 1x
GetCurrentFramebuffer read back at the render scale, and the 480x272 crop of it
kept the top-left corner. Read comparisons back at 1x, and save the whole
display at the render scale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 13:47:39 +02:00
Henrik RydgårdandClaude Opus 5.5 0dc6381ba4 Headless: Add --dump-compat, to apply compat settings to frame dumps
Headless leaves a game's compat.ini settings out when replaying a frame
dump, so comparisons with the PSP see what the renderer itself does. This
puts them back, for checking what a compat entry does to a dump (Puyo Puyo
Fever 2's depth rounding).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 09:59:59 +02:00
Henrik RydgårdandClaude Fable 5.1 2f6bb8d55e Android: Don't leak the activity on recreate, and hand it over safely
registerCallbacks took a new global ref to the activity on every onCreate
without releasing the previous one, and unregisterCallbacks only runs on a
real shutdown. Since rotation and resizing recreate the activity, each of
those leaked the old one, view hierarchy and all.

Releasing it means other threads can no longer use the raw global ref,
which had been copied into AndroidStorage and TextDrawerAndroid and was
read from the emu thread and IO threads. They now take a local ref under a
mutex (Android_GetActivity, and the equivalent inside AndroidStorage for
the shortcut activity's sake), so a call in flight keeps its activity
alive while the UI thread swaps in the new one.

The "no activity" bail is a null check on that local ref rather than a
separate HasActivity() test beforehand. Between such a test and the call
the UI thread can swap the activity out, and we'd hand null to a Java
method that wants a Context. The one remaining HasActivity() caller never
passes the activity on, so it has nothing to race with.

The ContentUri class ref and method IDs are now only looked up once,
instead of leaking a class ref per activity.

Headless and the unit test executable link Common but not app-android.cpp, so
they get a stub for this the same way they already do for getEnv and findClass.
Without it the Android CI build fails at link time, where a normal gradle build
is perfectly happy.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01152igCdjTNhT29qvphPyPC
2026-10-08 18:54:46 +02:00
Henrik RydgårdandClaude Opus 5.5 f22d9298f6 Headless: Convert hardware depth like ReadbackDepthbuffer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 10:36:19 +02:00
Henrik RydgårdandClaude Opus 5.5 6c04b9f068 Headless: Save the depth buffer from the hardware renderers too
--depth-save converts their float depth (z / 65536) to the PSP's 16-bit
values, or writes the unrounded values when the path ends in .f32.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 10:36:19 +02:00
Henrik RydgårdandClaude Opus 5.5 2c5ff39238 Headless: Add --screenshot-render-target
Lets a dump replay stopped with --replay-end be compared against the PSP on
an offscreen buffer. The framebuffer commands after the draw limit are
dropped too, so it shows the buffer the last drawn primitive went to.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 3c450eed4b Headless: Add --depth-save and --replay-end for frame dump comparisons
--depth-save writes the depth buffer next to the screenshot (u32 width, u32
height, then u16 depths), and --replay-end stops a frame dump replay after
that many primitives. Both match pspautotests' ppdmp-playback, so a dump's
depth can be compared with the PSP's at any point in the frame.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 15:09:42 -06:00
Henrik RydgårdandClaude Opus 5.5 2fdc96e9df Headless: Always print the debugger port when --debugger=0 picks one
wsdbg --launch learns the port from our output, which without --log was
nothing. Also set the port before reporting the server as running, so
WebServerWaitForStartup() callers can read it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 15:39:08 -06:00
Henrik RydgårdandClaude Opus 5.5 f6e70b88ad Headless: Take the timeout screenshot before ending the draw frame
On Vulkan, reading back the display framebuffer after EndDrawFrame hit
the insideFrame_ assert in CopyFramebufferToMemory, so any run that
timed out with --screenshot-save crashed in debug builds.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 14:05:00 -06:00
Henrik RydgårdandClaude Opus 5.5 619d331a86 Headless: Use localhost as the adhoc server
The net tests need WLAN on, but that also made every game run log into a
real adhoc server on the internet, so results depended on the network.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 12:37:48 -06:00
Henrik RydgårdandClaude Opus 5.5 e015977f3c Headless: Add --dump-file to copy a file out of a disc image
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 15:23:08 -06:00
Henrik RydgårdandClaude Opus 5.5 e310d144c9 Headless: Don't end a --debugger-run run when the CPU stops
A stop ended the run unless the CPU had been told to break at start, which is
only --debugger. So under --debugger-run, pausing from the debugger (or any
breakpoint) exited the process. Key it on whether the debugger is on instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 12:35:41 -06:00
Henrik RydgårdandClaude Opus 5.5 ea101b714f Headless: Run OpenGL offscreen on macOS, without a window
A CGL context with no drawable, rendering into a framebuffer object of its
own that stands in for the backbuffer through g_defaultFBO. Core profile,
as the SDL app uses on macOS.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 09:47:58 -06:00