Commit Graph
16314 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 d509370217 POPS: Give the disc switching table's empty strings a real, empty string
Older emulators (3.95 to 4.05) read an empty string's first character, and a null pointer there crashed
them as soon as they drew the menu, with one disc too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 22:06:21 +02:00
Henrik RydgårdandClaude Opus 5.5 31fd8e8a1c POPS: Multi-disc on every firmware from 5.01, by leaving the descrambling to the emulator
With the converted header's key of zeros, the emulator's descrambling of its disc switching strings only
reverses them, so ours go in backwards and its code is left alone. The tables are found by the code that
indexes them, which is the same from 3.95 on with only the registers differing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 19:36:02 +02:00
Henrik RydgårdandClaude Opus 5.5 572260101c POPS: Multi-disc on firmware 6.20 and later, the reset dialog's disc number, and a POPS string category
Every MAC comparison the manager makes passes for a converted EBOOT, rather
than the one in 6.60's disc header check: the export it's in has a
different NID in each firmware. The disc switching text is found in the
emulators of 6.20 to 6.61 (earlier ones have the same descrambling,
compiled differently), so a playlist on an earlier firmware fails with a
message saying so.

The reset dialog's disc number is the disc picker label's, moved by a
table of its own per language, which is now laid out for our text too.

The PS1 strings get a category of their own, POPS, with the experimental
warning moved there. langtool gets add-section, to make the section for a
new category, and inserts into an empty section rather than dropping the
line (which move-key had already removed from the old one).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 18:05:06 +02:00
Henrik RydgårdandClaude Opus 5.5 85e51ba23a POPS: Multi-disc games from .m3u playlists, with our own disc switching text
An .m3u of up to five disc images (any format PS1EbootFileLoader takes)
becomes a multi-disc EBOOT: a PSTITLEIMG header listing a PSISOIMG per
disc, as the firmware's POPS takes them. The discs share the first one's
ID for the memory card, and each keeps its own for the emulator's per-game
fixes. The game browser lists playlists, and hides the discs they list.

A converted multi-disc EBOOT has no MACs for its disc headers, which the
manager's disc header check compares when there are several discs, so
that comparison passes. It also has none of the key that the emulator's
disc switching strings (the HOME menu's item, the switch, confirmation
and reset dialogs) are descrambled with. Those are skipped, and ours,
from our translations, take their place, with the disc number's
positions laid out for them with the firmware's ltn0.pgf.

On Android, a cue sheet, .ccd, .mds or playlist given to PPSSPP as a
single file, rather than through a folder it has access to, now says so
rather than failing to find the other files.

Not done: the reset dialog's current disc number, which isn't drawn
where its gap is yet.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 17:15:56 +02:00
Henrik Rydgård 31e94fcf68 Merge pull request #22747 from hrydgard/virtual-pops-eboot
POPS: Boot PS1 disc images (cue/bin, ccd/img, mds/mdf, CHD, iso) as converted EBOOTs made on the fly
2026-10-23 16:51:55 +02:00
Henrik Rydgård c57f8c993d Merge pull request #22745 from hrydgard/android-file-grants
Android: Manage our persisted URI grants before the system drops them
2026-10-23 16:51:23 +02:00
Henrik RydgårdandClaude Opus 5.5 4f1298982b POPS: Warn that PS1 support is still experimental when a PS1 game starts
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 16:05:00 +02:00
Henrik RydgårdandClaude Opus 5.5 f4f68d37cc File dialogs: Offer PS1 disc images (.cue, .ccd, .mds, .mdf, .bin, .img)
Windows also gets a filter of its own for them. iOS's picker takes any
file and Android's doesn't filter by extension.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 15:50:28 +02:00
Henrik RydgårdandClaude Opus 5.5 4775d235ab UI: PS1 games get a region and the PS1 icon, and list views show the ID of games without an icon
DetectGameRegionFromID() only knew PSP IDs, so a PS1 one (SLUS, SCES,
SLPS and so on) came out as homebrew. A PS1 disc image or EBOOT with no
icon of its own showed the app icon in the game browser, and the list
views left out the ID of any game drawn without a picture.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 15:48:01 +02:00
Henrik RydgårdandClaude Opus 5.5 9312bf06d1 POPS: Boot PS1 discs from CHD and Alcohol .mds/.mdf images
A CD CHD is read through its track metadata: each track's frames are
padded to a multiple of four, a pregap is stored only when its type
starts with V, and audio is big-endian. A .chd that's a CD is a PS1 disc,
PSP discs being DVDs. The FileLoader-backed core_file the PSP CHD block
device used is now OpenCHDThroughLoader(), for both.

An .mds gives each track's start, pregap and length, and where in the
.mdf it's stored, with 96 bytes of subchannel data after each sector.
Anything between tracks isn't stored. A bare .mdf goes through its .mds,
which the game browser lists, and archives install with both.

MODE2/2336 cue sheets, which only had their sector headers made before,
are now tested too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 15:01:22 +02:00
Henrik RydgårdandClaude Opus 5.5 34a0090a18 Install PS1 discs from zip and 7z archives, and show them as playable only with a firmware that runs them
A zip or 7z with a PS1 disc in it (a cue sheet and the files it names, a
.ccd with its .img and .sub, or a lone raw .bin or .img) is now installed
like a zipped ISO. A disc of several files goes into a folder of its own,
named after the cue sheet or .ccd. An ISO in the archive still takes
priority, and a lone .bin or .img only counts when nothing else does.

The game screen offers Play for a PS1 disc image only when the installed
firmware's POPS can run it: 3.95 to 6.61, with popsman.prx.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 15:01:22 +02:00
Henrik RydgårdandClaude Opus 5.5 23dffa9db3 POPS: Boot PS1 disc images as converted EBOOTs made on the fly
A cue sheet, a CloneCD .ccd, or a bare .bin, .img or .iso of a PS1 disc
now boots on the firmware's POPS, as the converted EBOOT.PBP popstation
would make from it. PS1EbootFileLoader builds the PBP header, PARAM.SFO
(the disc ID from SYSTEM.CNF) and the PSISOIMG header, TOC and block
index, and serves the disc image from the image's own files. Each block
of 16 sectors is a stored DEFLATE block: the manager won't read an image
of uncompressed blocks in its own format, but the converted PBP's inflate
takes stored ones, and with every block the same size, reads map straight
onto the files. Nothing is compressed or written anywhere.

Cue sheets can have any number of files, AUDIO tracks, INDEX 00 and
PREGAP, and a missing FILE falls back to the cue sheet's own name. A
2048-byte .iso gets Mode 2 Form 1 sectors made around its user data.

The EBOOT is mounted as the only file in host0:, through BlobFileSystem,
which now saves its open handles in savestates. The game browser lists
.cue, .ccd, .bin and .img, leaving out the .bin and .img files a cue
sheet or .ccd next to them describes, and a bare image opened directly
goes through the cue sheet that names it. GameInfoCache reads the title
and ID of these, and of real PS1 EBOOTs, from their PARAM.SFO.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 15:01:22 +02:00
Henrik Rydgård da92d72d81 Merge pull request #22746 from hrydgard/cpu-jump-low-bits
CPU: jr and jalr ignore the target's low two bits on the main CPU too
2026-10-23 14:59:31 +02:00
Henrik RydgårdandClaude Opus 5.5 88d6befc02 CPU: jr and jalr ignore the target's low two bits on the main CPU too
On a PSP, jr and jalr to an address with its low two bits set raise no
address error: they run from the aligned address, the PC keeps no low
bits, and jalr's link is unaffected (cpu/jump/unaligned, recorded on
firmware 6.61). This was done only for the Media Engine.

The firmware's PS1 emulator returns through such an address. The
interpreters stopped there with an invalid jump, and the JITs found the
block at the aligned address only if it was already compiled, and
otherwise compiled one at the unaligned address.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 14:23:39 +02:00
Henrik RydgårdandClaude Opus 5.5 8e146f4c24 Android: Keep folder and shortcut grants, re-take the folders we use
Pruning now only runs when the config was really read: when the memory
stick isn't reachable at startup (an unmounted SD card, or right after
boot), NativeInit falls back to defaults and the recent list is empty, so
every recent game's grant looked unused and was released.

Grants for the memory stick, the Games tab folder and pinned folders are
re-taken at every startup. That moves their persisted time up, so they're
the last the system drops if we ever reach its limit. The new time is
written out with the next grant change, which is also the only time the
system prunes.

Home screen shortcuts can't be enumerated, so the 64 most recently
created or launched are remembered in a new [Shortcuts] section of
ppsspp.ini, and their grants are kept. ShortcutActivity runs without the
config loaded, so it queues them in SharedPreferences until the next
startup saves them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 13:54:31 +02:00
Henrik RydgårdandClaude Opus 5.5 020e39baa9 RecentFiles: Make the loaded list visible right away
Load() only queued the list for the worker thread, so reading it right
after loading the config could still see an empty list. Android's grant
pruning does exactly that, and would then treat recent games as
unreferenced, release their grants, and the queued clean would drop them
from the list as missing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 13:54:31 +02: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 a1ade54aac UMD Video: Boot the XMB for a video disc however it's started
The game list already did, but booting a video disc any other way (the command line, a file
association, dropping it on a running game, headless) tried it as a game and failed. The loader now
swaps in vshmain.prx with the disc mounted and auto-start on whenever it identifies a UMD Video
disc and the installed firmware can play it. GetUmdVideoVSHPath() moves from the UI to
Core/Util/PSARUnpack.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 12:08:10 +02:00
Henrik RydgårdandClaude Opus 5.5 5bd94f9fa3 Recents: Add UMD Video discs played in the XMB
Picking one boots vshmain.prx with the disc mounted, and only the file booted went into Recents
(minus vshmain.prx itself), so the disc never did.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 11:42:58 +02:00
Henrik RydgårdandClaude Opus 5.5 69ef4ce9fb UMD Video: Say which firmware is needed, instead of that it isn't supported
Without firmware that can play UMD Video (2.71 or later), the info screen and the boot error said
"PPSSPP doesn't support UMD Video", which is no longer true. They now say the firmware version
needed, from UMD_VIDEO_MIN_FIRMWARE, the same constant the check uses.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 11:36:50 +02:00
Henrik RydgårdandClaude Opus 5.5 6597483755 UI: No game-or-video prompt for a disc whose game is a placeholder
The EUR Video Demo Disc's UMD_DATA.BIN says GVA, but its EBOOT.BIN and BOOT.BIN are all zeroes, so
"Play game" could only fail. It now goes straight to the XMB, like "Watch video". What the disc
reports to the emulated PSP doesn't change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 11:24:55 +02:00
Henrik RydgårdandClaude Opus 5.5 dc41268bfc Firmware: Ignore empty updaters on discs
UMD Video discs ship an updater PARAM.SFO next to a 0-byte DATA.BIN, and PSN releases zero-fill it.
MountedDiscHasUpdater() only checked that DATA.BIN existed, so booting the EUR Video Demo Disc's
game with no firmware installed started an auto-install of "2.00" that failed with an OSD error.
It now wants a PSAR archive, like ReadBundledUpdateInfo() already did. The game info screen no
longer looks for an updater on UMD Video discs at all.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 10:55:27 +02:00
Henrik RydgårdandClaude Opus 5.5 d331bd6c8c UI: Play UMD Video discs in the XMB, straight from the game list
With firmware 2.71 or later installed, picking a UMD Video disc boots the XMB with the disc in the
drive and UMD auto-start forced on for that boot, so the disc's menu comes up. Without one, the
info screen saying UMD Video isn't supported shows as before. The info screen's Play does the same.

A disc with both a game and video asks which to start. For the video, the XMB comes up with
auto-start forced off, since auto-start runs the game.

Older firmware can't play these: 1.50, 2.00 and 2.60 crash in video_plugin or paf with a video disc
in the drive, and 2.50 refuses it. 2.71 through 6.61 reach Bourne Supremacy's menu.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 10:46:59 +02:00
Henrik RydgårdandClaude Opus 5.5 bf1f88d6e6 UMD: Tell video discs from game discs by UMD_DATA.BIN, and fix eject+insert while paused
- Fixes #22737

Some video discs (Spider-Man) have a PSP_GAME holding only a firmware update. We reported them as
game discs too, and the XMB said "The disc could not be read". The type now comes from the fourth
field of UMD_DATA.BIN (G, V, A; the EUR Video Demo Disc says GVA), with the directories as a
fallback, and PSP_GAME without SYSDIR/EBOOT.BIN doesn't make a mixed disc a game. Identify_File uses
the same check, so these discs list as UMD Video.

Ejecting and inserting a disc from the pause menu without resuming in between left the old disc in
the XMB: callback notifications don't queue, so the insert's argument replaced the eject's before
the drive callback ran. The insert now waits until the callback has seen the drive empty.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-23 10:46:59 +02:00
Henrik RydgårdandClaude Opus 5.5 ba55ee7e5f Screenshots: Take the game image while the game is on a TV
An output screenshot reads back the device's own screen, which only has
the touch controls on it then.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 23:07:10 +02:00
Henrik RydgårdandClaude Opus 5.5 04ae3fea5b External display setting: Show the game, or mirror the screen
Graphics > "External display" on iOS and Android. "Show the game" (the
default) is the TV mode, "Mirror the screen" leaves the TV to the system.
A change applies right away. It's disabled on backends that can't draw to
a second swapchain.

On Android 12 and up, a second screen that's part of the device (like a
dual-screen handheld's) isn't used, so the game doesn't move there by
itself.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 22:31:07 +02:00
Henrik Rydgård f6fa84329e Merge pull request #22736 from hrydgard/avc-spliced-stream-timing
AvcDecoder: Keep the output clock going when a new stream is spliced on
2026-10-22 22:14:54 +02:00
Henrik RydgårdandClaude Opus 5.5 ddfd3bcfb9 CmdLine: Take integer parameters in hex too, and refuse trailing junk
They were read with sscanf("%d"), which stops at the first character it
can't use, so --disable-hle=0x110 was accepted as 0 and quietly ran
everything on our HLE. Now decimal or 0x hex, optionally signed, and the
whole value has to parse. Up to 0xFFFFFFFF is taken, for full masks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:47:39 +02:00
Henrik RydgårdandClaude Opus 5.5 6c8c71a125 AvcDecoder: Keep the output clock going when a new stream is spliced on
Burnout Legends feeds its intro movies to one decoder back to back, with
no flush in between. Each new stream opens with a buffering period whose
cpb_removal_delay is 0, which put our reconstructed clock 18 frames back,
so every picture after it was held until the clock caught up, and then
20 came out of one decode. sceMpegAvcDecodeYCbCr converts every picture
a decode reports into the caller's buffer list, which only has room for
one, so mpeg.prx went on through stack garbage and crashed as the title
screen came up.

Such a buffering period now continues from the last access unit instead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 21:02:36 +02:00
Henrik RydgårdandClaude Opus 5.5 5009724d91 Windows: --game-window, on Vulkan
A second Win32 window, added as a Game output like the SDL one. Keys
typed into it still go to the emulator; the mouse doesn't. It's for
testing multiple outputs (standing in for a TV over AirPlay) until real
multi-window support takes it over.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 20:41:35 +02:00
Henrik Rydgård 47e67cb122 Merge pull request #22728 from hrydgard/multi-swapchain-3
Multi-swapchain (multi-window rendering) support, for Metal
2026-10-22 20:09:32 +02:00
Henrik Rydgård ba26373cb1 Merge pull request #22731 from hrydgard/zuma-analog
Zuma: Correct aiming algorithm to behave better with analog sticks
2026-10-22 20:03:04 +02:00
Henrik RydgårdandClaude Opus 5.5 b3c44cba8c Zuma: Correct aiming algorithm to behave better with analog sticks
Zuma is a pretty bad port from PC and has really weird aiming math,
clearly partially derived from the mouse aiming code.

- Each stick axis got its own dead zone (7864 of 32767), so the angle
  bent towards the nearest axis, and at half deflection anything within
  about 15 degrees of an axis was on it.
- The aim puts a cursor 450 pixels out along the stick and points the
  frog at it, but only moves a cursor coordinate when that axis is
  non-zero. With an axis in its dead zone, or the stick straight along
  the other one, the frog pointed at wherever the cursor had last been:
  full right after a diagonal aimed 27 degrees off.
- The axes are read as 127 minus the PSP's value, so a stick at rest
  reads -1, which the old dead zone hid.

Two hooks: one gives the left stick a circular dead zone of the same
size, centred on 128, once the game has stored its floats; the other
moves a cursor coordinate that wasn't updated to the frog's own.
Reading the angle the game sets for 24 directions at full, 50% and 45%
deflection, it now matches the stick's to within 0.6 degrees, the
stick's 8-bit steps. Before, it was off by up to 27 degrees.

The aim still ignores the stick below about 43% of its travel: the
game's own minimum length on top of the dead zone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 19:31:54 +02:00
Henrik RydgårdandClaude Opus 5.5 d491fc90cf Secondary outputs, and --game-window on Mac to show the game in a second window
The ScreenManager keeps a list of secondary outputs, each with its own
swapchain, recreated after a lost device. Platform code adds, resizes and
removes them from any thread through Native_AddOutput() and friends, which
take effect at the start of the next frame.

Each frame, every output is drawn by the topmost screen that claims it
(Screen::renderOutput). EmuScreen claims Game outputs: it presents the
game there, laid out for that output, and leaves it out of the backbuffer,
where the touch controls and the menus stay. Not in non-buffered mode,
which draws straight into the backbuffer.

--game-window opens such an output on Mac, with Metal. It's a smoke test
for multiple swapchains, and a stand-in for an AirPlay display.

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 a2d18d21cd iOS: Make Metal the default graphics backend
Not on tvOS, where the Metal backend hasn't been run yet. Existing
installs keep the backend saved in their ini.

The multi-swapchain plan now does Metal before Vulkan to match.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 18:42:53 +02:00
Henrik Rydgård 679d804c26 Merge pull request #22730 from hrydgard/config-constexpr-parentptr
Config: Alternative speeds in percent, constexpr setting tables, view parent pointers
2026-10-22 18:41:48 +02:00
Henrik RydgårdandClaude Opus 5.5 44b5dc1b95 Config: Default AutoFrameSkip to false
Its default called IsVREnabled() during static initialization, before VR is
set up, so it was always false in practice.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 18:00:17 +02:00
Henrik RydgårdandClaude Opus 5.5 729047d449 Config: Make the setting tables constexpr
SETTING now takes offsets with offsetof and passes them along with the
setting's type, instead of the block and member pointers, and the
ConfigSetting constructors are constexpr. So the tables are built at compile
time, and later per-setting metadata can be checked by the compiler too.

Defaults that weren't constants had to change:
- AutoFrameSkip called IsVREnabled() during static initialization, before VR
  is set up. It's now a default callback, evaluated when the config is
  loaded or reset, which can turn it on by default on VR devices.
- DisplayRefreshRate read g_Config's own initial value, which is 60.

Path defaults now share the string member of the default union.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 15:34:47 +02:00
Henrik RydgårdandClaude Opus 5.5 71b38e58e6 Config: Store the alternative speeds in percent
The settings screen showed them in percent but they were stored as frame
rates out of 60, so the screen kept converted copies and wrote them back. Now
they're stored in percent under new ini keys (AltSpeedPercent,
AltSpeedPercent2, AnalogAltSpeedPercent), migrated from the old frame rate
keys like the audio volumes were, and converted to a frame rate where the
timing needs one.

Low percentages no longer round to a 0 fps limit, which meant unlimited.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 15:28:18 +02:00
LunaMooandClaude Opus 5.5 3ce3973781 Add overlay (bezel) support
Draws a user image around or behind the PSP screen, to fill the black bars
on displays whose aspect ratio doesn't match the PSP's (#20325).

- Overlays are PNGs in PSP/OVERLAYS. A plain PNG there just works: it's
  drawn in front of the game, and its transparent hole is found by walking
  from the image center to each edge until a nearly opaque pixel. Existing
  RetroArch overlay images can be dropped in as-is.
- A subfolder with an overlay.ini can define overlays with a Name, Layer
  (Foreground/Background) and an explicit Hole=x,y,w,h.
- "Load overlay image..." in the display layout screen imports any PNG
  (including Android content URIs) into the overlays folder and selects it.
- The overlay is fit to the screen (aspect preserved, centered). The hole is
  mapped with the same scale, and the game is fit into it with the normal
  stretch / integer scale / aspect ratio options. Offset and scale don't
  apply while an overlay with a hole is active. This goes through
  CalculateDisplayOutputRect, so touch mapping and post shaders agree.
- The selection is a per-orientation, per-game DisplayLayout setting.
- Disabled in VR, Cardboard, and with skip buffer effects (except software
  rendering), since the presentation path doesn't run there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QGBg32EFPsTXjAFu7dnTcz
2026-10-22 14:09:51 +02:00
Henrik Rydgård 35180fb0bc Merge pull request #22726 from hrydgard/webserver-shutdown-no-hang
WebServer: Don't make shutdown wait for a stuck server registration
2026-10-22 12:22:45 +02:00
Henrik Rydgård bf3d9e7388 Merge pull request #22724 from hrydgard/vsh-kernel-fpl
HLE: Resolve the kernel sceKernelAllocateFpl
2026-10-22 12:09:10 +02:00
Henrik RydgårdandClaude Opus 5.5 a1b4a93cac HLE: Resolve the kernel sceKernelAllocateFpl
It was left unresolved with the kernel mutex calls, as they all let the
flash drivers reach the NAND controller during a VSH boot. It's the
mutexes that do that: with only the FPL resolved, sceIdStorage_Service
allocates from its pool and every firmware's VSH in 1.50-6.61 boots to
the same XMB picture as without it. Resolving the mutexes still hangs on
0xbd101300.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 11:24:14 +02:00
Henrik RydgårdandClaude Opus 5.5 a79999e715 WebServer: Don't make shutdown wait for a stuck server registration
Registering with report.ppsspp.org starts with a DNS lookup that can't be
cancelled, and it ran on the server thread, so ShutdownWebServer() from
onDestroy waited for it on the main thread (an ANR on Play). It now runs on its
own thread, which the server leaves behind if it's still in the lookup.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 11:22:04 +02:00
Henrik RydgårdandClaude Opus 5.5 37be6ce4e8 Config: Clamp the touch layout snap grid size on load
The layout editor divides by it, and drawing the grid never finishes when
it's 0. Only the slider limited it, so a hand-edited ini could hang.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 10:55:31 +02:00
Henrik Rydgård 1adf3c553d Merge pull request #22549 from Kethen/aemu_postoffice_enet
UDP(ENet) mode for adhocctl & aemu_postoffice relay
2026-10-21 23:51:28 +02:00
Henrik RydgårdandClaude Opus 5.5 1fe87ec263 Syphon Filter: Logan's Shadow: Fix the explosion smoke crashing at 60fps
The game shares Resistance: Retribution's effect engine, smoke trails and all: a trail gets a
new node on every update while the nodes expire by the effect's clock, so the faster the game
runs the more smoke there is. When the tank explodes, the smoke grows the display list past its
500KB buffer at a steady 60fps (an overclocked CPU gets there), the list runs into the heap and
the game crashes on a bone array it overwrote. Hook the same two functions as in Resistance:
nodes at most every 1/30 of the effect's clock, and the overflow guard as a backstop. At 428MHz
the list now peaks at 351KB, against 512KB and a crash before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 23:11:23 +02:00
Henrik Rydgård 03457d559b Merge pull request #22716 from hrydgard/vsh-mount-iso
Allow --mount in the app, and make VSH UMD autoboot a setting
2026-10-21 20:57:42 +02:00
Henrik RydgårdandClaude Opus 5.5 5a8e865532 Config: Default VshUmdAutoboot to off
Booting the VSH with a disc now stays in the XMB, with the disc in the
Game column, rather than starting the game right away.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 19:57:01 +02:00
Henrik RydgårdandClaude Opus 5.5 2d116a5b76 sceReg: Let a key read a bool setting, use it for VSH UMD autoboot
KeyValue gets an optional pointer to a g_Config bool, which the key
reads as 0/1. The setting wins over guest writes: since category_version
is kept low, the VSH upgrades the registry at every boot and rewrites
every key with its default.

/CONFIG/SYSTEM/umd_autoboot reads the new SystemParam VshUmdAutoboot
(default true, as before). Turned off, --vsh --mount stays in the XMB
with the disc in the Game column instead of starting it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 19:52:38 +02:00