The percentages were converted back to frame rates whenever any dialog
closed with OK. That overwrote the values Restore Defaults had just set,
and the integer rounding drifted unchanged values down (100 fps became 99
on any unrelated change). Restoring defaults also had the old reporting
choice written back on exit; it's now re-read too.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The saved tab was selected before the Search tab was added, so the index
was out of range and no tab was created. Select it after adding Search, and
create the current tab anyway if the saved one no longer exists (Search is
hidden on small screens).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
None is -1, but the developer tools list had it last at index 8, which is
CENTER. Building the tab clamped None to Bottom Left, and picking None
showed notifications centered instead.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Cancel on the "missing file browser" popup opened the help page anyway,
and More info closed the memstick screen as if a folder had been picked,
because any child dialog finishing with DR_OK did that.
- When switching to the new folder failed, the error was set but never
shown. After a move, the screen was left with no buttons.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Importing showed "File not found" when the file existed and the import
failed, and the other message when it was missing.
- The game ID check returned without closing the cheat file.
- Closing the download popup with Back started a download of an empty URL.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Both worked out the ID themselves with a copy of the homebrew rule that
lacks the PS1 exception added to GameInfoCache in 7ced173c17. For a PS1
EBOOT in PSP/GAME, Game Settings then edited the global config even though
Create Game Config had made a per-game one under the real ID.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
CreateSettingsViews and CreateContentViews both cleared all the view
pointers, and which runs first depends on orientation. In landscape, Install
and Play were never disabled during an install; in portrait, the done and
error notices never showed. Reset them once in BeforeCreateViews instead.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The static string was only initialized on the first call, so the text
editor (used where there's no native input dialog) showed whatever was
typed the first time, even after picking another server from the list.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The static analysis cleanup in d6ebfed432 dropped the negation, so the CPU
name was only shown when it was "Unknown".
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The format was set on the wrong slider (alternative speed 2, which already
had it).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Building a settings tab should only create views, so that the tabs can
later be described as data and searched without being shown.
- Don't reset the MSAA level when the device has none. The GPU already
falls back to no MSAA (UpdateMSAALevel), and the reset overwrote the
setting, including per-game ones.
- Read the reporting state in the constructor. It was only read when the
System tab was built, but always written back on exit, so leaving
settings without opening System turned reporting off.
- Move the Windows memstick location probe (which writes a temp file next
to the exe) out of the System tab, and keep the checkboxes' enabled
state in members instead of poking at the views.
- Show the Android/UWP memstick path through a callback instead of a
copy made at build time, and drop a USB path computation whose result
was never used.
- Make the reliable-UDP checkbox follow the UDP setting directly instead
of recreating all views when it changes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The game shares Resistance: Retribution's effect engine, smoke trails and all: a trail gets a
new node on every update while the nodes expire by the effect's clock, so the faster the game
runs the more smoke there is. When the tank explodes, the smoke grows the display list past its
500KB buffer at a steady 60fps (an overclocked CPU gets there), the list runs into the heap and
the game crashes on a bone array it overwrote. Hook the same two functions as in Resistance:
nodes at most every 1/30 of the effect's clock, and the overflow guard as a backstop. At 428MHz
the list now peaks at 351KB, against 512KB and a crash before.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Booting the VSH with a disc now stays in the XMB, with the disc in the
Game column, rather than starting the game right away.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
KeyValue gets an optional pointer to a g_Config bool, which the key
reads as 0/1. The setting wins over guest writes: since category_version
is kept low, the VSH upgrades the registry at every boot and rewrites
every key with its default.
/CONFIG/SYSTEM/umd_autoboot reads the new SystemParam VshUmdAutoboot
(default true, as before). Turned off, --vsh --mount stays in the XMB
with the disc in the Game column instead of starting it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
--mount ISO used to be headless-only, and EmuScreen always cleared
CoreParameter::mountIso. Pass it from the command line into the first
EmuScreen, which keeps it across resets and drops it when another game
is booted.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The XMB passes its state as vshmain args (0x400 bytes) when it launches
something. We now keep them and start vshmain with them when the game
exits, so it comes back where it left off instead of booting fresh: no
opening, the launched item selected, and no UMD auto-start. vshmain's
module_start only copies them in when they begin with their size and
0x20, which the kernel has to fill in.
A launch that fails to load (like the Video Demo Disc's game part, whose
EBOOT.BIN is 2384 zero bytes) now goes back to the XMB too, instead of
stopping emulation. Before the args were handed back, this looped: the
fresh XMB auto-started the disc again.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
pspnet_apctl.prx exports the internal handler, scan and BSS functions to
the VSH under sceNetApctl_lib (as listed by its 6.61 export table). We had
them, but only under sceNetApctl, so the XMB's netconf plugin got an
unresolved import and the browser and online manuals stopped at 8002013A
before their Network Connection screen.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
On a PSP, sceKernelExitGame in a game the XMB launched goes back to the
XMB. Ours ended the session. In a session booted as vshmain.prx, an exit
from anything but the VSH itself now LoadExecs vshmain.prx again; the
shell then plays its opening and comes back on the Game column.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>