Both took their key through GetPointerWriteOrException, which raises a
memory exception for 0, though the code behind them treats a null key
as no key. The VSH's Saved Data Utility checks PARAM.SFO hashes without
one, and emulation stopped there.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The VSH's Saved Data Utility opens msstor0p1:/ to ask whether a stick is
in and writable (ioctls 0x02125008/9) and what type it is (devctl
0x02125802), and gave up quietly when the open failed. msstor paths now
open as a node with nothing behind it, answering those as msstor.prx's
handlers and a PSP do (pspautotests io/msstor/msstor): in, writable, type
0x40, without rescheduling, and 0x80220183 for a short output buffer.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Inserting and ejecting a disc there adds and removes the umd0:/umd1:/
umd:/disc0: mounts, and MetaFileSystem's state pairs sections with mounts
by position without recording which disc was in. A state could then fail
to load, or load one disc's open files onto another. The pause screen
already hid its save buttons for vshmain.prx; the save hotkeys, rewind,
the ImDebugger and the WebSocket debugger all went around that. The check
is now in SaveState itself, and rewind snapshots aren't taken. Verify
still runs, since it reads back with the mounts as they are.
A proper fix records the mounted image in sceUmd's state and remounts it
on load.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Video textures were clamped to a 480x272 frame on both axes, so filtering
can't pull in whatever lies next to the frame in memory. The VSH's UMD
Video player converts 480 rows, into a 256 or 512-wide texture it uses as
a ring that its strips wrap around: the rows past 272 smeared, and the
wrapped strips were stretched. The clamp now covers the rows written at
the texture, and the columns the strips written into its first row cover.
An axis the frame fills keeps the game's wrap mode. A game's 480x272
frame is clamped as before.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The descriptor's +0x08 and the second output pointer (pspautotests
video/mpeg/cscavc): with a second buffer the fields are split and packed,
even rows into the first and odd ones into the second, and +0x08 leaves
the odd field out; without one, rows stay in place and +0x08 skips the
odd ones. The VSH's UMD Video player converts the top field of its 720x480
pictures this way, and our 480 full rows ran over the UI library's VRAM
allocator. Each field is converted with its own chroma rows, as
interlaced 4:2:0 has them.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The VSH's video player outputs sound through vshbridge.prx, which imports
sceVaudio under its driver name, so every sceVaudioOutputBlocking went
nowhere. Register the same functions as sceVaudio_driver. The player
reserves its channel with sceVaudioChReserveBuffering, which in vaudio.prx
is sceVaudioChReserve with a flag set; treat it as that.
It also polls sceDisplay_driver_996881D2 through vshbridge.prx, and skips
setting up its picture output when it's 0. display_02g.prx's init sets
that from the LCD panel type in IdStorage (0x0103000B for type 1,
0x0103020F for type 2), so it's never 0 on a PSP.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The VSH's UMD Video player reads what the ME writes beside each decoded
picture, which games' mpeg.prx only passes through (pspautotests
video/mpeg/avcdetail, recorded on a PSP):
- Given a 0x540-byte buffer at ctx+0x38, as mpeg_vsh.prx provides in the
modes the player uses, the ME points each picture descriptor's +0x24
and +0x28 at two 0xA4-byte records for it. The first is the picture's
SEI record: cpb_removal_delay at +0x18, dpb_output_delay at +0x1C, the
pic_struct at +0x20 (which the player picks a duration with), and flags
of 7 for a keyframe and 2 otherwise. sceVideocodecGetSEI writes the
record of the last access unit decoded to the buffer at ctx+0x50.
- A descriptor's +0x20 is 1 for a keyframe. The player drops every
picture after starting or seeking until it sees one.
- The 0x64 bytes at ctx+0x30 carry the VUI timing (num_units_in_tick at
240000 ticks a second), which the player checks against 5005 for film.
All of it comes from what ffmpeg's H.264 parser reports per access unit,
which AvcDecoder now hands back with each picture.
Each picture also goes into the next of the four frame stores, as the ME
rotates through its own: the player converts a picture in strips spread
over its next few frames, while decoding goes on.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
While the XMB is running, the pause screen offers "Insert UMD" with the
drive empty, opening the Switch UMD game picker, and "Eject UMD" with a disc
in. The XMB notices the change through its drive callback, as it does over
the debugger's game.umd.insert/eject. Games keep "Switch UMD".
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
In mpegbase_260.prx, sceMpegBaseCscInit calls this with its built-in
22-byte table. The VSH's video player imports it directly. We keep
converting with the standard matrix, and log the one passed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Four 720x480 pictures take 2MB, all of the 2MB ME memory we modelled, so
UMD Video's decoder never got its frame buffers. mpeg.prx accepts ME
addresses up to 4MB. What we allocate still starts at 1MB, so games' videos
get the same addresses as before, and a savestate from before grows its
memory in place.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A UMD Video image can now be inserted, and the drive's disc type (devctl
0x01F20001) comes from what's on the disc: PSP_GAME, UMD_VIDEO and UMD_AUDIO
each set their bit. With a video disc the XMB moves to the Video column,
shows the disc with its icon and background, and hands it to the firmware's
video player when selected. sceUmdGetDiscInfo still tells games they're on
a game disc.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Headless forces every library onto our HLE for anything that isn't a disc,
so pspautotests run without a firmware dump, and a --vsh boot fell under
that too. The VSH is firmware, and gets the same modules as in the app:
with our HLE sceMpeg, the exports of the real mpeg_vsh.prx were dropped and
the video player called into stubs.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A block that jumps to an invalid address is usually code that never runs,
such as a call through an import that isn't linked yet, and the error-level
"Jump to invalid address" sent people chasing it. A jump that actually runs
still fails as before.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The XMB, booted without a game, now starts with an empty drive: when
vsh_module loads with nothing at disc0:, the drive is marked empty, and
activating it tells the shell's drive callback (SceVshMediaDetectUMD)
there's no disc. Activation used to report a readable disc regardless,
so the shell asked for its type and showed "The disc could not be read."
The disc type devctl (0x01F20001) answers 0 without a disc, as JPCSP
does.
game.umd.insert (path) mounts a disc image into the empty drive and
calls the drive callback as inserting one does; game.umd.eject takes it
out. UmdMount() is UmdReplace() without its need for a disc already in.
Inserted into the 6.61 XMB, Daxter shows in the Game column with its
icon, background and animated preview, and launching it boots the game
to its title menu.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
-i, -j, -r, -J, --ir and --jit-ir are gone. -r was also --root's short form,
and --root won, so -r took the next argument as the root path and nothing
booted (test.py -r failed every test).
-i, -j and -J now fail with an error pointing at --cpu= instead of quietly
running on the default core. Docs and test.py updated.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Fixes#20287
The menus and manuals flip from a GE finish callback and only sleep 100us
per frame, so the GE's drawing time sets the frame rate. Ours is instant,
so they ran at 400-800fps and the host couldn't keep up. Hook the end of
the per-frame update and wait for a vblank unless one has already passed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A replay looks compat.ini up by the ID in the dump's header, so PS1 games'
dumps now get the PS1 emulator's settings when replayed with them
(--dump-compat). The file name keeps the game's own ID.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The PS1 emulator draws sprites with their corners on pixel centers (x.5) and
their texture coordinates on texel centers. At 1x that covers whole pixels,
but above 1x it covers half a pixel more on the right and bottom and reads
the neighbouring texels, which shows as seams between tiles. With compat.ini's
SnapHalfPixelSprites, such sprites and their texture coordinates are moved back
half a pixel, which draws the same at 1x. Texture coordinates are also nudged
a 64th of a texel in, as a scaled sprite can sample right on a texel edge,
where interpolation can fall short of the texel the GE reads (the emulator's
video unpacking does).
Set for PS1 games (POPS00000).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Where the backend has no logic op and no framebuffer fetch, the logic op is
done in the shader against a copy of the framebuffer taken when the batch is
drawn, so draws batched together don't see each other. The PS1 emulator puts
each 24-bit video frame together by ORing passes into the same pixels, so it
lost all but the last. With compat.ini's SeparateLogicOpDraws, such draws are
flushed one at a time, and the rectangles of one draw are split, as they can
overlap too.
It costs a framebuffer copy per draw. Set for PS1 games (POPS00000).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
compat.ini's NativeResolutionFramebuffer (an address) keeps that framebuffer at
1x whatever the render resolution, for one that holds data read back texel by
texel: the PS1 emulator unpacks 24-bit video into one three bytes per pixel.
Set for PS1 games (POPS00000).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A framebuffer the game always keeps at one size, and also reads and writes
outside the area it draws: the PS1 emulator's VRAM, a 1024x512 16-bit surface
it copies between and reads back from the CPU. compat.ini's FixedFramebuffer
("address,width,height") makes it that size instead of one estimated from the
drawing, initializes it from memory even when the first draw doesn't look like
one, and uploads into it without filtering, since what goes in is data that's
read back (#22686 saw FFVI crash on bilinear uploads at 4x).
Set for PS1 games (POPS00000).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
They're all run by the firmware's PS1 emulator, whose workarounds don't
depend on the game, so compat.ini settings for it go under POPS00000
(letters and digits like other IDs). Only the compat.ini lookup uses it:
savestates, save data and everything else keep the game's own ID.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Only the CPU thread runs IRInterpret, so it doesn't need to be thread-local.
Volatile keeps the compiler from dropping or sinking the stores, which
nothing but the fault handler reads.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
HandleFault only handled faults in JIT code, so a game's bad load or store
under the IR interpreter crashed the app. Now IRInterpret marks itself
(a thread-local, cleared around HLE calls, replacements and the MIPS
interpreter fallback) and the handler steps over the faulting instruction,
reports the exception and stops the core, or just ignores the access when
IgnoreBadMemAccess is on. If the instruction can't be decoded it still
crashes, as before.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Same as Android: rotation rates go to NativeGyroscope, and SYSPROP_HAS_GYRO shows the "Use gyroscope" option.
Accelerometer data is now scaled from g to m/s^2, like Android's. Tilt normalizes it, but the gyro fusion only trusts the accelerometer when it reads about 9.81, so in g it would never correct drift.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sony renumbered the kernel NIDs all at once at a few releases, so the
table now has a column per generation (6.60-6.61, 6.30-6.39, 6.00-6.20,
5.01-5.55, 3.95-4.05) and a row per function, instead of an entry per
old NID. A new firmware generation is a new column.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
3.9x's emulators set the block size a few instructions before the jal,
not next to it. Still a single site in every emulator from 4.05 to 6.61.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The user modules started after the manager now come from the firmware's
own boot configuration (pspbtcnf.bin's POPS mode), since they differ by
version: 4.05's pafmini.prx needs heaparea2.prx first. 6.60's also
lists common_util.prx, which wasn't started before.
4.05 renumbered the kernel NIDs again. Its aliases are checked like
5.55's, and the sysreg ones by what they do to the registers: the call
sites got five of those wrong. Its manager allocates from a pool with
the kernel's sceKernelAllocateFpl, which stays unresolved for kernel
modules (see ThreadManForKernel), so only the manager's import gets the
user one. scePower_driver gets 6.60's clock frequency NID, which 6.20's
and 4.05's old one now map to.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
They import by 5.55's NIDs, and their managers are 5.55's at other
layouts. 5.0x's scePopsManLoadModule only loads the emulator when its
second argument is nonzero (later ones skip it on 2), so the layout says
what to pass.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
5.55's manager is an older design: the kernel hands it the version key
(there's no license probe to replace), and the emulator opens the game
through a manager export that first looks up its license. A converted
PBP has none, so that lookup now returns success, as the MAC checks do.
Store games still need 6.00 or later, so the 5.55 layout only matches
for converted PBPs.
Its code changed too much to map every import by call site, so its NID
aliases are only the ones used here, each checked by its call site (and
the sysreg ones by lowio.prx's code).
The emulator's disc read call is now found by the block size next to
it, which 5.55's pops.prx passes from the other side of the jal: the
same single site as before in every 6.x emulator.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
6.10's popsman.prx imports by 6.20's NIDs, and has the same code at
another layout.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
6.20 renumbered the kernel NIDs again, so it gets aliases of its own,
mapped through 6.30's from the call sites in popsman.prx. Its manager has
the same code at another layout, and its scePopsManLoadModule has a
bigger frame (it also picks a model-specific emulator module), so the
layouts now carry that function's frame size for the check.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The manager's code used here is the same from 6.30 on, but 6.30/6.31's
sits at other offsets. The offsets are now a table of layouts, picked by
the code at them when the manager loads, and kept in the savestate (the
patches change that code). With the NID aliases, Crash Bandicoot plays
the same on 6.30, 6.31, 6.35, 6.36, 6.37 and 6.39 as on 6.60/6.61.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sony renumbered the kernel NIDs between firmware versions, so a module
from 6.3x imports the functions we implement under 6.60's NIDs by other
ones. Rather than a second table entry per function, an alias table maps
the older NID to the implemented one, and GetHLEFuncIndexByNib() falls
back on it. Savestates store the table index, which this doesn't touch.
The 6.30-6.39 entries cover what popsman.prx imports (the same NIDs in
all of them). They come from matching the call sites in popsman.prx's
6.39 and 6.60 builds, whose code is the same, and agree with matching
the exporting modules' code where that's unchanged.
6.30/6.31's popsman.prx also takes the semaphore the main CPU and the ME
share through sysreg calls, which 6.60's does inline: implemented here as
lowio.prx does them.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The game browser left out EBOOTs with the PS1 category, which couldn't
run. Now they're listed where the Media Engine can be emulated
(Memory::CanCreateMESpace). And as a PS1 game's PARAM.SFO has no
DISC_TOTAL, the browser took it for homebrew and gave it a made-up ID
(SCUS00580 for Crash Bandicoot's SCUS94900): its own is kept now.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
How the firmware's POPS manager and emulator are booted, what stands in for
the Media Engine, what's patched in them, the rendering changes they need,
and what's not done yet.
Both as CFW's popcorn does, for any EBOOT. The emulator only shows the
manual when a name the manager hands it matches one it keeps, and popcorn
makes that check pass.
It only takes an 80x80 PNG as ICON0. A converted EBOOT without one hangs,
and one with a 144x80 ICON0 (the size of a PSP game's) crashes it. Such an
ICON0 now reads as a blank 80x80 PNG: its size and its read are replaced.
Both are found by their code, which is the same in pops_01g and pops_02g.
In impose.prx (6.60), it returns a flag impose's thread sets each pass while
HOME is held, but only if impose leaves HOME to the application. Its init
does that for application types 0x300 (POPS) and 0x400, and
sceImpose_9DBCE0C4 changes it. So games get 0, and the PS1 emulator, which
polls it to open its in-game menu, gets HOME.
It was unimplemented, so it returned SCE_KERNEL_ERROR_LIBRARY_NOT_YET_LINKED.