Commit Graph
47585 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5 6b82202a2d PrxDecrypter: add the 3.0x, 3.1x and 3.5x sceResmgr keys too
Those generations predate the per-model split and have a single
flash0:/vsh/etc/index.dat. 3.30 through 3.52 now reach an interactive XMB;
3.30 to 3.51 share 3.52's key.

3.03's mesg_led.prx is older than keys330_1, so its table was confirmed against
keys300_1 and keys280_1 instead - both match byte for byte.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-10 12:46:18 -06:00
Henrik RydgårdandClaude Opus 5 f32c015659 PrxDecrypter: add the sceResmgr keys for the 5.0x, 6.0x and 6.3x XMB indexes
flash0:/vsh/etc/index_XXg.dat is the index of what the XMB shows, and each
firmware generation tags it with a key per PSP model. We only had the 6.6x
triple, so 5.03 through 6.39 decrypted nothing, drew no icons, and gave up with
the red error screen.

The keys come out of each firmware's own mesg_led_XXg.prx, whose tag table is
24-byte entries of tag plus 16-byte key. Extracting the 6.6x triple that way
reproduces the three keys already in this file byte for byte, which is what
establishes the layout.

5.03, 5.50, 5.55, 6.00, 6.20, 6.31 and 6.39 now reach an interactive XMB.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-10 12:44:47 -06:00
Henrik RydgårdandClaude Opus 5 0054d30b1d HLE: cover the rest of the NIDs the VSH uses across firmware versions
sceKernelLoadModuleVSH, sceKernelGetModel and sceImposeSetStatus each appear
under a different NID in 3.95/4.05, 6.00/6.20 and 6.31/6.39. Without the load
one those shells started no plugins at all and sat on a black screen.

Identified the same way as the 5.xx set: for each firmware, the modulemgr export
with sceKernelLoadModuleVSH's callee set, and the vshbridge export whose body is
the user-level check 6.61 wraps sceKernelGetModel in - which is also the only
SysMemForKernel import those shells actually call.

3.95 and 4.05 now reach an interactive XMB.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-10 12:44:47 -06:00
Henrik RydgårdandClaude Opus 5 3bdc4380d8 sceReg: report a registry category_version every VSH accepts
The dumped 0x66 comes from a 6.6x PSP, and 5.50's shell rejects any schema
version above 0x58 as a corrupt registry - it drew its whole XMB and then
replaced it with the "settings are corrupt, press O to repair" dialog.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-10 12:44:47 -06:00
Henrik RydgårdandClaude Opus 5 41509a9295 sceReg: don't let a nested registry close drop another opener's categories
We hand every sceRegOpenRegistry the same handle 0, so closing one used to wipe
every open category - including ones a still-open registry handle owns. On
hardware each open gets its own object and they don't interfere.

The VSH's alarm scan does exactly this: it holds /CONFIG/ALARM open, then opens
and closes the registry once per alarm slot, and found its own category gone by
the end. Count the opens and only clear on the last close.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-10 12:44:47 -06:00
Henrik RydgårdandClaude Opus 5 56b6c3af90 PrxDecrypter: add the 5.xx sceResmgr key
flash0:/vsh/etc/index_XXg.dat is the index of what the XMB shows, and 5.50 tags
it 0x0B2B11F0, which we had no key for - so the shell decrypted nothing, drew
no icons, and gave up with the red error screen.

The key is read out of that firmware's own mesg_led_02g.prx, whose tag table is
24-byte entries of tag plus 16-byte key. The two neighbouring entries hold
keys330_1 and keys505_a byte for byte, which is how the layout was confirmed.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-10 10:48:32 -06:00
Henrik RydgårdandClaude Opus 5 1a3f14acd0 HLE: add the 5.xx NIDs the VSH of that era imports
sceKernelLoadModuleVSH, sceKernelGetModel, and four of sceImpose_driver's calls
already have implementations; 5.xx just asks for them under different NIDs, so
the 5.50 shell got nothing back. Each one was identified by disassembling that
firmware's own module and comparing the body against 6.61's, where the same
function is exported under a name - the pairs are instruction-for-instruction
identical apart from context-struct offsets.

GetModel was the one that mattered most: unresolved, vshbridge handed the shell
a garbage model number, and it went looking for PSP-3000 resources on a dump
that has none.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-10 10:48:32 -06:00
Henrik RydgårdandClaude Opus 5 765d5bfbcf headless: make --nand and --screenshot-save work
--nand had a field and an ApplyToConfig branch but nothing ever parsed it, so it
was silently ignored.

--screenshot-save only fired when a GE replay finished or a test used the
EMIT_SCREENSHOT devctl, and even then only under --compare. Anything else - a
game, or --vsh - ran to the timeout and wrote nothing. Capture the display at
the end of the run when nothing else did, which is what makes it usable for
looking at what a booting system actually has on screen.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-10 10:48:32 -06:00
Henrik Rydgård a52b11aa2e Merge pull request #22275 from hrydgard/scemp4-review-fixes
sceMp4: Read the effective flags (not the config), don't reload the modules by accident
2026-09-09 19:32:10 -06:00
Henrik Rydgård 9d2ebd5a3a Merge pull request #22274 from hrydgard/headless-debugger-run
headless: say when we're waiting at the entry point, and add --debugger-run
2026-09-09 17:13:04 -06:00
Henrik RydgårdandClaude Opus 5 95d03449b3 sceMp4: don't reload the modules, read the effective flags, check four bytes
Three from a read-through of the sceMp4 firmware-module path:

Clearing the module UIDs when the game says it's done with the MP4 module
didn't unload anything - it only meant the next load brought in a second copy
of libmp4.prx and mp4msv.prx, some 220KB at the top of user memory each time.
Keep them for the boot instead; __UtilityInit clears them per game, which is
the point at which they really are gone.

The flag test read g_Config directly, so it ignored the very fallback
CheckDisableHLEAvailability computes when the dump is missing - it would go and
try to load modules that aren't there while import resolution had correctly
stayed on HLE. It also ignored a boundary restored from a savestate.

sceKernelGetModuleGPByAddress checked one byte of the pointer it writes four to.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-09 16:59:21 -06:00
Henrik Rydgård 9356a3acbc Merge pull request #22271 from hrydgard/firmware-screen
Add a Tools/PSP Firmware screen, improve compat with older firmwares
2026-09-09 16:50:13 -06:00
Henrik RydgårdandClaude Opus 5 29c5d1baf1 Offer to install the disc's firmware updater from the game context menu
The game screen already reports which updater a disc carries, but there was no
way to act on it - the only route to installing one was picking an updater PBP
out of the browser. The unpacker already handles being pointed at a disc, so
this just wires the menu entry to it.

Shown only when the disc actually has one, and not while a game is running:
installing wipes the NAND that game has mounted.

InstallUpdateScreen takes the archive size now, because for a disc the size of
the file it was handed is the game's and says nothing about the firmware.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-09 16:25:13 -06:00
Henrik RydgårdandClaude Opus 5 63eb3d5b21 Boot the VSH on every firmware, 1.50 through 6.61
Backwards from 5.01, one release at a time, against all 39 versions that ship
on a disc plus the download-only 6.61. Nothing here is an offset - it's almost
entirely Sony renumbering the kernel *_driver NIDs, which sends an import we
mean to HLE into the real firmware module instead.

Four more NIDs each for sceRtc_driver/sceRtcSetAlarmTick and
sceHprm_driver/sceHprmReadLatch, covering 1.50 up. The rtc one is what parked
every thread on a SceSysconSync semaphore; the hprm one runs once a frame, so
unresolved it was most of the boot log. Also sceImposeGetParam/sceImposeChanges
(1.50 - 2.xx) and sceKernelLoadModuleVSH (1.x, which is how the shell loads its
own plugins - unresolved it got module id 0 and StartModule failed).

sceRtcIsAlarmed had to be implemented too; it returns 0, as in JPCSP. As a null
entry it returned LIBRARY_NOT_YET_LINKED, and the 3.0x-3.5x VSH read that as
"ask the hardware instead" and went back to blocking on syscon.

Two structural findings:

- Up to 4.05, scePaf's heap allocator is a separate heaparea1.prx that paf
  imports as scePafHeaparea. Load it when it's there. Its pool pointer needs
  the same pre-fill paf's does, at gp - 0x7FCC rather than gp - 0x7E88.
- 1.50's vshmain.prx declares no module attributes at all - PSP_MODULE_VSH_MODE
  only appears from 1.52 - so the whole VSH bootstrap was being skipped. Accept
  the module name too.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-09 14:43:39 -06:00
Henrik RydgårdandClaude Opus 5 480c5e44e2 Boot the VSH on every firmware from 5.01 up
The blocker below 6.60 wasn't offsets, it was that Sony renumbered the kernel
*_driver NIDs between versions. A function we HLE under its 6.6x NID is a
stranger on an older build, so the import lands in the real firmware module
instead - and that's where it goes wrong:

- sceRtc_driver sceRtcSetAlarmTick. Without the HLE the VSH's alarm call ran
  the real rtc.prx, which called on into syscon.prx and blocked forever on a
  SceSysconSync semaphore. That was the whole "stalls with every thread parked"
  symptom; the tell was a fourth SceSysconSync waiter a healthy boot lacks.
- sceHprm_driver sceHprmReadLatch, called once a frame - so before this an
  older firmware's 12-second boot logged ~20000 lines of one unresolved import.

Three extra NIDs each, found by disassembling the module from both firmwares
and matching on the address of the user-mode export whose NID never changed
(sceRtc/0x7D1FBED3, sceHprm/0x40D2F9F0).

5.55 additionally needed two PRX decryption keys we didn't have (0x4C941AF0
and 0x4C941BF0) - without them none of flash0:/kd decrypted and the shell came
up with no drivers behind it at all.

Checked one release at a time against every version that ships on a disc, plus
6.61. 4.05 and below still die on a null write inside vsh_module.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-09 13:40:37 -06:00
Henrik RydgårdandClaude Opus 5 caf3ed4335 Firmware screen: don't list details a fonts-only install doesn't have
With no kernel modules there's no firmware to speak of - it's the fonts we
pulled off a game's disc - and rows reading "Kernel modules: 0 / XMB: No"
say nothing. Keep the font count and the size.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-09 12:56:43 -06:00
Henrik RydgårdandClaude Opus 5 b21e40a53f Boot the VSH on firmware 6.60 too, and stop patching builds blind
6.60 ships byte-identical paf.prx and vshmain.prx to 6.61 - all 6338 + 669
functions disassemble the same - and boots to an interactive XMB, so let
FirmwareVersionSupportsVSH accept it. That matters because no UMD carries 6.61
(it was download-only), so 6.60 is the best a disc-installed firmware can be.

The two module patches were hardcoded offsets from the module base applied to
any module of the right name, which is quietly wrong on any other build:

- The scePaf heap arena slot moves with every build (0x18CCD8 on 6.00 through
  0x18D728 on 6.60/6.61) but sits at gp - 0x7E88 in all of them, so find it
  that way. On its own this turns an immediate SIGSEGV inside scePaf into a
  clean stall on 6.00 through 6.39 - they still don't reach an XMB, they get
  stuck in sceVshBridge_Driver instead.
- The vsh_module alarm-category offset has no such anchor, so check the word
  there is the one the patch was derived from. On 6.20 and 6.00 it's ASCII
  string data - the unconditional write was corrupting a string table.

Also resolve the per-model kernel drivers (memlmd, loadexec, wlanfirm) to the
model being emulated. They were asked for as _01g, which a firmware unpacked
for a single model doesn't have - and our own updater unpack defaults to 02g.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-09 12:56:34 -06:00
Henrik RydgårdandClaude Opus 5 bb96802f72 Fix a heap overflow decrypting a PRX whose size we had to guess
KIRK CMD1 writes header + data_offset + align16(data_size) bytes into outbuf,
and all three come out of the header the decrypter just decrypted, not from the
caller. The SHA1 check doesn't bound them - it only covers the header, so it
passes just as happily for a block that's been cut short.

The PSAR walker has to guess how long an updater's second block is (nothing
records it, so it tries the sizes real updaters use), and a wrong guess sent
KIRK off the end of the buffer: unpacking a firmware crashed roughly half the
time, on every version and disc I tried, depending on the heap layout.

Bound the write against the size the caller gave us, in all six decrypt types.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-09 12:56:17 -06:00
Henrik Rydgård 304eef9b16 Merge pull request #22270 from hrydgard/reverb-tuning
Reverb effect tuning
2026-09-09 12:26:43 -06:00
Henrik RydgårdandClaude Opus 5 57e96b2d81 Add a PSP Firmware screen under Settings > Tools
Shows what's actually in PSP/NAND - nothing, a fonts-only partial install, or
a full firmware with its version, build date and region - read from
flash0:/vsh/etc/version.txt, which is present both in a PSAR-unpacked install
and a NAND dumped off hardware.

Also offers to install an updater, erase the NAND, and launch the XMB, the
last one gated on FirmwareVersionSupportsVSH() since the module patches that
get vshmain.prx running are tied to 6.61's offsets.

Installing a firmware now erases flash0/flash1/ipl first - two firmwares can't
be merged, a file the new one doesn't have would linger and still get loaded.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-09 12:07:28 -06:00
Henrik Rydgård 6d945404d1 sceSas: Fix send/return levels for the reverb effects
The two fudge factors in the reverb path cancelled, which is why the
overall level felt roughly right.

- The send is accumulator * 0x20 >> 16, i.e. sample >> 2. We used >> 1,
  driving the reverb 6dB hot.
- The return is (evol * out) >> 11. We used >> 12, i.e. 6dB quiet.

Net level is therefore unchanged, but the reverb now runs at the level
the presets were designed around. That matters because the filter clamps
internally, so a 6dB hot input changes how the feedback path saturates -
worst on the presets with heavy feedback.

Also adds a slider in the imgui.
2026-09-09 11:05:40 -06:00
Henrik Rydgård 5402579a44 sceSas: correct the reverb presets
The reverb presets came from nocash's PS1 table. Six of the nine are
identical on the PSP, but three are not.

Also correct our linear interpolation expression: we were close but
our formula can produce an off by 1 at times.
2026-09-09 10:48:53 -06:00
Henrik RydgårdandClaude Opus 5 794263ed6a headless: say when we're waiting at the entry point, and add --debugger-run
--debugger sets startBreak, so the run sits at the entry point until a client
resumes it. A session that forgets to do that looks like a frozen game rather
than a paused CPU - all the way down to "ticks: 0" - so say so on the way up,
and add --debugger-run for the common case of wanting the debugger attached to
a run that just goes.

Also stop headless forcing HLE for the graduated modules. Those come out of the
game's own disc rather than a firmware dump - libpsmfplayer.prx and friends are
user libraries, always present - so forcing them to HLE made headless quietly
disagree with the app about which code a game runs. Tekken 6 plays its movie
through scePsmfPlayer, and headless was faking it, so the movie never reached
sceMpeg at all; now the disc's real psmfplayer drives the real mpeg.prx.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-09 10:15:02 -06:00
Henrik Rydgård e5b98b07e8 Merge pull request #22269 from hrydgard/me-re-tools
Headless RE tools: Add some options to disassemble ME images
2026-09-09 09:41:53 -06:00
Henrik Rydgård 280a5df557 headless: split the second stream out of an ME image, and accept a bare one
An ME image is two KL4E streams back to back: the code that runs at
0x88300000, then a blob the image decompresses to ME local RAM at
0x00101000, which is where its data segment lives. Decompressing only the
first left every global unaccounted for.

--re-decrypt now reports how much of the plaintext the first stream used
and writes the remainder to <out>.tail, and accepts an already-plain
KL4E/KL3E file so that tail can be fed straight back in.
2026-09-09 09:00:29 -06:00
Henrik Rydgård 1a1430e8ca headless: add --re-decrypt and --re-raw-base to the RE tool
--re-decrypt runs pspDecryptPRX() over a file and unpacks the KL4E/KL3E
stream behind it. This opens up flash0:/kd/resource/*.img, the images the
Media Engine actually runs: they are ordinary tagged containers (tag
862648D1, which PrxDecrypter already has a key for) with the ~PSP
signature blanked, so the normal module loader never touches them.

--re-raw-base analyzes --re-module as a flat code image at a given
address rather than as a PRX. The decrypted ME images are raw MIPS with
no ELF around them; the address they were linked for is recoverable from
their own jal targets (0x08300000 for meimg.img).

Also makes PrxDecrypter.h self-contained - PSP_Header is built from _le
types, so it needs Common/Swap.h rather than relying on the includer.
2026-09-09 09:00:29 -06:00
Henrik Rydgård d163551823 Merge pull request #22262 from NABN00B/stereo-settings
Show stereo shader options dynamically
2026-09-09 08:53:46 -06:00
Henrik Rydgård 3d59ef6f09 Merge pull request #22268 from hrydgard/revert-mipstracer
Revert exposing MIPSTracer to websocket
2026-09-09 08:46:19 -06:00
Henrik Rydgård f161e7f9f9 Revert "Expose the MIPSTracer over the WebSocket debugger"
This reverts commit ccdaa94f53.
2026-09-09 08:07:19 -06:00
Henrik Rydgård 25812b70fb Merge pull request #22266 from hrydgard/firmware-module-reset
sceUtility: forget injected firmware modules between games
2026-09-08 17:08:09 -06:00
Henrik Rydgård a333a93a7c sceUtility: forget injected firmware modules between games
Clear them in __UtilityInit, which runs per boot.
2026-09-08 16:57:43 -06:00
Henrik Rydgård 32a70d0d3d Merge pull request #22265 from hrydgard/vaudio-mp3-sample-counts
sceVaudioChReserve: accept the MP3 frame sizes
2026-09-08 16:14:13 -06:00
Henrik Rydgård 47c17b5589 Merge pull request #22264 from hrydgard/re-module-dump
Module dumper for reverse engineering
2026-09-08 16:07:37 -06:00
Henrik Rydgård c192ddcaa1 Merge pull request #22263 from hrydgard/hardware-verified-hle-fixes
Hardware verified HLE fixes
2026-09-08 15:53:35 -06:00
Henrik RydgårdandClaude Opus 5 ed9078b055 sceVaudioChReserve: accept the MP3 frame sizes
The channel took only 256, 1024 and 2048 samples, so a game that hands it MP3
frames got SCE_KERNEL_ERROR_INVALID_SIZE and no music. Dead or Alive Paradise
does exactly that from its music player: sceVaudioChReserve(1152, 44100, 2),
1152 being the MPEG-1 Layer III frame size.

The format check moves below the sample count check to match: the module
returns 0x80000104 for a bad count before it ever looks at the format, so a
call with both wrong got the wrong error out of us. The two error codes we
already returned are the ones it uses.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-08 15:52:10 -06:00
Henrik Rydgård cc44ba22d0 Android buildfix 2026-09-08 15:45:11 -06:00
Henrik RydgårdandClaude Opus 5 fa577eaa83 Document --re-module in AGENTS.md and docs/reverse-engineering.md
A tool nobody knows about is a tool nobody uses. AGENTS.md gets a short section
pointing at it, plus the two things most likely to be got wrong when reading
the output: that a function's arity can't be inferred from the registers it
reads, since MIPS code passes arguments through untouched, and that a finding
is worth much more when the comment says which module it came from.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-08 15:32:23 -06:00
Henrik RydgårdandClaude Opus 5 386ff2fc3d headless: add --re-module, a static reverse-engineering dump for one PRX
Loads a single module standalone - no game, no boot - and writes a report:
module header and segments, the export and import tables with NIDs resolved to
names through the HLE tables, one annotated disassembly file per function, and
a call graph as JSON.

Reuses the emulator's own loader rather than parsing PRXes a second time, so
decryption, decompression, relocation and import resolution can't drift from
what actually runs. Only enough of the system is brought up to load a module:
memory map, timing, HLE tables and the kernel allocators. Nothing executes.

Two things beyond a plain disassembly, both aimed at the questions that come up
when reading unfamiliar MIPS:

- lui/addiu (and lui/load) pairs are folded and reported as the address they
  form, which is how every global and constant table gets reached.
- Per function, a register evidence block instead of a guessed signature. A
  MIPS function that takes two arguments and passes the second one down often
  never reads it, so 'never read but live across a call' is reported as
  forwarded rather than quietly dropped from the signature.

SetForceRealModuleLoads() is needed because modules like sceAudiocodec_Driver
have no DisableHLEFlags bit and so can't be turned off the normal way - they'd
fake-load and there would be nothing to look at.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-08 15:32:14 -06:00
Henrik RydgårdandClaude Opus 5 fb976d4f61 Clamp the module function scan to the text range it already validated
The no-code-sections path validates textStart..textEnd, then derives its
actual scan boundaries from modinfo->libent/libstub without checking those
land inside it. flash0:/kd/sysmem.prx and loadcore.prx from a real firmware
dump put them tens of megabytes past the end of the text, so the scan walked
off into unmapped memory - a debug assert in Read_Instruction, and a pointless
134MB scan in release builds.

For a well-formed module every boundary is already inside the range, so this
is a no-op there.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-08 15:31:29 -06:00
Henrik Rydgård 1e2eaa1690 MacOS: Add shortcut to load the VSH 2026-09-08 15:26:26 -06:00
Henrik Rydgård 41144e3b35 Give the memory partitions the caller's privilege, not the syscall's
PPSSPP decided whether a caller was privileged with hleIsKernelMode(), which reports whether the
syscall being executed is itself a kernel-only export. That's a different question from the one
the hardware answers: on a PSP the privilege belongs to the calling module, and a kernel module
reaches sceKernelCreateTlspl through the ordinary ThreadManForUser NID like anything else. So a
kernel module asking for partition 1, 3 or 4 got ILLEGAL_PERM where a real PSP hands it over,
which the new threads/tls/kernel/partition test shows directly.

BlockAllocatorFromID now also accepts a caller whose thread belongs to a kernel module, via a new
__KernelCurThreadIsKernelMode(). It checks the thread's own attribute first and then the owning
module, because a kernel module's main thread isn't necessarily flagged kernel - the attribute
comes from PSP_MAIN_THREAD_ATTR, which needn't set it. That mirrors how sceKernelCreateThread
already works out allowKernel.

This only ever widens access, and only for threads belonging to kernel modules, so games are
unaffected - they run in user modules and see exactly what they saw before.
2026-09-08 15:18:02 -06:00
Henrik Rydgård a676eecba7 Tlspl partitions: 1-6 in both privilege levels, and document kernel-mode tests
threads/tls/kernel/partition now records the sweep from a kernel module, which settles the range
question the user-mode recording couldn't: privilege changes the permission check, not the range.
Partitions 1, 3 and 4 are ILLEGAL_PERM from user mode and fine from kernel mode, while 7 and up
are ILLEGAL_ARGUMENT either way. So the check goes back to a plain 1-6 for both, and the
kernel-mode carve-out from the last commit - which would have let 8 and 9 through - is gone.

The hardware doc gains a section on kernel-mode tests: what COMMON_KERNEL does, why the stock
crt0 makes a kernel PRX unloadable, which libraries can't be imported, and how much room there
actually is in the kernel partition.
2026-09-08 15:18:02 -06:00
Henrik Rydgård 705ea7eb02 Keep the SHA-1 context in game memory too, and scope the Tlspl partition range to user mode
sceKernelUtilsSha1Block* had the same single global context that MD5 did, so it gets the same
treatment: state, counters and block buffer now live at ctxAddr in the layout hash/sha1ctx
records off hardware. Unlike MD5, SHA-1 does not stream whole blocks through buf, which happens
to be what our sha1_update already does - so no fill-in step is needed there.

The Tlspl partition range from the last commit was too broad a cut. Hardware says only 1-6 exist,
but that recording is from user mode, and BlockAllocatorFromID deliberately maps 8 and 10 to the
user partition for a kernel-mode caller - rejecting them outright would have taken that away.
The tightened range now applies to user mode only and kernel mode keeps what it had.
threads/tls/partition also shows the answer doesn't depend on the compiled SDK version, checked
across 1.00 through 6.06, and that partition 5 is accepted - which no test had covered.
2026-09-08 15:18:02 -06:00
Henrik Rydgård 8a02d1ee0f Keep the MD5 context in game memory, and make MT19937 actually be MT19937
Three fixes, all of them things the new hardware tests turned up.

sceMd5Block* and sceKernelUtilsMd5Block* shared one static md5_context and ignored the context
pointer the caller passed in, with a TODO saying it would do "unless games do several MD5
concurrently". hash/md5ctx shows a real PSP keeps everything in the caller's 96 bytes and happily
runs two digests at once, so do that instead: the state, the counters and the block buffer now
live at ctxAddr in the game's own memory, in the layout the test pins down. Two interleaved
digests come out right, and a context that gets copied mid-digest carries on correctly. As a
side effect the state is now covered by savestates, which a file-static never was.

MersenneTwister masked both halves with 0x80000000 where the low half needs 0x7FFFFFFF, so
sceMt19937UInt and sceKernelUtilsMt19937UInt were returning a sequence that isn't MT19937 at
all - every number differed from hardware from the first draw. hash/mt19937ctx computes the
reference sequence itself and confirms the PSP is plain MT19937; with the mask fixed we match it
for both seeds tested. Init also twists the array immediately, as hardware does, so a context
that has been seeded but not drawn from now holds what a real one would.

sceKernelCreateTlspl accepted partitions up to 9 before falling through to the permission check.
Hardware draws the line at 6 - threads/tls/create records 7, 8, 9 and 10 all returning
ILLEGAL_ARGUMENT - so 8 and 9 were coming back ILLEGAL_PERM. Note this is genuinely different
from sceKernelCreateVpl right above it, which does let 8 and 9 through to ILLEGAL_PERM; the two
had been sharing a check that was only ever right for Vpl.

Risk worth naming: the MT19937 change alters the numbers any game gets from these calls. That's
the point - they were wrong - but a savestate taken mid-sequence will resume with a generator
that behaves differently from the one that made it.
2026-09-08 15:18:01 -06:00
Nab b2dface40d Add/remove options when switching stereo shaders 2026-09-08 20:58:33 +00:00
Nab e9ae17331d Comment Atrac frame size limit for GTALCS 2026-09-08 20:58:33 +00:00
Henrik Rydgård 4d795f5130 Merge pull request #22248 from hrydgard/fat-short-names
sceIo: generate and resolve FAT 8.3 short names
2026-09-08 14:42:18 -06:00
Henrik Rydgård e706d27e14 Merge pull request #22258 from hrydgard/pkg-research
PKG install support
2026-09-08 13:44:42 -06:00
Henrik Rydgård 2845de3a4d Merge pull request #22259 from hrydgard/firmware-modules-load-high
Firmware modules: Load to high memory
2026-09-08 13:44:14 -06:00
Henrik Rydgård 11265049c0 Merge pull request #22260 from hrydgard/iso-trailing-partial-sector
Don't drop an ISO's trailing partial sector
2026-09-08 13:43:04 -06:00