Commit Graph
6686 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 a4ce937f81 Savestate: Bounds-check BufferQueue and Atrac pending output, fix dummy JIT prefix
An interpreter state always claimed an uneaten VFPU prefix, which made a
JIT loading it run in unknown-prefix mode for the rest of the session.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 11:32:07 -06:00
Henrik RydgårdandClaude Opus 5.5 cd94e414cd Savestate: Reset what older states lack instead of keeping pre-load values
Missing sections (videocodec, audiocodec, aac, mp3) and old-version
branches (impose, io, umd, gps, mic, display, font) left the session
before the load in place.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 11:32:07 -06:00
Henrik RydgårdandClaude Opus 5.5 8663ce2a33 Savestate: Save state that was missing from several modules
sceMpeg's AVC resource flag, whether the VSH is running, sceReg's handle
counter, sceNet's pending apctl events and product code block, and the
save dialog's copy of the original request (without which the first
Update after a load reloaded the request and lost its results).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 11:32:07 -06:00
Henrik RydgårdandClaude Opus 5.5 52299a5e17 Savestate: Save sceDmac and sceUsbCam, which were never called, and NpDrm
__DmacDoState and __UsbCamDoState existed but weren't in the module list,
so the memcpy deadline and camera state carried over from before a load.
The NpDrm licensee key wasn't saved or reset at all, so EDATA opened after
a load in a fresh session couldn't be decrypted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 11:32:07 -06:00
Henrik RydgårdandClaude Opus 5.5 dd9adfdf60 HLE: Resolve imports of states from before HLE v3 with the flags of then
Without saved flags, those states were resolved against today's defaults,
and everything graduated since (sceMpeg, sceFont, the leaf libraries) ended
up on unresolved stubs.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 11:32:07 -06:00
Henrik RydgårdandClaude Opus 5.5 e0767e30a4 Savestate: Give sceFont's newer action types new ids in older states
Same as the exit callback: states from before them numbered the action
types without them, so the boot-time ids can belong to other types.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 10:57:21 -06:00
Henrik RydgårdandClaude Opus 5.5 58bae839e2 Savestate: Don't let AtracOutput take another event's id in older states
A state from before the event kept its boot-time id, which Atrac restores
first, so the event the state had under that id moved to a new one while
its queued occurrences kept firing AtracOutput. In Outrun 2006 that was
the vblank, and the game waited for it forever.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 10:57:21 -06:00
Henrik RydgårdandClaude Opus 5.5 40a70b004c Savestate: Reset video frame tracking and ME busy time on load
Neither is serialized, and both went stale on load. The ME busy time was
measured against the pre-load clock, so loading an earlier state made the
next SAS/codec job wait until the old time came around, freezing the game.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:36:48 -06:00
Henrik RydgårdandClaude Opus 5.5 f96a58fb3e Savestate: Don't change the running game's state when saving
Some DoState code meant for after a load ran on every save:
- scePower reset the bus frequency a game set (and with a locked CPU
  speed, applied the current setting to the clock).
- sceDisplay reset the lag sync baseline, and could schedule lag sync in
  the measuring pass only, which failed the save.
- GPUState dirtied the texture, sceUmd notified the UI, and sceMpeg
  dropped a pending ringbuffer fix-up for an old state.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:34:06 -06:00
Henrik RydgårdandClaude Opus 5.5 2e5edcd298 Savestate: Don't break event callbacks when a save fails
CoreTiming::DoState replaced every event's callback with the anti-crash
one in every mode, relying on each module's restore to put it back. A save
that failed partway never got to those, and left the running game with
events that break into the debugger. The missing-section fallbacks then
also ran on the save: cheats and the mic re-registered events into the
wrong slots, and achievements reset the runtime.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:34:06 -06:00
Henrik RydgårdandClaude Opus 5.5 937c100af8 Savestate: Defer operations while the GE debugger holds a list
The list belongs to the sceGe call still in progress, whose end would have
run on the loaded CPU state. Also stop the camera and GPS when a state has
them off, don't restart capture when saving, and fix a double free of the
pmp frame queue (it only holds the media engine's own frame).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:34:06 -06:00
Henrik RydgårdandClaude Opus 5.5 0a687b9435 Savestate: Bounds-check sizes from the file, and plug leaks on load
Reject sizes past the end of the state before allocating (FPL, PGF,
achievements, SAS grain, savedata list, the memory fast path), fail
instead of desyncing on a SAS voice count mismatch, and free what old
states' paths and shrinking pointer containers dropped.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:34:06 -06:00
Henrik RydgårdandClaude Opus 5.5 a4e169893f sceAudiocodec: Recreate decoders from the context on load
They were recreated without block size or extradata, which Atrac3 needs,
so it stayed silent after a load. Also drop the old decoders when the
state has none, and don't overflow on v1 states.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:34:06 -06:00
Henrik RydgårdandClaude Opus 5.5 490f08d220 Savestate: Fix action and exit callback handling on load
Delete the old HLE mips call actions instead of leaking them or keeping
stale ones, fail the load on an unknown action type instead of crashing,
and derive the exit-callback-pending flag from the loaded state.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:34:06 -06:00
Henrik RydgårdandClaude Opus 5.5 06e11fb176 Savestate: Rehash analyzed functions before writing replacements on load
The function list is from before the load, where other code (an overlay
module) may have been. Hashing it again from the loaded memory keeps the
hooks to code that actually matches.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:34:06 -06:00
Henrik RydgårdandClaude Opus 5.5 639e57c2bd Savestate: Keep pending adhocctl requests, so a waiting Init survives a load
With WLAN on and no server reachable, sceNetAdhocctlInit keeps its thread
waiting for the login. The load dropped the request, and the wait ended in
BUSY, which Init can't retry. Splinter Cell then ran its failure path with
a deleted event flag. Also stop freeing matching event buffers into the
restored allocator, and take the event lock when clearing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:34:06 -06:00
Henrik RydgårdandClaude Opus 5.5 db071361b0 Savestate: Wait for the SAS and IO threads before touching memory
Both drained only in their own DoState, after memory and Atrac contexts
had already been replaced under a mix or read still in flight. Also fix
sceUmd loading umdActivated into the wrong variable, and count each save
once in saveStateGeneration (it also bumped in the measuring pass).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:34:05 -06:00
Henrik RydgårdandClaude Opus 5.5 d28b79960b Savestate: Fix loading kernel heaps and pre-exit-callback states
KernelHeap was missing from CreateByIDType, and an unknown type returned
without an error, desyncing the rest of the load. States from before exit
callbacks kept the boot-time action slot, which sceMpeg's restore took over.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:34:05 -06:00
Henrik RydgårdandClaude Opus 5.5 b39cf47124 Charge measured SAS mix time, and share the Media Engine between its jobs
The SAS mix estimate was a guess capped at 1200us. Measured on a PSP
(pspautotests audio/timing/sastiming), a mix costs 110us plus 0.49us per grain
sample, plus per voice and sample 0.445us + 0.0675us per unit of pitch ratio
(VAG; PCM and noise slightly less), plus 0.64us per sample with a reverb type
set. Linear to 1% across 64-2048 samples and 0-32 voices: 32 VAG voices at 512
samples take 8.7ms, not 1.2.

The mix runs on the Media Engine, as do video and audio decoding, so they now
queue behind each other there (MEScheduleJob). A game that keeps SAS running
during a movie - Star Wars: Lethal Alliance - decodes more slowly for it, like
on hardware.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 17:33:33 -06:00
Henrik RydgårdandClaude Opus 5.5 8f70003cce Remove the PaceVideocodecDecode workaround for Ys I & II
The blit cost model now paces its movies at 30 fps without it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 16:44:28 -06:00
Henrik RydgårdandClaude Opus 5.5 7a675b42d5 Model the movie blit by texture format, location and width; scale costs with the clock
Measured on a PSP (pspautotests gpu/timing/blittiming), a full-screen blit
from an unswizzled texture costs what the texture fetch costs: 16-bit formats
half of 32-bit, VRAM a fifth of RAM, and rectangles wider than ~128 texels
~7.5x as much as narrow strips, from texture cache thrashing. Framebuffer
format, filtering and blending don't matter. Ys I & II draws its movie as one
full-width sprite from a 565 texture in RAM, which takes 33ms - that, not the
decode, is what holds it to 30 fps.

All of the ME and GE costs speed up with the clock (2/3 as long at 333/166),
since the whole system runs from the one PLL.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 16:40:11 -06:00
Henrik RydgårdandClaude Opus 5.5 298facd614 sceAudiocodec: Charge AAC decode time too
sceMp4AacDecode of an AAC-LC stereo frame takes about 1.7ms on a PSP, measured
with pspautotests video/mp4/mp4timing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 16:14:37 -06:00
Henrik RydgårdandClaude Opus 5.5 a0b812bc31 Charge hardware-measured time for movie decode, colour conversion and blit
Movie players like the one in Star Wars: Lethal Alliance present every decoded
frame after a single vblank wait, with no clock or timestamp check, so the
frame rate depends on decode, CSC, ATRAC decode and the GE blit adding up to
more than a vblank. We charged nearly nothing for any of them, so such movies
ran at 60 fps until the ringbuffer's slack ran out.

Costs measured on a PSP with a copy of that player (pspautotests
video/mpeg/playertiming), for a 480x272 frame:

- sceVideocodecDecode: 3.4ms (sceMpegAvcDecode 5.8ms less sceMpegAvcCsc 2.4ms)
- sceMpegBaseCscAvc: 2.4ms, was a flat 4ms
- sceAudiocodecDecode, ATRAC3+ only: 2.5ms per frame
- GE: 9.7ms for a through-mode rectangle blit from a decoded video frame,
  charged by area, only for textures in the buffer a decoder last wrote.

GE time also now carries across stall address updates. Before, a list sent
in stalled chunks only had its last chunk's time counted, so sceGeDrawSync
returned 39us after a blit that takes 9.7ms. This affects every game that
builds its lists incrementally, so GE-timing-sensitive games need checking.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 15:23:08 -06:00
Henrik Rydgård cae623f4e6 Merge pull request #22349 from hrydgard/sceutility-morework
sceUtility: Move all save/load file I/O to be off the main thread, assorted fixes
2026-09-25 14:12:52 -06:00
Henrik RydgårdandClaude Opus 5.5 b9e7d9e0a0 Netconf: Report success, and don't get stuck without a way out
- The result was only ever written on cancel, so a connection that worked
  gave the game back whatever was in the field, which some fill with -1.
- Infrastructure mode drew a Cancel button that did nothing, so if the
  access point never gave an IP there was no way out. Cancelling now also
  disconnects the connect it started, so the game isn't left connected
  after being told the dialog was aborted.
- An unknown netAction went down neither path and never finished.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 12:50:41 -06:00
Henrik RydgårdandClaude Opus 5.5 1d07fdba86 Utility: Small fixes from reviewing the dialogs
- Loading a utility module without room for our (rough) memory block stored
  (u32)-1 as its address, which av_atrac3plus then memset. It now loads
  without the block, which HLE doesn't need.
- LoadNetModule's module id was unsigned, so a load error was passed on to
  sceKernelStartModule as an id.
- The dialog helper threads put the game's priorities straight into ORI
  immediates; ones that aren't a priority at all now fall back to 0x20.
- A fade never finished with an animSpeed of 0 or less.
- MsgDialog V3 button captions filling all 64 bytes ran on into the next
  field.
- GamedataInstall: a file shorter than it claimed was retried forever, Abort
  worked in any state and wrote through an unchecked pointer, and the game
  and data names were read as C strings from fixed-size fields.
- NpSignin: a cancel was overwritten with SUCCESS in the same frame, so the
  game saw a sign-in. The status is reset on start, so a reused struct no
  longer hangs.
- Unloading av_atrac3plus never reached __AtracNotifyUnloadModule, leaving
  the atrac state pointing at freed memory.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 12:50:41 -06:00
Henrik RydgårdandClaude Opus 5.5 41b5074a98 Utility: Release volatile memory on a forced shutdown, fix dialog savestates
- The Yugioh savedata workaround force-stopped the other dialog without
  releasing the volatile memory it held, so the savedata helper then waited
  for it forever.
- Screenshot's ShutdownStart and Update succeeded when no screenshot was
  running, putting it back into SHUTDOWN.
- Savestates: Netconf didn't keep its request address, and a DNS json
  download going on was gone after a load, so it could wait for it forever;
  it now fetches the json again (it's cached). NpSignin didn't keep its
  request address either. Both restart their timeouts instead of timing out
  at once. With states from before, they keep the current address as they
  used to.
- Loading a state from before NpSignin, GameSharing or HtmlViewer were saved
  resets them rather than keeping this session's state (including the
  HtmlViewer's memory block), and without Shutdown's side effects, which
  would write to the loaded memory and release its volatile lock.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 12:50:41 -06:00
Henrik RydgårdandClaude Opus 5.5 9e69c1f9aa Savedata: Keep the IO thread out of the dialog's state
Replaces the temporary fix that did the hidden modes' IO inside Update.

The IO thread read and wrote the dialog's request, display state and save
list, all shared with the emulator thread, which kept using them to draw the
dialog and reload the request from the game.

Now the IO thread works on its own copy of the request, its own SavedataParam
and directory names resolved up front, and shares nothing else with the
emulator thread but the (locked) file system, MemoryStick_FreeSpace's cached
use and sceChnnlsv's scratch buffer and kirk state, the last two now under
locks too. It still reads and writes the game's buffers directly, like a PSP's
utility threads and sceIoReadAsync do, so a savestate waits for it before it
saves or loads memory. Save
bookkeeping, the save indicator and display changes happen on the emulator
thread when the results are taken, and only the request fields the IO changed
are copied back, so a game's own edits in the meantime survive.

Hidden modes take the results at the next Update (or, with Host IO timing,
the first Update that finds them done). The visible dialogs keep drawing and
take them once the IO is done; save and load used to stall the emulator
thread for the whole operation. Savestates keep results that haven't been
taken yet.

When the results land in PSP memory doesn't matter to games, so
utility/savedata/filelist now only prints them once the utility has finished.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 12:50:39 -06:00
Henrik RydgårdandClaude Opus 5.5 5f261bd7ff Utility: Stand in for the HtmlViewer, offering to open the page in a browser
Instead of the PSP's web browser, a dialog shows the URL the game wants
and opens it in the host's browser on X, or backs out on O. Either way the
game sees the browser closed normally. Platforms that can't open a URL
(the new SYSPROP_CAN_LAUNCH_URL) only offer to back out. Only plain
printable-ASCII http(s) addresses are handed over, and on Linux without a
shell.

What the firmware does (sceUtility_Driver, 6.61, plus
utility/dialog/htmlviewer): the HtmlViewer has its own state apart from the
other dialogs, so they don't block each other, and its calls return
WRONG_TYPE until one has started. The request size picks the 2.00 to 3.00
layout, and InitStart allocates 3.5MB of user memory (4.5MB with options
bit 0x400 from 2.70 on), failing with 800200d9 without it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 12:50:23 -06:00
Henrik RydgårdandClaude Opus 5.5 3c15b2b112 Utility: One dialog at a time whatever its type, like the firmware
On a PSP, every InitStart fails with INVALID_STATUS until the last dialog
started is back at NONE, including while it's shutting down, and before
its params are checked. A failed InitStart leaves the current type alone.
We returned WRONG_TYPE instead, and a failed InitStart (e.g. a bad size)
still switched the current type, so every later dialog was refused. (One of
ours that fails after already starting, as savedata can, still becomes the
current type, since the game may poll it.)

The busy check applies status changes that are due, but doesn't use up an
auto status dialog's one-time INITIALIZE/SHUTDOWN reports; one that only
waits to report SHUTDOWN is let finish. Auto status dialogs now release
volatile memory on the way to NONE, including when the game saw RUNNING
before the init thread was done, which used to leave it locked for the next
dialog. GamedataInstall no longer requires currentDialogActive, which its
ShutdownStart cleared even when it then failed, so it could never finish -
and would now have blocked every other dialog.

Also: MsgDialog accepts exactly the three sizes sceUtility_Driver does (we
memcpy'd whatever size was given), and HtmlViewer GetStatus answers
WRONG_TYPE.

Adds utility/dialog/status and utility/dialog/priority, recorded on a PSP.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 12:50:23 -06:00
Henrik Rydgård 774ed9a5a9 Logging cleanups in sceHttp/sceHttps 2026-09-25 12:38:40 -06:00
Henrik RydgårdandClaude Opus 5.5 be30966079 sceNet: Update the connection's DNS server when the infra-dns json arrives
The apctl info copies the AutoDNS server when the connection gets its IP, which
normally happens before netconf has downloaded infra-dns.json, so games that read
the primary DNS server afterwards (to do their own lookups) got an empty string.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 12:24:02 -06:00
Henrik RydgårdandClaude Opus 5.5 6cb7c985bd sceHttp/sceParseUri: Fix the size query and header block, Wipeout Pure's browser works
sceUriParse's size query (no parsed-URI or work area) returns 0 on a PSP, not -1,
which made Wipeout Pure's embedded browser give up before connecting. And
sceHttpGetAllHeader hands out the header block as received, ending with the blank
line, NUL-terminated and with the NUL counted; without the blank line the browser
never displayed the image the page consists of. Both from the 6.60 firmware
modules (libparse_uri.prx, libhttp.prx).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 12:24:02 -06:00
Henrik RydgårdandClaude Opus 5.5 feb6caa3c4 Implement mfic and mtic
Both were no-ops, so mfic left its destination unchanged. They read and
write the interrupt enable flag that sceKernelCpuSuspendIntr/ResumeIntr
use. Only bit 0 counts for mtic, which also goes for
sceKernelCpuResumeIntr, since on hardware it's just mtic.

Adds the intr/mfic test, recorded on hardware.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 11:08:13 -06:00
Henrik RydgårdandClaude Opus 5.5 f47269864a _sceKernelAllocateTlspl: Check user pointers, support the timeout
The third argument is a timeout pointer, as threadman.prx shows. A kernel
address from user mode is ILLEGAL_ADDR there; we used to write through it.
Also, no lookup by index: the syscall requires the exact uid.

Adds the threads/tls/allocate test, recorded on hardware.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 10:05:07 -06:00
Henrik RydgårdandClaude Opus 5.5 ac39c55c36 Implement _sceKernelAllocateTlspl
This is the syscall usersystemlib's sceKernelGetTlsAddr makes when the
thread's cached TLS address is null, as (uid, &addr, 0). Code that has to
run before usersystemlib.prx is loaded (like plugins built with a Rust SDK)
inlines sceKernelGetTlsAddr and imports this directly.

Shares the allocation with sceKernelGetTlsAddr. A thread waiting on a full
pool stores its address pointer as the wait value, so no state changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 10:04:44 -06:00
Henrik RydgårdandClaude Opus 5.5 ff7371dcc5 Atrac: Write decoded samples when sceAtracDecodeData returns, not when called
On a PSP the Media Engine decodes, and the samples land in the output
buffer as the call returns, a couple of milliseconds in. We wrote them at
once and only then delayed the thread. Since sceAudio plays straight out
of game memory, that matters: Fired Up decodes each chunk to 0x40 bytes into
one of its two buffers, running over the first 16 samples of the other one,
which it has just queued, and relies on the mixer having read those first.
Writing early replaced them about 21 times a second, which is the constant
crackle in its music and intro (it showed up with the sceAudio buffering
rework, which stopped copying buffers at enqueue).

Now the decoder's output is set aside, the old contents put back, and a
CoreTiming event writes the samples just before the thread wakes. Pending
writes are kept in savestates.

Adds audio/blocking/parked, recorded on a PSP: a blocking output that had
to wait returns before any of its buffer has played, so the game really
does depend on the decode's latency.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 14:49:09 -06:00
Henrik RydgårdandClaude Opus 5.5 f20dd7565b Don't crash at exit with a download still in flight
A request started since the last RequestManager::Update (headless never
calls it) sat in newDownloads_, which CancelAll skipped. It was then
destroyed along with the static g_DownloadManager at exit, and its
destructor removed its progress bar from the already destroyed g_OSD:
"mutex lock failed". Seen with a Netconf dialog still downloading the
infra DNS json when a test ended.

CancelAll now takes the new ones too, and runs at shutdown while g_OSD is
still there. The Netconf json request is also let go of when the emulator
shuts down, rather than living on into the next game.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 11:23:59 -06:00
Henrik RydgårdandClaude Opus 5.5 d4de590e3f sceCtrl: Name the port-taking buffer functions
sceCtrlPeek/ReadBufferPositive2/Negative2, which take a port before the
usual buffer and count. Not implemented.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 19:30:31 -06:00
Henrik RydgårdandClaude Opus 5.5 30fada5833 Utility: Run GameSharing through a placeholder dialog, quiet its WRONG_TYPE logs
GameSharing InitStart used to return 0 without starting anything, so its
GetStatus stayed at NONE and a game waiting for the dialog to finish
would hang. It now goes through the normal lifecycle (INIT, RUNNING,
FINISHED, SHUTDOWN, NONE) and reports that the user cancelled.

PSPPlaceholderDialog was abstract and unused (and missing from CMake);
it's now that stand-in.

WRONG_TYPE from GameSharing GetStatus/Update/ShutdownStart is what a PSP
returns whenever another dialog type was the last one started, so log it
at debug like the other dialogs. Sega Rally polls it every frame.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 19:25:24 -06:00
Henrik RydgårdandClaude Opus 5.5 cafbab50d9 Io: Reject memory stick paths with characters FAT can't store
sceIoOpen on such a path returns an invalid-argument error on hardware,
not file-not-found (utility/savedata/idlist).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 19:25:24 -06:00
Henrik RydgårdandClaude Opus 5.5 20970d5fad Dialog: Follow the firmware's thread priorities in init/shutdown
On a PSP, dialog init and shutdown happen partly at the accessThread
priority and partly at the graphicsThread priority, one phase after the
other. Model that with one helper thread that switches priority per phase,
and let starting it reschedule normally instead of disabling interrupts.

A caller with worse priority than both now sees shutdown complete inside
ShutdownStart, as on hardware. NFL Street 3 (graphics 17, access 19,
caller 111) calls the next InitStart right after ShutdownStart and used to
loop forever on 'A save request is already running' (#19957). The utility
pspautotests, where the caller has better priority, are unchanged.

Also logs the dialog thread priorities at debug level.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 19:25:24 -06:00
Henrik RydgårdandClaude Opus 5 4aef060293 Carry "this is video" across block copies
videos_ only learns about the CSC output, so the texture we actually draw is
just an ordinary 512x512 8888 texture whose contents happen to be different
every frame: hash, miss, throw it in the secondary cache, rebuild, forever.

So track the copy. NotifyVideoCopy marks the destination as video when the
source is, and the copy funnels call it: sceDmacMemcpy, sceKernelMemcpy, and
the four replaced memcpy/memmove variants. It sits outside their "is either
side VRAM" gate, since a RAM-to-RAM copy of a frame is still a frame.

Being a video texture also gets it forced linear filtering and keeps it out of
texture upscaling, which is what you want for a video either way.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 16:10:28 -06:00
Henrik Rydgård ffc32d02fe Merge pull request #22336 from hrydgard/misc-work
Code cleanup, add a dismissable reminder about the 32-bit version on a 64-bit OS.
2026-09-22 13:15:12 -06:00
Henrik Rydgård 2fe24d1182 Add warning if you run the 32-bit version on 64-bit window. 2026-09-22 10:04:31 -06:00
sum2012 14fd9d1752 Remove Fontltn12Hack 2026-09-22 20:10:33 +08:00
Henrik Rydgård d21467beb0 De-claude some commments. 2026-09-21 16:50:22 -06:00
Henrik Rydgård be62c09fe5 Merge pull request #22327 from hrydgard/sce-ge-queue
Claude code review: Rework sceGe
2026-09-21 16:28:15 -06:00
Henrik RydgårdandClaude Opus 5 333d84935e Add --force-hle, and stop reporting a normal sceAudiocodec re-init
--disable-hle had no counterpart, which made "is this our fault or the game's?"
awkward to answer: the only ways to put our HLE back were a per-game config or
hiding the firmware, and neither works from a script - the setting is per-game
and the firmware gets found anyway. --force-hle takes the same bitmask and runs
our HLE for those libraries even where the real module is now the default, so
the same repro can be run both ways and the logs diffed.

Used it on the warnings left over in Tekken 6 under the real mpeg.prx. Three of
them appear identically with --force-hle=16, so they are the game's own and
match what the hardware answers: sceKernelChangeThreadPriority(-1) eight times
in a row (pspautotests/threads/threads/change says hardware returns
UNKNOWN_THID for -1 too), sceAtracAddStreamData on a released id with a zero
byte count, and a sceKernelDeleteMutex on a garbage id.

The fourth only happens under the real module and is ours. mpeg.prx sizes its
allocation through a scratch context in its own bss and calls
sceAudiocodecReleaseEDRAM on that one, while decoding through a different
context that never gets released - so the next movie's sceAudiocodecInit finds
a live decoder and replaces it. That is once per video on every game running
the real module, and it was a WARN_LOG_REPORT, so it would have reported from
everyone's machine. It is bounded - removeDecoder deletes the old one and Init
makes exactly one more - so it is an INFO_LOG now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 15:26:51 -06:00
Henrik RydgårdandClaude Opus 5 626a442c34 Stop shouting about two things the hardware does too
Both of these fire once per video in Tekken 6, and neither is a fault.

sceAudiocodecReleaseEDRAM warned "failed to remove decoder" whenever there was
no decoder to remove. There usually isn't: mpeg.prx calls CheckNeedMem and
GetEDRAM to size the allocation, and only creates a decoder if the stream turns
out to need one, so releasing without ever having made one is the normal path.
Demoted to debug.

While there, its signature was one argument too long. audiocodec_260.prx's own
sceAudiocodecReleaseEDRAM (080007c0) reads only a0 and sets up a1 through a3
itself, so the "id" we took was whatever the caller happened to leave in the
register - which is how the log came to show an sceMpeg error code as the
second parameter of an audio call.

sceUtilityLoadModule logged MODULE_ALREADY_LOADED at error level. It is a
normal answer that games rely on: Tekken 6 asks for av_avcodec three times and
never unloads it, ignoring the result each time. Only that one code is demoted;
everything else from a module load is still an error.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 15:01:03 -06:00