A PSP never writes the info struct's length, and on a call without new data writes updated = 0 and
the last data's timestamp again, 0 before any came (pspautotests net/adhoc/pair/gamemodeinfo).
PPSSPP wrote the length and left the timestamp alone without new data. Shaun White Snowboarding
reads the timestamp either way, from a struct on its stack, and keeps the newest it has seen as the
time of the last update; stack garbage got in there, and the host gave up on the game a moment
after the match started ("The game is no longer available").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a PSP, with 8192 bytes in flight from an 8 KB send buffer, poll leaves out SEND and a
non-blocking send of 16 more returns WOULD_BLOCK until about half the buffer has drained, and poll
leaves out FLUSH until the data has arrived, which takes as long as a flush (pspautotests
net/adhoc/pair/ptpsendready). PPSSPP reported both at once and took any send. The in-flight bytes
from the flush model now drain linearly until their delivery time; sends that don't fit wait, and
GetPtpStat counts what's left. Relay PTP sockets now record their buffer size, which they didn't.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PSP returns size 0 without touching a buffer too small for one entry, and GetPeerList refuses
a negative size with INVALID_ARG (pspautotests net/adhoc/pair/listsizes). PPSSPP cleared the
whole buffer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What a PSP does, where PPSSPP differed:
- A PtpOpen or PtpListen on a port in use uses up a socket ID, as PdpCreate's does.
- A connect to a MAC that isn't in the group would block or times out, and is refused once the
retransmits would have run out, (count + 1) * interval. It returned INVALID_ADDR at once.
- PtpAccept's flag must be 0 or 1.
- A blocking broadcast returns 0, so one the host can't take in time is dropped, not a TIMEOUT.
The FIXMEs and TODOs other tests already answer (socketids, adhocctl, gamemode, ptp) now say what
the PSP does instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a PSP a non-blocking flush right after a send returns WOULD_BLOCK, and 0 once the data is in
the peer's buffer, about 4.5 ms plus 2.4 us per byte, and GetPtpStat counts the bytes as unsent
until then (pspautotests net/adhoc/pair/ptpflush). PPSSPP's flush returned 0 at once. NBA 2K13's
host sends the match info, flushes until it gets 0 and closes; with the instant flush it closed in
the same frame, and the joiner, which checks for a disconnect before reading, never read the last
chunk ("There was a problem transferring the match information").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GetScanInfo returned nothing while in a group. On a PSP the list from the last scan stays after
Create, Join and Disconnect, and only a scan attempted while connected empties it (pspautotests
net/adhoc/pair/scaninfo, added here along with ptpflush). NBA 2K13's joiner looks for the group it
just joined in the list and leaves when it isn't there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PSP refuses it immediately (pspautotests net/adhoc/ptp, "Connecting to nobody"), while the
relay held it for about 4 seconds before giving up. NBA 2K13's joiner connects to each entry of
its peer table in turn, itself first, and gave up on the group before it got to the host.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rebuilding the stencil after a reinterpret (89fabe21fb) copied the whole
destination to a temp buffer and redrew the stencil from it in a pass of its
own. Tantalus games reinterpret about 70 times a frame, and on an M1 with
Vulkan that made MX vs ATV Untamed 44% slower (GPU bound).
Now a stencil variant of the reinterpret shader reads the source the same way
the color reinterpret does and draws the stencil from its alpha, into the same
rectangle right after it, so it lands in the same render pass and needs no
copy. One draw with stencil export, otherwise a clear and one per alpha bit.
The 1141 dumps in ppsspp-framedumps render identically before and after, on
Vulkan (export) and OpenGL (per bit) on macOS. Split Second, OutRun and
Ultimate Ghosts 'n Goblins dumps go through this path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ninja only knew git-version.cpp from its timestamp at the start of the
build, so when the GitVersion step rewrote it, the object wasn't rebuilt
until some later build happened to notice. An Android APK built right
after a commit could carry the previous commit's version string.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Emulation does a burst of work each frame and then waits, so the scheduler
sees a mostly idle thread and moves it to slow cores at low clocks. On
Android 13+, report the frame thread's CPU time per frame against a target
of half a frame. CPU time leaves out the frame limiter's sleep and any
blocking on vsync or the GPU. Idea from Azahar.
Does nothing on devices without hint session support.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
setFramesPerDataCallback forced callbacks of PROPERTY_OUTPUT_FRAMES_PER_BUFFER
frames, which made Oboe add a block adapter holding back an extra buffer
whenever that doesn't match the burst. On a Galaxy S21 Ultra the MMAP stream
has a 96 frame burst against a 192 frame property. NativeMix takes any size.
The buffer was fixed at two bursts, 4 ms there, with nothing to recover from
underruns. Use Oboe's LatencyTuner, which starts at two bursts and adds one
per underrun, and log the underrun count when the stream closes.
Also take the lock when clearing the error and info strings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Android batches touch moves and joystick axis events until the next UI
vsync, up to a frame of latency. On API 30+, ask for them unbatched.
Non-pointer sources only apply to the focused view, so the surface view
now stays focused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A press and release that both land between two sceCtrl samples were
lost. Now the next sample sees the button pressed. Idea from Azahar.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- various bug fixes
- allow disabling enet when creating relay and adhocctl server objects
- pdp_peek_next_size can now be used to check whether a pdp session has no packet or an upcoming 0 size packet
The game runs without its disc there, so a state saved from it would keep that broken
environment. Saves are refused, and so is --save-state together with a state to boot.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The count is opens of files missing from the disc, and reads and seeks on handles whose file
was dropped as the state loaded, from the load on. It tells how far a game booted without its
disc has strayed from its usual path. headless_bench.py takes bare .ppst files and directories
of them (--state-dir), and reports the count with the timings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Running a .ppst boots a placeholder program, with the disc ID and boot type peeked from the
state, then loads the state over it. An ISO game gets a disc with nothing on it, which the
mounts now load into by name. Meant for measuring performance with states that can be kept
without the games; the app still refuses to boot a state.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SaveStart (still version 6) also stores whether the game was booted from an ISO, a PBP, an
ELF or a disc directory. PeekBootInfo decompresses only the start of a state to read them with
the disc ID, so a boot from a state alone can be set up before the state is loaded.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mounts were stored by position with no size, so adding, removing or reordering one broke
every older state, or silently fed sections to the wrong filesystem. Now a load pairs them by
prefix, skips a mount it doesn't have, and leaves one the state doesn't have as it is. Older
states load as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixed width, at the start of the first section, so the game a state belongs to can be read
without loading it, to boot from a state alone. A load into a different game is logged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The icon-with-text drawing helpers and the large list dimensions move
with it, exported since DirButton uses them too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- sceRegGetKeys looped to the requested count instead of the category's,
reading past the key list, and its size check could overflow.
sceRegGetKeyValue wrote an INT to a buffer smaller than 4 bytes, which
the ByName variant already refused.
- Savestates now wait for async IO even while stepping, when a plain
sync returned at once and a read in flight could land in the loaded
memory.
- A bad POPS layout index in a savestate fails the load instead of
indexing past the table.
- sceNpDrm's EDATA check stored ReadFile's s64 result in a size_t, so an
error passed the length check. The PGD ioctl handed uninitialized
stack bytes to pgd_open when the file was short.
- A negative chnnlsv key length from the guest context indexed before
the key buffer.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- The ringbuffer's packets * packetSize went straight into the media
engine's allocations. It's clamped to what a savestate can hold, and
the put callback's return value is clamped before it sizes the
validation demuxer. A ringbuffer with no packets no longer divides by
zero.
- sceMpegAvcCsc with a range starting past the frame gave a negative
width. sceMpegBaseCsc* reject a stride that overflows the size math,
and the MJPEG output size could overflow 32 bits.
- MpegDemux clamps the header's stream offset, and doesn't push PES
payload past what's been read. read16/read24/readPts relied on the
unspecified evaluation order of | for their byte order. A negative
stream offset made every later savestate fail to load.
- Atrac: DecodeForSas clamps the split packet so the second copy stays
in the stack buffer. A zero frame size from the file is rejected
before it's divided by. decodeTemp_ is reallocated when SetData
changes the channel count. The standalone decoder only has buffers
for two channels, so it clamps the count it's given.
AtracSasBindContextAndGetID indexed with a negative ID and
dereferenced a missing context.
- Psmf's player tempbuf path could read past the 64KB buffer from the
stream table or EP map offset.
- sceMpegDelete didn't forget the context's PMP state, so a new context
at the same address skipped the PMP codec setup and crashed.
Releasing an Atrac ID or setting its data now waits for a SAS mix in
progress, which can be decoding from it on the SAS thread.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
pspautotests net/adhoc/sizes measures, roughly: PdpSend takes 50 us plus
20 ns a byte (to a peer, to everyone, to nobody or to itself), PdpRecv
of waiting data 15 us plus 11 ns a byte, PtpSend 95 us plus 70 ns a
byte, PtpRecv 25 us plus 12 ns a byte, and PollSocket 20 us plus 1 us a
socket. We charged 10-50 us whatever the size, and nothing at all for a
PdpSend to a MAC nobody has.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Logging after the panel closed showed the app still active but no key window,
so keys beeped until the window was clicked. Making the window key in the
completion handler doesn't stick (the sheet is still attached then), so do it
on the next runloop pass. The Dock bounce isn't needed, and only
[NSApp activate] is kept, for the case where the app did lose activation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keyboard focus still didn't come back after a panel closed. The panel runs
out of process, and SDL's own Cocoa file dialog works around the same thing
by activating the Dock and then the app again, so do that too.
SDL handles input for its windows itself, so the sheet didn't stop clicks
and keys from reaching the UI under it. Drop presses and motion in
ProcessSDLEvent while a panel is open.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>