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>
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>
__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>
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>
The first one got n-1, which is the state's last event, so that one moved
to a new id while its queued occurrences fired the newcomer. n-1 dates
from before RestoreRegisterEvent could fall back when out of range. Also
recompute a debugger run-until deadline against the loaded clock.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
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>
MP3 emulation already went through FFmpeg, leaving MiniMp3Audio dead.
The one live user was loading MP3 UI sound effects (custom achievement
sounds), which now splits the file into frames and decodes them with
the FFmpeg MP3 decoder.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
The write pass stores memory with the JIT's emuhacks cleared, but the
verify pass compared against memory that still had them, so it would
report a mismatch under a JIT. Only EMULATOR_DEVCTL__VERIFY_STATE runs it,
and nothing currently does. It also counted as a save in the generation.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
DirectoryFileSystem reused one entry across files, so a failed reopen
could seek another file's handle. VirtualDiscFileSystem leaked every open
handle on each load. MemoryStick ignored the saved free space basis.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
The old icons stayed in PPGe's decimation list with kernel addresses from
before the load, and got freed out of whatever the loaded state had there.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
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>
DoState replaced native_ while holding a lock_guard on its mutex, so the
old NativeInput could be freed before the guard unlocked it. Crashed every
state load (e.g. Splinter Cell Essentials, anywhere in the game).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
since 7e6405291 only full 64 KB reads get cached, so any read touching the
shorter last block of a file came back short. count blocks with
blocks_.size() too, so failed reads can't inflate the count until
MakeCacheSpaceFor loops forever. also stop read-ahead from asking the
backend for blocks past the end of the file.
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>
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>
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>
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>
While apctl was in JOINING, every Update restarted the fade-in, so the dialog
sat at its first, barely visible step until the state moved on to getting an
IP. It now keeps the fade-in it started with.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- 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>
- 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>
- InitStart checks the address and size the way sceUtility_Driver does
(0x40 and 0x44 are both fine, utility/dialog/sizes) and that the field
struct is in memory.
- The input text was read up to a terminator with no limit and no memory
check, and the output was written through an unchecked pointer every
Update. Both are bounded and checked now.
- Converting a string to UTF-8 checked for room before each character, then
wrote up to three bytes and a terminator, overflowing a 2048-byte stack
buffer on long non-ASCII text (both conversions did).
- An output buffer of length 0 made FieldMaxLength wrap around, and the
keyboard preview then indexed the text at -1.
- The native input box's callbacks captured the dialog and could write into
it after it was deleted, and its status was read and written without the
lock. They now share a small state object instead, a new one per start
and per state load (unless a box is still open, which answers into the
loaded state).
- Savestates keep the current keyboard and the Korean combining state. With
older ones, it picks a keyboard the field allows, as Init does.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- 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>
InitStart sizes: Netconf and NpSignin accepted any size, then wrote
common.size bytes back from a 64-68 byte host struct, copying host memory
into PSP RAM. GamedataInstall looked for install files before checking the
size, and the HtmlViewer read options before checking the whole request was
in memory. All the dialogs now check the address, then the sizes
sceUtility_Driver accepts (utility/dialog/sizes), before anything else, as
the firmware does (a bad address is INVALID_ADDRESS), and write back no more
than the struct.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
Until then the dialog runs normally (and writes result = 0, which an
immediate abort skipped). Measured with one Update per vblank; at one every
other vblank a PSP took 6, so it isn't purely a count.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>