Commit Graph
49156 Commits
Author SHA1 Message Date
RulariaandClaude Opus 5.5 ed347bf0e4 ReplaceTables: Fix Amagoushi no Yakata 3D investigation hit-test readback
- Fixes #22514

The 3D investigation mode renders an ID picking pass into VRAM and reads
it back on the CPU to build the hit-test table. Hook the decode function
to flush the framebuffers to VRAM first, and force lower resolution for
effects to avoid stutter.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 15:15:59 +02:00
Henrik Rydgård 370a9ca963 Merge pull request #22627 from hrydgard/windows-exit-hangs
Windows: Fix hangs on exit (HID pads, web server), and log the exit waits
2026-10-17 14:48:43 +02:00
Henrik Rydgård 9673f63b21 Merge pull request #22626 from hrydgard/ui-fixes
UI: Recent tab toolbar, and Escape from Settings back to the pause menu
2026-10-17 14:06:38 +02:00
Henrik Rydgård f29d72cea8 Merge pull request #22625 from hrydgard/ios-frametests
Fix two frame dump batch problems found by frametests --ios
2026-10-17 13:57:00 +02:00
Henrik RydgårdandClaude Opus 5.5 37591a2401 Web server: Don't hang shutdown when it lands during startup
StopWebServer() only stopped a RUNNING server, while ShutdownWebServer()
joined in any state but STOPPED. Shutting down while the server was
still STARTING joined a thread nobody had told to stop, which then
entered its loop and never left. Stop it while STARTING too, and only
switch to RUNNING if no stop came in meanwhile.

Also make the report.ppsspp.org registration cancellable. It runs at
startup and every nine minutes, and couldn't be interrupted, so exiting
during a stalled one waited out its connect and 900s header timeouts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 13:53:52 +02:00
Henrik RydgårdandClaude Opus 5.5 f959acd680 Log the waits on exit
So a log from a process that never exits shows which step it hung in.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 13:51:56 +02:00
Henrik RydgårdandClaude Opus 5.5 2968debfe7 Windows: Don't hang on exit when a HID pad stops sending reports
DualShock, DualSense and Switch Pro pads are read with a blocking
ReadFile on the input thread, and WM_DESTROY joins that thread. If the
pad stops sending reports the read never returns, so the window is gone
but the process stays. Cancel the thread's I/O until it stops, retrying
in case a cancel lands just before a read starts. This also covers the
synchronous writes the pads' shutdown does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 13:51:56 +02:00
Henrik RydgårdandClaude Opus 5.5 64ae00da91 Darwin: Keep the view reservation intact while releasing views
MemoryMap_Shutdown deallocated each view, opening holes in the reserved
range, and then released the whole range. A mapping another thread got in
a hole meanwhile was unmapped along with it. On iOS that was Metal's
command buffer memory: headless crashed in vkQueueSubmit about a hundred
frame dumps into a launch. Map inaccessible pages over a released view
instead, so the range stays ours until ReleaseViewSpace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:33:45 +02:00
Henrik RydgårdandClaude Opus 5.5 f0f0b11576 GPU: Reset the flip count when creating a GPU
Framebuffer ages are measured in flips, and the count carried over from
the previous game. Headless replaying several frame dumps in one process
(frametests.py --ios does, 100 per launch) then rendered a dump
differently from replaying it alone: Colin McRae Rally lost its car and
trees whenever any dump came before it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:33:45 +02:00
Henrik Rydgård c21f3521e2 Merge pull request #22624 from hrydgard/ios-frametests
iOS: Add support for running PPSSPPHeadless on iOS, add support in the frametests.py script
2026-10-17 12:53:14 +02:00
Henrik Rydgård 1461c929e3 Merge pull request #22623 from hrydgard/tekken-movie-fps
GPU: Use the width inside the scissor to compute the timing of video blits
2026-10-17 12:52:47 +02:00
Henrik RydgårdandClaude Opus 5.5 ea1ea14042 Pause screen: Ignore the pause key while another screen is on top
- Fixes #22622

Escape is both the UI back key and the default pause key. The pause
screen listens to the control mapper, which UI screens pass keyboard
input to, so Escape in Settings closed Settings and the pause screen
under it at once, dropping straight back into the game. EmuScreen
already checks IsOnTop() for the same reason.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 12:36:03 +02:00
Henrik RydgårdandClaude Opus 5.5 57601a9d3b Game browser: Give Recent the toolbar, and the large list by default
Recent gets the view mode and grid settings buttons. It has no path,
nothing to navigate, and its list comes from memory, so the path,
navigation and refresh stay out. Its old grid setting is no longer
migrated, like Games and Homebrew.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 12:32:42 +02:00
Henrik RydgårdandClaude Opus 5.5 1b475f219c GPU: Cite blittiming's scissor-strip cases for the clipped blit width
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 12:11:21 +02:00
Henrik RydgårdandClaude Opus 5.5 a69ca3b218 GPU: Judge the blit texture cache by the width inside the scissor
Tekken 6 draws its intro movie as one full-width sprite eight times, each
scissored to a 64-pixel strip. The fill cost model picked the thrashing
rate from the sprite's full 480-pixel width, charging ~51ms per frame, so
the game's sceGeDrawSync held it to 15fps. It plays at 30fps on a PSP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 12:04:05 +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årdandClaude Opus 5.5 ab587c98e8 FileUtil: Make CreateFullPath work with relative paths
GetRootVolume() returns a relative path unchanged, so the path to create
came out empty: nothing was created, and it still returned true.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 11:11:31 +02:00
Henrik Rydgård bd3df9de3c Merge pull request #22621 from mariobob/ccc-stop-at-invalid-input
sceCcc: Stop string conversions at invalid input
2026-10-17 10:03:45 +02:00
Henrik Rydgård e03cd3ef32 Merge pull request #22619 from hrydgard/vertex-depth-snap
GE: Make hardware and software transform depth agree more often, and keep more near/far-plane draws on the GPU
2026-10-17 09:42:01 +02:00
Henrik Rydgård 5f2d4afbdc Merge pull request #22617 from hrydgard/medievil-fire
Fix the Medievil Dragon Armor fire at 60hz
2026-10-17 00:16:14 +02:00
Henrik RydgårdandClaude Opus 5.5 bf2a9c7529 MediEvil Resurrection: Fix the dragon armour's fire breath at 60fps
- Fixes #20296 (by hacking the game to fix the bug with a replacement func)

A known game bug, on hardware too: in areas light enough to run at 60fps,
holding Square with the dragon armour throws no fire. Each flame looks up a
node in its model at init, and dies on its first update when it isn't there.
The node list comes from a resource that's freed when no flame holds it, and
at 60fps every flame dies before the next spawns, so it is reloaded each time
and never ready at init. At 30fps the flames overlap and keep it loaded.

Hook the update where it checks for the node, and look it up again there,
when the list has been filled.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 00:00:14 +02:00
Henrik RydgårdandClaude Opus 5.5 7e74ea90d8 CmdLine: Add --cpu-mhz to lock the emulated CPU clock
Overrides the locked CPU speed setting without saving it, so a headless run
can reproduce a game at a different frame rate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 00:00:01 +02:00
Henrik RydgårdandClaude Opus 5.5 59fe760b16 UnitTest: Write test files to the temp directory, and close the cheat file before deleting it
The cheat tests left their ini files in the working directory: on Windows the
delete failed while CheatFileParser still held the file open.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 00:00:00 +02:00
Henrik RydgårdandClaude Opus 5.5 0e7a48907b scePower: Keep the game's own CPU clock, and apply locked clock changes live
The game's requested CPU frequency wasn't stored anywhere, only the effective
CoreTiming clock. So a state saved with a locked clock kept that speed when
loaded with the lock off, and turning the lock off in-game left the CPU at
the locked speed until the game set its clock again (most only do at boot).
Changing the lock in-game didn't take effect either, until a reboot or a
state load.

Now RealcpuFreq is tracked and saved next to the PLL and bus, the setters
record the game's request even under a lock, and one function applies
either the lock or the game's clocks. It runs at init, on state load, and at
vblank whenever the setting has changed. Older states guess the game's clock
from the saved effective one, capped at the game's PLL.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 00:00:00 +02:00
Henrik Rydgård 7867da4047 Merge pull request #22618 from hrydgard/gtavcs-white-lies
GTA: Vice City Stories: Set the gas pedal on planes, so the hovercraft doesn't stick
2026-10-16 23:59:10 +02:00
Henrik RydgårdandClaude Opus 5.5 325fa0d8ea GTA: Vice City Stories: Set the gas pedal on planes, so the hovercraft doesn't stick
- Fixes #13200

The game stops any vehicle moving less than 0.005 a frame with its gas
pedal up, so parked vehicles don't drift. Planes, the Bovver '64 hovercraft
among them, never set the pedal, so at a high enough frame rate they stick
to the ground. That happens on a PSP too, in light scenes.

Hook the plane control code where it clears the pedal and set it from the
throttle instead, as Parik27 worked out in the issue. The function is the
same in the US and EU versions.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 23:36:00 +02:00
Henrik Rydgård 2a24d19ff8 Merge pull request #22614 from hrydgard/android17-local-network
Android 17: Local network permission, upload button, large-screen orientation
2026-10-16 22:46:55 +02:00
Henrik RydgårdandClaude Opus 5.5 b514cf628b Keep draws on the GPU when the min/max clip planes are the viewport's Z planes
A draw with vertices beyond the near or far plane went through the software
transform unless the GPU had both clip and cull distances. When the viewport's
near and far planes both fall on min/max Z, and those are inside 0..65535,
the min/max clip planes clip exactly where the GE does, and also take away
what's beyond the far plane: no need for cull distances or the CPU.

On Mac Vulkan, Metal Gear Solid: Peace Walker's six dumps go from 15.3k px
off the PSP to 10.4k, and Call of Duty: Roads to Victory from 10.0k to 8.9k.

viewportNearPlaneMatchesOutput was never set back to false.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 22:36:11 +02:00
Henrik RydgårdandClaude Opus 5.5 0f0b610a1a Vertex shader: Snap screen Z and W so hardware and software transform agree
Hardware transform does the matrices on the GPU, software transform on the CPU,
and their depths differ by a few ulps on 40-90% of pixels. That's enough to
fail an equal-depth GEQUAL test where a frame mixes the two, which happens to
draws that cross the near plane on GPUs without cull distances: DBZ Tenkaichi
Tag Team's outlines, Naruto Ultimate Ninja Impact's grass over the ground.

Both paths end in the same vertex shader code, so snapping there makes them
agree: screen Z to 1/8 (whole units break a decal in Star Wars Lethal
Alliance) and W to 16 significant bits. W is left alone without integer ops.

On the framedump set with Mac Vulkan: DBZ 3198 -> 77 px off the PSP, Naruto
3243 -> 906, Monster Hunter 792 -> 21, FF Type-0 559 -> 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 22:36:11 +02:00
Henrik RydgårdandClaude Opus 5.5 d90ee09144 libretro: Add the permission stubs
The ad hoc code now asks for the local network permission, so Core needs them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 22:11:39 +02:00
mariobob 8945f6fad4 sceCcc: Stop string conversions at invalid input
When the input starts with something it can't decode, each of the six
string converters returns 0 on the PSP and writes only the terminator
(ccc/convertstring, all six "Invalid data" cases). We decoded on
regardless and wrote replacement or garbage characters for every unit.

Stop at the first invalid character instead: for UTF-8, a byte that can't
start a sequence (UTF8::invalid()); for UTF-16 and Shift-JIS, INVALID from
the decoder. Valid characters the tables can't map still get the error
character, as before.

ccc/convertstring passes now and moves to tests_good.
2026-10-16 22:05:12 +02:00
mariobob a42ed5e312 Encoding: Report bad UTF-16 surrogates and Shift-JIS lead bytes as invalid
UTF16_Type::next() assumed every surrogate came in a valid pair, so a lone
high surrogate swallowed the unit after it, and one right before the
terminator stepped over it and kept reading. It now returns INVALID and
consumes only the bad unit, which sceCccDecodeUTF16() already checks for.

ShiftJIS::next() returned 0xA0 and 0xFD-0xFF as single-byte characters.
Like 0x80, which it already rejected, they encode nothing (the PSP's JIS
to UCS table in pspautotests/tests/ccc/jis2ucs.bin has no entry for any of
them), so they're INVALID too.

Only sceCcc uses these decoders.
2026-10-16 22:05:12 +02:00
Henrik RydgårdandClaude Opus 5.5 beaae7016f Android: Keep the orientation setting on large Android 17 screens
Android 17 ignores fixed orientations on sw600dp+ displays, but exempts
games, and we declare appCategory="game". Tested on an Android 17 tablet
emulator: with the category, Portrait is honored (letterboxed); without it,
the system logs "Ignoring requested fixed orientation". So stop hiding the
rotation setting there, and drop the smallestScreenWidthDp plumbing that
only existed for that check.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 20:44:07 +02:00
Henrik RydgårdandClaude Opus 5.5 7163bd4b9e Translate the nearby players permission string
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 20:44:06 +02:00
Henrik RydgårdandClaude Opus 5.5 19e4b77be4 Android: Ask for the local network permission for ad hoc, UPnP and servers
Part of #21585

Android 17 blocks LAN traffic in both directions (TCP and UDP, including
broadcast and multicast) without ACCESS_LOCAL_NETWORK. Ask for it when
networking, the built-in ad hoc server, UPnP, share-on-startup or the
remote debugger gets turned on, when a game initializes ad hoc with
networking enabled, and at startup if a server is set to start then.

A denied ad hoc send fails with EPERM, which the games never see, so show
a message for it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 20:44:05 +02:00
Henrik RydgårdandClaude Opus 5.5 fd6e4dfccd Disable the upload button until a game folder is chosen
On a fresh install with scoped storage the Games tab has no folder, and the
upload server had nowhere to put files, so every request got a 404.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 20:44:05 +02:00
Henrik RydgårdandClaude Opus 5.5 41bf0dec01 Translate the local network permission strings
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 20:44:05 +02:00
Henrik RydgårdandClaude Opus 5.5 d2e9e87242 Android: Ask for the local network permission before starting file upload
Part of #21585

Android 17 apps need ACCESS_LOCAL_NETWORK to serve other devices on the LAN.
The upload screen now asks for it when it opens, starts the web server once
it's granted, and shows a warning and a retry button if it's denied.

The permission only exists from API 37, so check for that rather than 35.
On Android 15 and 16 it reads as denied, which left Remote ISO's connect tab
stuck behind a permission request that could never succeed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 20:44:04 +02:00
Henrik Rydgård 62b2eaa3fd Merge pull request #22613 from hrydgard/sw-transform-noclip-range
Software transform: Without depth clip, cull by the screen Z range
2026-10-16 20:41:33 +02:00
Henrik Rydgård a181ded189 Merge pull request #22612 from hrydgard/misc-fixes
Assorted fixes picked from various unmerged branches
2026-10-16 20:08:13 +02:00
Henrik RydgårdandClaude Opus 5.5 b23f5033b7 Software transform: Without depth clip, cull by the screen Z range
With depth clip off, the software transform dropped a triangle when any vertex
was beyond the near or far plane, which is the viewport's own Z range. The GE
only drops it when a vertex's screen Z is outside 0..65535 or it's behind the
camera, as the software renderer does. Yu-Gi-Oh GX Tag Force's backdrop has a
10000..50000 viewport and vertices past it: with hardware transform off, parts
of it were missing (4192 px off the PSP, now 64 like with hardware transform).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 20:08:13 +02:00
Henrik Rydgård 754dfe954c Merge pull request #22611 from chriopter/mic-wait-input-end
Mic: Implement sceAudioWaitInputEnd/PollInputEnd (follow-up to #22459)
2026-10-16 19:07:42 +02:00
chriopter cd0532b321 Mic: Implement sceAudioWaitInputEnd/PollInputEnd and the sceUsbMic equivalents
A non-blocking sceAudioInput keeps filling its buffer from the host capture
after it returns, but WaitInputEnd and PollInputEnd reported it done at once,
so a game that reads the buffer right after the wait (FTB2's voice chat) got
nothing. WaitInputEnd now blocks like the blocking input path, and
PollInputEnd returns 1 until the buffer is full. Both pad with silence after
the same 100 ms of host jitter, so a stalled device cannot hang the game.
2026-10-16 18:32:07 +02:00
Henrik Rydgård 17a68b0435 LoongArch64Emitter: range-check BZ fixups against the BZ range
The FixupBranch path for BEQZ/BNEZ used BranchInRange (16-bit offset, the B/BEQ
form) rather than BranchZeroInRange (21-bit), which is what the direct
BEQZ(rj, dst) overloads use. So a fixup branch further than 128KB but within the
2MB the instruction can actually encode asserted for no reason. Encoding itself
was already correct.
2026-10-16 18:16:53 +02:00
Henrik RydgårdandClaude Opus 5.5 6a458bf148 Debugger: Fix breakpoints swallowed or reported twice when resuming
The skip-first marker suppressed everything at the address you resume or
step from. That's needed for the pause, or you could never get off a
breakpoint, but it also dropped the log and hit count. So a log breakpoint
you'd stepped onto, or were parked on for some other reason, never logged
when execution moved on.

Now the skip only drops the pause, unless the breakpoint at that address is
the one that stopped us, which has already logged and counted this
execution. The check moves into the Exec* functions instead of being
repeated in the interpreter, IR interpreter and three JITs, where the
register breakpoint path only applied it to the pause and so logged twice
when stepping off a log+pause one. The marker gets an explicit valid flag,
since address 0 meant "none", and Core_Break() clears it.

Based on the earlier cleanup-skip-first branch, reduced to one marker plus
a flag. Checked over the WebSocket debugger on the interpreter, IR, x86 JIT
and JIT-IR: log breakpoints on a parked and a stepped-onto address each log
once, and stepping off a log+pause one doesn't report it again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 18:16:33 +02:00
Henrik Rydgård ab0fa898ae Fix the latent tail from the Common/Core review
Buffer::PeekAll resized the destination and then appended, so it returned twice the
data with zeros in front. No callers today.

The assert dialog's caption was a function-local static, so it froze at whichever
kind of assert fired first in the session - a later Critical Assert would still be
titled 'Debug Assert'. Made it a plain const char *.

containsNoCase passed a possibly-negative char to std::toupper, which is UB for
anything but EOF; non-ASCII bytes get here from ELF symbol names.

ApplyDeadzoneXY's circular path lacked the deadzone >= 0.99f guard its own sibling
ApplyDeadzoneAxis has one function above, so it divided by ~0.
2026-10-16 18:14:44 +02:00
Henrik Rydgård 49552b0881 Merge pull request #22608 from hrydgard/sdl-fribidi-hebrew
SDL text: Reorder mixed-direction text with FriBidi
2026-10-16 18:03:09 +02:00
Henrik Rydgård 137a327a96 Merge pull request #22609 from hrydgard/resize-stencil-ram-fb
Don't copy stencil out of a framebuffer that has none when resizing
2026-10-16 18:02:57 +02:00
Henrik Rydgård a0638c49cd Merge pull request #22590 from LunaMoo/PSPAR_support
Cheats: Support PSPAR (_M) codes
2026-10-16 17:50:11 +02:00
Henrik RydgårdandClaude Opus 5.5 074b66b49b thin3d: Add Framebuffer::HasDepthStencil, check it before depth/stencil copies
ResizeFramebufFBO now asks the framebuffer whether it has a depth/stencil
buffer instead of guessing from the z address and stride.

Vulkan CopyFramebuffer gets the debug asserts and bail-out BlitFramebuffer
already had, and GL copies and blits get the same, so a copy out of a
framebuffer without depth/stencil asserts where it's queued instead of in
the queue runner.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 17:44:09 +02:00