Commit Graph
15815 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 b9b1a29c42 Read CLUT8 from 16-bit framebuffers in the shader, replacing the SOCOM hack
The shader depal CLUT8 mode now handles 565, 5551 and 4444 framebuffers
(the format goes in the depal format bits, which this mode didn't fill
before), rebuilding the 16-bit pixel and taking its low or high byte.
This replaces SOCOMClut8Replacement, which assumed the high byte.

- The GE samples a pixel at its corner, the GPU at its center. With u
  stepping two bytes per pixel (SOCOM and Bend's games use u = 1 + 2x)
  that's a whole byte, so the shader steps back to the corner.
- The CLUT index shift, mask and start are applied, as in the other
  depal modes.
- The self-render copy region counted CLUT8's U in pixels, not bytes.

SOCOM, Star Wars: The Force Unleashed and the NPJG90068 dumps improve a
lot. Syphon Filter and Resistance: Retribution now read the right bytes,
which exposes a 16-bit precision difference in their bright pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 01:04:23 +02:00
Henrik Rydgård 0ac523c4b5 Merge pull request #22475 from hrydgard/cars-brightness
Tantalus games: Fix brightness, fix vertical stripes
2026-10-09 19:22:00 +02:00
Henrik Rydgård 4464ef4efd Merge pull request #22432 from sum2012/patch4
Add some kernel NIDs and sceReg write overlay by Zcode
2026-10-09 18:57:39 +02:00
Henrik Rydgård 324e6c3954 Merge pull request #22476 from hrydgard/community-security-fixes
Two security fixes by acts-1631, and more found by Claude
2026-10-09 18:55:57 +02:00
Henrik Rydgård b0ea555b79 Fix a couple of problems with recent commits in MSVC debug builds 2026-10-09 17:52:55 +02:00
Henrik RydgårdandClaude Opus 5.5 a9b18a9192 Compat: Read back Cars Race-O-Rama's luminance reduction for its brightness adaptation
The game reduces the scene to 8x8 on the GPU, block transfers that into RAM,
and the CPU sets its brightness adaptation from it. With BlockTransferAllowCreateFB
the copy went into a GPU-side buffer instead, the CPU read stale RAM, and the
game kept getting darker on the hardware backends (#15914).

New compat flag BlockTransferReadbackSmall: transfers of up to 64 pixels into
RAM are still read back, a frame late, so it doesn't stall. Brightness from
the issue's savestate now follows the software renderer's (72 vs 84 at 6 s,
84 vs 95 at 20 s, from 25 and falling), at the same speed as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 17:01:37 +02:00
Henrik RydgårdandClaude Opus 5.5 a521f257ce CmdLine: Add --msaa
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 15:49:15 +02:00
Henrik Rydgård 06fbbf1218 Merge pull request #22471 from hrydgard/parallel-loop-max-threads
Allow specifying a cap on the number of threads used in ParallelLoop
2026-10-09 15:38:09 +02:00
Henrik RydgårdandClaude Opus 5.5 6e0d6f4357 Savestates: Terminate fixed-size name strings on load
Add DoCString() for standalone fixed-size strings, and
TerminateLoadedCString() for names inside structs that are loaded whole.
These all get used as C strings (GetName(), logs, the debugger), and are
always terminated when created. Module names are copied from the ELF with
strncpy, so terminate those on creation too.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 15:32:06 +02:00
Henrik RydgårdandClaude Opus 5.5 9372d4b490 sceIo: Validate PGD block sizes and dir listing state
The PGD block size comes from the file header (or a savestate), and is
used to size the block buffer and as a divisor and mask, so require a sane
power of two. On load, force the next read to fill the buffer, which isn't
saved. sceIoDread only checks the listing index against the end.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 15:32:06 +02:00
Henrik RydgårdandClaude Opus 5.5 864778a11b FileSystems: Reject parent directory references in savestate file names
DirectoryFileSystem reopens the open files' guest paths on load, with
their original access mode, so a crafted state could create or truncate
host files outside the memstick or game folder. VirtualDiscFileSystem had
the same problem for reads. Guest paths never contain ".." here, the meta
file system resolves them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 15:32:06 +02:00
Henrik RydgårdandClaude Opus 5.5 1d18d182e1 MemSlabMap: Validate slab ranges from savestates
FillHeads() trusts start/end, so a bad slab wrote out of bounds of heads_.
On failure, reset the map since heads_ may point to the deleted old slabs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 15:32:05 +02:00
Henrik RydgårdandClaude Opus 5.5 d7a577044f Sas: Validate voice positions and pitch from savestates
Out-of-range PCM positions made ReadSamples() compute a huge size and write
past the output, and sampleFrac/pitch/curSample index fixed buffers.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 15:32:05 +02:00
Henrik RydgårdandClaude Opus 5.5 0bcf7c8e2b Ctrl: Validate the sample buffer indices from savestates
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 15:32:05 +02:00
Henrik RydgårdandClaude Opus 5.5 65fdadf5dd GE: Validate display list stack pointers and pending interrupt list IDs on load
CALL/RET only check stackptr against exactly 32 and 0, and getList() doesn't
check the ID at all.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 15:32:05 +02:00
Henrik RydgårdandClaude Opus 5.5 8e6f8de19b Kernel: Validate thread, MipsCall and interrupt indices from savestates
waitType, thread priorities, MipsCall::numArgs, action type IDs and pending
interrupt numbers all index host arrays (some of them function pointer
tables) without further checks.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 15:32:05 +02:00
Henrik Rydgård 0810fee60f Merge pull request #22469 from hrydgard/virtkey-create-frame-dump
Add bindable key to create frame dumps, allow headless to save high-res screenshots
2026-10-09 14:49:12 +02:00
Henrik Rydgård 831acb099e Allow specifying a cap on the number of threads used in ParallelLoop 2026-10-09 14:33:58 +02:00
Acts1631 b0e8b20acd Validate MPEG demux savestate bounds
A savestate could replace the demux buffer length after the buffer was
allocated from the media engine ringbuffer size. The saved length then
controlled a copy into the smaller allocation.

Deserialize into temporary values and reject lengths or cursors that do
not fit the constructor-established buffer before copying any data.
2026-10-09 14:04:34 +02:00
Acts1631 62ee0879aa Validate thread queue savestate geometry
A savestate could specify a queue size larger than its separately
serialized capacity. Restoring the queue then calculated a negative
starting index and copied attacker-controlled IDs before the allocation.

Validate the logical size, capacity, and available serialized bytes
before allocating or restoring a queue.
2026-10-09 14:04:34 +02:00
Henrik Rydgård 9948c57e13 Make MIPSState standard-layout again, so offsetof is valid
Turns out that the actual problem here was "private", not the vector.
2026-10-09 13:54:28 +02:00
Henrik Rydgård 88f9379376 Merge pull request #22450 from hrydgard/atrac-mpeg-security-fix
Fix Atrac heap overflow and MPEG uninitialized memory disclosure
2026-10-09 13:51:22 +02:00
Henrik Rydgård 1862efe392 Add a bindable key to create frame dumps 2026-10-09 13:26:22 +02:00
Mohammad Syuhada d2f3d93cad DirectoryFileSystem: Skip the stripped PSP/ prefix when fixing path case
With STRIP_PSP (memstick directory named PSP), FixPathCase walked
<memstick>/PSP/... which doesn't exist, so on case-sensitive storage every
write (savedata, mkdir, rename, ...) failed with not-found, and games
reported the memory stick as full.
2026-10-09 17:56:29 +08: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 Opus 5.5 8b4d9c3b74 Headless: Don't apply compat flags to frame dumps
Headless replays frame dumps to compare them with references and with the PSP, which a game's
workarounds skew: DarkStalkers' present hack (16510) replaces the game's final upscale, so the
replay didn't match the PSP's. The app still applies them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-09 02:03:31 +02:00
Henrik RydgårdandClaude Fable 5.1 ce19265949 Don't save log levels set from the command line
--loglevel, -d and -v are applied to the channels directly, and the next
config save wrote them to [Log], permanently replacing the user's levels.
The other command line options avoid this with DoNotSaveSetting.

The debugger had the same problem and a flag for it. Generalize that one
(NotifyChannelsChangedByDebugger -> DoNotSaveChannels) and set it here too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-08 20:11:23 +02:00
Henrik RydgårdandClaude Opus 5.5 ae32477764 Core: Let 16-bit CPU accesses through the depth mirrors see the GE's layout
With the software renderer, which now stores depth as the GE does, a
16-bit read or write at VRAM's 0x04200000 or 0x04600000 mirror goes to
where the GE's 16-bit or 32-bit color layout puts that depth pixel
(ppsspp-re geprobe exp88: the mirrors are the layout's exact inverse).
Games access depth there with nothing else, so other widths stay plain.

The accessors check the address, then whether the GPU core is the
software renderer (CoreParameter); the hardware renderers keep depth plain
and handle the layout their own way. The renderer keeps the layouts up to
date with the EDRAM address translation. With it, the JITs send 16-bit
loads and stores to the interpreter, and the IR passes don't change a
load's width to or from 16 bits.
The pspautotests that read depth through the mirrors now do so 16 bits
at a time, re-recorded on hardware unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 18:37:23 +02:00
Henrik RydgårdandClaude Opus 5 bd94e8141f Android: Converge the GL and Vulkan lifecycles on a shared RunGraphicsLoop
Stage 5 of the JavaGL removal. Now that the GL path owns its own EGL context and
render thread, Android's two lifecycle branches and the Win32 one in
MainThreadFunc were the same code three times over, so this moves the branch into
one place - RunGraphicsLoop() in Core/EmuThread.cpp. It picks the thread
arrangement the context asks for: either this thread becomes the render thread
and spawns the emu thread (OpenGL), or it just runs the main loop because the
backend spawns its own render thread (Vulkan, D3D11). Android, Windows and
headless all go through it now, which is what the "TODO: Merge with the Win32
EmuThread and so on" in app-android.cpp was asking for.

Falls out of having one implementation instead of three:

- MainThreadFunc called graphicsContext->ThreadStart() right after
  EmuThread_Start(), which already calls it, and ThreadEnd() right after
  EmuThread_Join(), which already calls that too. So Windows and headless GL ran
  CreateDeviceObjects()/DestroyDeviceObjects() twice per context, leaking a VAO
  on the way in and double-deleting it on the way out. Dates back to 16206d18ab,
  which moved those calls into EmuThread_Start/Join without removing the
  originals.

- RECREATE_VIEWS is gone. Only the GL path ever posted it; Vulkan goes through
  the same destroy/recreate without it and always has. Verified on device: views
  survive pause/resume, and both a real rotation and a density change - each one
  an activity destroy and recreate - lay them out again by themselves.

- The non-separate path is now named EmuThread rather than MainThread on
  Windows. It's the thread that runs NativeFrame(), which is what
  DebuggerThreading.md calls the emu thread, and the real main thread there is
  the GUI message pump.

The loop keeps an explicit exit flag, as a shouldExit callback, rather than
relying on ThreadFrame() returning false. In VR we skip ThreadFrame() entirely
while the XR session is idle, and the session going idle is exactly what
backgrounding the app does - so the exit would never be observed, the render
thread would sit in that sleep forever, and the UI thread joining it would hang.
That is not hypothetical: it deadlocked a Quest 2 on the first try.

Also drops a dead EmuThreadState enum and an unused JNIEnv local in
app-android.cpp, both orphaned when the JavaGL JNI entry points went away.

Tested on a nubia Z2464N (Mali-G57, Android 15) on both backends: cold start,
booting a homebrew, pause/resume in the menu, in a settings screen and in-game,
rotation back and forth, a density-change activity recreate with the game live,
the pause menu, and exit to menu. ThreadStart/ThreadEnd log exactly once per
context, and every one of those cycles tears the EGL context down and builds a
new one.

Also tested on a Quest 2 with the vr flavor, which is what shook out the deadlock
above: cold start into a focused XR session at 72fps, backgrounding (render
thread joins in 27ms where it used to never return), resuming back into VR with
a fresh EGL context, and exiting from in-app. xrCreateSession runs on the render
loop thread, which is where the EGL context is current - VRBase.cpp builds its
graphics binding from eglGetCurrentDisplay/eglGetCurrentContext, so that ordering
matters.

Windows x64 debug and the Android gradle builds are clean.

Not converged, and not a small change: NeedsSeparateEmuThread() itself. GL needs
its API calls on the thread its context is current on, so somebody has to pump
ThreadFrame() there, whereas VulkanRenderManager owns its thread outright.
Getting rid of the virtual means teaching GLRenderManager to spawn and own its
own render thread, for every GL platform at once.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01152igCdjTNhT29qvphPyPC
2026-10-08 15:29:12 +02:00
Henrik RydgårdandClaude Opus 5 812d8f4c09 Android GL: Delete the JavaGL path, leaving only native EGL
The EGL path from the previous commit is now the only one, so GLSurfaceView
and everything that existed to serve it are gone:

- NativeRenderer.java and NativeGLSurfaceView.java, deleted.
- The javaGL branches in PpssppActivity. GL and Vulkan now share one
  lifecycle: a plain SurfaceView, startRenderLoopThread() when the surface
  arrives, joinRenderLoopThread() when it goes away.
- The "androidJavaGL" queryConfig, and the AndroidNativeEGL setting and its
  developer-tools checkbox.
- displayInit/displayRender, and with them the "Second time, joining the
  emuthread and starting it up again" dance - restarting the emu thread from
  inside a JNI callback because a lost context was only observable as a
  second onSurfaceCreated.
- GraphicsContext::NotifyContextLost, which existed solely because that join
  drained the render queue on a thread with no current context. The render
  thread now joins the emu thread with the context still current, so the
  drain is real work and nothing has to be skipped.
- g_emuThread, now that nothing starts an emu thread outside the render loop.

Three things displayInit/displayRender did that nothing else did, moved
rather than dropped:

- EnterVR and the per-frame StartVRRender/UpdateVRInput/FinishVRRender hooks
  now live in RenderLoopThread's GL branch. Java's GL thread could simply
  return early and wait for the next vsync when the VR session was inactive;
  this loop drives itself, so that case sleeps 16ms instead of spinning.
  Not tested - no headset here.
- RECREATE_VIEWS after the emu thread starts, as before. Vulkan never did
  this, so it's a candidate for removal when the two paths converge.
- The thin3d error callback, which surfaces GL errors as OSD messages. That
  one moved into AndroidEGLGraphicsContext::InitSurface, next to the rest of
  the context's own setup, matching WindowsGLContext.

Tested on a nubia Z2464N (Unisoc UMS9632, Mali-G57, Android 15): cold start,
UI, booting a homebrew, backgrounding and resuming both in the menu and
in-game, and a density change, which destroys and recreates the activity the
way a rotation does. Every cycle tears the EGL context down and builds a new
one, and the renderer comes back with FBOs and textures intact. The config
ladder's first choice, RGBA8888 D24S8, is what the Java chooser landed on
here too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01152igCdjTNhT29qvphPyPC
2026-10-08 15:22:25 +02:00
Henrik RydgårdandClaude Opus 5 f868976af2 Android GL: Add a native EGL path, behind a dev toggle
First step towards removing JavaGL. The point isn't EGL itself - it's who
owns the render thread. With GLSurfaceView, Java creates the thread and the
context and calls us back, so a lost context is only observable after the
fact as a second onSurfaceCreated. That's what forces displayInit to tear
down and restart the emu thread from inside a JNI callback, and to call
NotifyContextLost first because the queue is about to be drained on a thread
with no context. None of that is needed when we own EGL: the render thread
joins the emu thread itself, with the context still current, so the drain is
real work rather than something to skip.

AndroidEGLGraphicsContext subclasses OpenGLGraphicsContext (whose own
InitSurface assumes someone else already made a context current) and adds
display/config/surface/context plus a swap function. With it, GL reuses the
Vulkan lifecycle wholesale: queryConfig("androidJavaGL") returns false, so
Java builds a plain SurfaceView and starts the native render loop when a
surface arrives - including all of the surface/pause ordering fixes from the
previous branch. runVulkanRenderLoop/requestExitVulkanRenderLoop are now
runRenderLoop/requestExitRenderLoop and serve both backends; the thread they
spawn branches on NeedsSeparateEmuThread, because the two backends split the
work in opposite directions (Vulkan: this thread is the main loop and the
render manager spawns the render thread; GL: this thread *is* the render
thread and spawns the emu thread).

Deliberately a port, not an improvement:

- GLES client version still follows NativeRenderer - 3 for VR, 2 otherwise.
  Not a cap; GLES3 is detected from GL_VERSION at runtime.
- No swap-interval function. GLSurfaceView gave us no way to set one, so
  PresentMode::IMMEDIATE has always been ignored here, and honoring it now
  would be a behavior change. Worth revisiting once this path is the only one.

The config ladder does take the user's word that Mali 400 and the Xperia Play
are no longer a factor: it asks for RGBA8888/D24S8 and falls back twice,
rather than reproducing GLSurfaceView's ask-for-nothing default that the
comments in PpssppActivity.onCreate exist to explain. ANativeWindow's format
is set from EGL_NATIVE_VISUAL_ID, which is the proper fix for the EGL_BAD_MATCH
those comments describe.

Off by default, behind Developer tools / Graphics / "Use native EGL instead
of GLSurfaceView" (OpenGL backend, Android only). Needs an app restart, since
the context is chosen in NativeApp.init.

Builds for arm64-v8a and on Windows; NOT yet run on a device.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01152igCdjTNhT29qvphPyPC
2026-10-08 15:22:25 +02:00
Henrik RydgårdandClaude Opus 5.5 70d64311af MediaEngine: Don't leak uninitialized host memory when swizzling video
When writing a decoded frame to swizzled VRAM, the staging buffer was
allocated for the guest's frame width but only the decoded width was
filled, so the row padding (heap residue including host pointers) was
swizzled into guest memory. Swizzling also reads whole 8-row tiles, past
the end of the buffer when the height isn't a multiple of 8.

Zero the staging buffer and size it (and the guest range check) for whole
tiles, with checked arithmetic. Rows are capped to the frame width so
they can't overlap or run past the end. writeVideoImageWithRange() also
swizzled from, and freed, the advanced row pointer instead of the start.

Discovered and reported by redthing1 (https://github.com/redthing1) of
V12 Security.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 14:27:01 +02:00
Henrik RydgårdandClaude Opus 5.5 283ec4db4c Atrac: Fix host heap overflow from a loop start past the end of the file
A crafted RIFF smpl chunk could put the stream read offset past the end
of the file. CalculateStreamInfo() and AddStreamData() then subtracted it
from the file size in unsigned arithmetic, the remaining size wrapped, and
the guest's data was copied to a guest-chosen distance past dataBuf_.

Check the offset before subtracting, and route every copy into dataBuf_
through a helper that clamps it to the file. SeekToSample() no longer
prefills from past the end, and dataBuf_'s slack covers a whole frame.

This is the old Atrac implementation, which is only used when chosen in
the config or when loading old savestates.

Discovered and reported by redthing1 (https://github.com/redthing1) of
V12 Security.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 14:27:01 +02:00
Henrik Rydgård c8814f545f Merge pull request #22449 from hrydgard/stencil-write-mask
Shader write mask: Remove the compat flag (after tweaking it), improve emulation
2026-10-08 13:10:10 +02:00
Henrik RydgårdandClaude Opus 5.5 ec8a590c81 Read a frame dump's game ID after a leading issue number in its name
Dumps before version 4 don't store the game ID, so it's taken from the file
name, which PPSSPP writes as ULUS10064_0001.ppdmp. The ppsspp-framedumps
collection prefixes the GitHub issue number ("11928 ULUS10064_0001 OutRun
water.ppdmp"); skip that, so those dumps get their compat settings too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 12:33:09 +02:00
Henrik RydgårdandClaude Opus 5.5 feaa41fadb Always emulate partial color write masks in the shader, drop ShaderColorBitmask
Partial red, green and blue write masks now always go through the shader with
a framebuffer read. Across the frame dump set this only improves games outside
the old list (Split/Second, Ultimate Ghosts 'n Goblins, older DiRT 2 and OutRun
dumps without a game ID). Partial alpha masks keep the old heuristic: alpha is
the stencil, with its own write mask, and taking the shader path for them made
Pro Yakyu Spirits and Gangs of London worse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 11:18:05 +02:00
Henrik Rydgård 8e596032f1 Merge pull request #22446 from hrydgard/misc-rendering-fixes
Misc rendering fixes
2026-10-08 11:16:14 +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
sum2012 93c0d44b24 Fix Register_sceGe_driver 2026-10-07 18:55:59 +08:00
Henrik Rydgård 3c6c7f548b Merge pull request #22433 from hrydgard/softgpu-threading
SoftGPU: Improve threading
2026-10-06 08:09:17 -06:00
sum2012 472cf26be4 Add some kernel nids
sceReg:made the registry writable in-memory (sceRegSetKeyValue/sceRegCreateKey overlay + merged cached lookups), which is what fixed the Chinese font. Fix #14032
2026-10-06 04:59:56 +08:00
Henrik RydgårdandClaude Opus 5.5 3c3025a087 SoftGPU: Keep drawing queued when a display list stalls
Returning to the CPU flushed all queued drawing, also when a list only reached its stall address.
Wipeout Pure builds its lists as it goes and stalls about a hundred times a frame, so the main thread
spent a third of its time finishing the drawing at those flushes. A stalled list isn't done, so now
only a finished one flushes there, which makes Wipeout Pure 15% faster.

What relied on that flush now flushes itself: memory copies, sets, readbacks and uploads that overlap
the queued drawing, and save states, before memory is saved or loaded (FlushPendingDrawing). Syncs and
the display already did. Queued drawing still reads textures from PSP memory when it runs, so a game
that rewrites a texture between stalls without syncing would see the new one; on hardware the GE has
usually drawn with the old one by then.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 14:55:04 -06:00
499cdd55ad Validate untrusted IDs before using them as directory names
Texture pack installs took the destination directory from the archive's
textures.ini, and PKG update installs from the PBOOT's DISC_ID, both
unchecked, so a crafted archive or package could write outside
PSP/TEXTURES or PSP/GAME. Add IsSafePathComponent() to PathUtil and check
both, replacing the WebServer's own upload filename check with it too.

DISC_ID from any PARAM.SFO also names configs, cheats, textures, exdata
and savestates, so ReadSFO now drops one that isn't a safe name and the
game gets a fake ID like homebrew. The same goes for a frame dump's ID.

Combines #22419 and #22420 by Acts1631.

Co-Authored-By: Acts1631 <acts1631kjv@proton.me>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 13:39:06 -06:00
sum2012 a66249683d Add support sctrlHENSetStartModuleHandler by Zcode 2026-10-06 02:28:15 +08:00
Henrik RydgårdandClaude Opus 5.5 27ce1be6e9 Module start/stop threads: Honor the whole SceKernelSMOption
sceKernelStartModule, sceKernelStopModule and the self-stop calls now
check their option first: with SDK 2.08 or later its size must be 0x14,
and attribute bits outside 0x00F06000 are an error.

The option's attribute is added to the module's thread attribute rather
than ignored (or, when stopping, replacing it), and the default attribute
is 0 rather than the module's own attribute. A nonzero mpidstack puts the
stack in that partition, which has to be user-accessible for a user
module and kernel-only for a kernel module.

A failed thread creation is now returned instead of asserting.

Hardware test: modules/startoptions.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 10:10:22 -06:00
Henrik RydgårdandClaude Opus 5.5 efc1969177 Module start/stop threads: 0x1000 default stack for kernel modules
modulemgr only defaults a module_start/module_stop thread's stack to 0x40000
for user modules. Kernel modules get 0x1000. We gave every
module 0x40000, which mostly cost kernel memory when booting the VSH.

User modules, and so games, are unaffected.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 09:42:04 -06:00
Henrik Rydgård 9a2f5eb191 Merge pull request #22414 from hrydgard/ge-bit-exact
SoftGPU: Make it mostly bit exact vs PSP hardware
2026-10-05 09:15:24 -06:00
Acts1631 7de6a6ccfb Avoid overflow in matching packet length checks
Matching packet handlers added untrusted payload lengths to fixed header
sizes before comparing them with the datagram length. Large values could
overflow and permit out-of-bounds reads. Compare against the remaining
packet size instead, and reject negative ACCEPT sibling counts.
2026-10-03 21:55:32 -04: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