feat: PSP overlay plugin PRX with in-game overlay and background music#17
feat: PSP overlay plugin PRX with in-game overlay and background music#17AndrewAltimit merged 26 commits intomainfrom
Conversation
…d music Add oasis-plugin-psp, a kernel-mode PRX companion module that stays resident alongside games via CFW PLUGINS.TXT. Hooks sceDisplaySetFrameBuf for overlay UI (menu, OSD, status bar) and streams MP3 playback through a dedicated audio thread. Includes plugin install/remove/status terminal commands in the EBOOT, INI config parser, and full documentation. Also adds sctrlHEN syscall hook bindings and SyscallHook helper to the rust-psp SDK (separate repo, not included in this commit). Co-Authored-By: Claude Opus 4.6 <[email protected]>
The PSP on-screen keyboard and confirm dialogs never appeared because the SDK's polling loops now properly manage the GU frame lifecycle (fix/osk-dialog-gu-frame branch of rust-psp). After the dialog returns, the caller's display list is closed and must be re-opened. - Add PspBackend::reinit_gu_frame() to re-open the main DISPLAY_LIST - Call reinit_gu_frame() after OSK in terminal (Square button) - Call reinit_gu_frame() after execute_command (covers rm's confirm_dialog) - Call reinit_gu_frame() after file manager delete dialog - Point both PSP crate Cargo.toml files to rust-psp fix/osk-dialog-gu-frame Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Replace #![no_std] with #![feature(restricted_std)] so module_kernel! macro can resolve std::panic::catch_unwind - Rename lib.rs to main.rs so cargo-psp produces a binary (.prx) output - Fix sceKernelIcacheClearAll -> sceKernelIcacheInvalidateAll (correct name) - Fix SceUid newtype comparisons (SceUid(0) instead of bare 0) - Fix static_mut_refs errors: use &raw mut, raw pointers, and constants instead of creating references to static mut (Rust 2024 deny-by-default) - Fix unsafe_op_in_unsafe_fn: wrap zeroed() and ptr::add() in unsafe blocks - Remove unused SCREEN_HEIGHT import - Add rust-std-src to .gitignore (local symlink for cargo-psp sysroot) Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Draw a 2x2 green dot at (1,1) every frame to confirm the display hook is running (remove once overlay is confirmed working) - Add L+R+START as a fallback trigger combo -- CFW (ARK-4/PRO) often intercepts the NOTE button for its own VSH menu before the plugin sees it Co-Authored-By: Claude Opus 4.6 <[email protected]>
Writes initialization trace to ms0:/seplugins/oasis_debug.txt to diagnose why the display hook fails on 6.20 PRO-C2. Tries four module/library name combinations for sceDisplaySetFrameBuf since different CFW versions expose it under different names. Co-Authored-By: Claude Opus 4.6 <[email protected]>
…l crash The std runtime initialization (TLS, allocator, panic hooks) was crashing in kernel mode on 6.20 PRO-C2, causing the system to lock on game startup. module_kernel! has a no_std code path using core::intrinsics::catch_unwind that works without std. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Unresolvable import stubs crash the PRX at load time because these modules (MP3 decoder, audio channel, power, RTC) aren't loaded in the game's kernel context. Stub out audio module and simplify status line to only use kernel-safe imports (sceIo, sceCtrl, sceKernel, sctrlHEN). Co-Authored-By: Claude Opus 4.6 <[email protected]>
The SDK's no_std path uses extern crate panic_unwind and core::intrinsics::catch_unwind which require unwind support. Also picks up rust-psp kernel thread fix (no USER flag) and kernel partition allocator. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Separates sctrlHENFindFunction and sctrlHENPatchSyscall calls with debug_log messages to identify which CFW API call crashes on 6.20 PRO-C2. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Check if the import stub was actually resolved by CFW before calling the function. Logs the raw function pointer address. Co-Authored-By: Claude Opus 4.6 <[email protected]>
MIPS dcache coherency: freshly loaded PRX .rodata may not be visible to kernel functions. Flush dcache before calling sctrlHENFindFunction and copy string args to stack to avoid stale cache reads. Co-Authored-By: Claude Opus 4.6 <[email protected]>
If ThreadMan lookup crashes too, the function itself is broken on PRO-C2. If it works, the issue is display-specific arguments. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Read the MIPS instructions at the sctrlHENFindFunction and sctrlHENPatchSyscall stubs to see what the firmware patched them to (j addr, syscall, jr $ra, etc). Co-Authored-By: Claude Opus 4.6 <[email protected]>
CFW APIs crash if called before SystemControl is fully initialized. Common PSP plugin pattern: wait for the system to stabilize before hooking display functions. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add runtime check that SystemCtrlForKernel import stubs were resolved by the firmware loader before calling them. Reads the raw stub bytes - a resolved stub starts with `jr $ra` (0x03E00008), while an unresolved stub contains Stub struct pointer data. Also updated rust-psp dependency which fixes the import flags from 0x4001 to 0x4009 (adds kernel library bit so the loader searches kernel space for SystemCtrlForKernel). Co-Authored-By: Claude Opus 4.6 <[email protected]>
Kernel imports are patched with `j target` (direct jump, opcode=2) not `jr $ra; syscall N` (user-mode pattern). The stub check now accepts both formats. Co-Authored-By: Claude Opus 4.6 <[email protected]>
PSP firmware patches kernel import stubs with `j target` but leaves the delay slot containing the original Stub struct data (a pointer value). This decodes to `lwl $a0, offset($at)` which corrupts arguments and crashes. Instead of calling through the psp_extern! wrapper (which executes the broken stub), extract the jump target address from the `j` instruction and call the kernel function directly via transmuted function pointer. This bypasses the stub entirely. Co-Authored-By: Claude Opus 4.6 <[email protected]>
When sctrlHENPatchSyscall returns an error (e.g. another plugin already patched the syscall table entry), fall back to inline hooking: save the first two instructions of the target function, write `j our_hook; nop` at the entry point, and build a trampoline that executes the saved instructions then jumps to original+8. Also logs the PatchSyscall return value and the original function's first two instructions for diagnostics. Co-Authored-By: Claude Opus 4.6 <[email protected]>
2x2 pixel at (5,5): green=idle, red=any button, white=L+R+START. Helps diagnose whether sceCtrlPeekBufferPositive reads input from the display hook context. Co-Authored-By: Claude Opus 4.6 <[email protected]>
The user-mode sceCtrlPeekBufferPositive import doesn't return data from the display hook context (kernel display driver thread). Resolve sceCtrlPeekBufferPositive from sceCtrl_driver via sctrlHENFindFunction and call it directly. This kernel-mode variant reads the shared controller buffer regardless of calling context. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Initialize sceCtrlSetSamplingCycle(0) and sceCtrlSetSamplingMode(1) via the kernel driver after resolving sceCtrlPeekBufferPositive. The game's user-mode controller init may not apply to kernel-mode reads. Also use read_volatile on the buffer to prevent the compiler from optimizing away the read. Added one-shot diagnostic log showing the first poll result (return value, timestamp, button state). Co-Authored-By: Claude Opus 4.6 <[email protected]>
Move controller polling from display hook context (where syscalls don't work) to a dedicated kernel thread that reads sceCtrl at ~60Hz via AtomicU32. Draw overlay onto uncached framebuffer (addr | 0x40000000) before calling original sceDisplaySetFrameBuf to eliminate horizontal striping from stale cache lines and flickering from mid-scanout writes. Co-Authored-By: Claude Opus 4.6 <[email protected]>
…nction Replace ~300 lines of manual kernel stub resolution, MIPS instruction encoding, inline hooking, and PatchSyscall workarounds with calls to psp::hook::SyscallHook::install() and psp::hook::find_function() which now handle all of this internally. Removed: resolve_kernel_stub, extract_j_target, encode_j, is_stub_resolved, install_inline_hook, Trampoline, FIND_FUNC_STUB, PATCH_SYSCALL_STUB, ORIGINAL_SET_FRAME_BUF, log_find_function_result, write_log_cstr. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Co-Authored-By: Claude Opus 4.6 <[email protected]>
The fix/osk-dialog-gu-frame branch was merged into main via PR #6. Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Widgets: 15+ -> 20+ (22 modules in oasis-ui) - Commands: 80+ -> 90+ (76 terminal + 17 core = 93) - Modules: 14 -> 17 (12 terminal + 5 core) - Fix PRX output filename in README (oasis-plugin-psp.prx) - Add oasis-plugin-psp to design.md crate tree Co-Authored-By: Claude Opus 4.6 <[email protected]>
Gemini AI Code ReviewIssues
Previous Issues(none) Suggestions
Notes
Generated by Gemini AI (gemini-3-flash-preview). Supplementary to human reviews. |
Review Response Agent (Iteration 1)Status: No changes needed Fixed Issues
Ignored Issues
Deferred to Human
Notes
The agent reviewed feedback but determined no code changes were required. |

Summary
oasis-plugin-pspkernel-mode PRX crate: a resident overlay module loaded by CFW (ARK-4/PRO) viaPLUGINS.TXTthat hookssceDisplaySetFrameBufto draw UI into the game's framebufferSyscallHookandfind_function()APIs (upstreamed in rust-psp PR fix(tls): harden TLS implementation and add comprehensive tests #6) for clean kernel hooking with inline hook fallbackaddr | 0x4000_0000) and draw-before-original orderingsceCtrl_driver) withAtomicU32cross-thread button stateoasis-backend-pspby managing GU display list lifecycle around utility dialogsNew Crate:
oasis-plugin-psphook.rsSyscallHook, controller thread, function resolutionoverlay.rsrender.rsfont.rsaudio.rsconfig.rsms0:/seplugins/oasis.inimain.rsmodule_kernel!()declarationTest plan
cd crates/oasis-plugin-psp && RUST_PSP_BUILD_STD=1 cargo +nightly psp --releasecd crates/oasis-backend-psp && RUST_PSP_BUILD_STD=1 cargo +nightly psp --releasermcommand (GU fix)Generated with Claude Code