feat: PRX background MP3 audio, CPU clock control, and FTP server#18
feat: PRX background MP3 audio, CPU clock control, and FTP server#18AndrewAltimit merged 29 commits intomainfrom
Conversation
Implement three features that close gaps with IR Shell: - PRX audio: kernel-mode background MP3 playback using runtime NID resolution via find_function() for sceAudio and sceMp3 drivers, with dynamic module loading fallback, playlist scanning, decode thread, and atomic playback controls - CPU clock control: resolve scePower driver NIDs at hook init, cycle through 4 clock presets (333/266/222/133 MHz), display CPU MHz and battery percentage in overlay status line - FTP file server: poll-based TCP server (FtpServer) modeled after RemoteListener, wired into main loop via FtpToggle command output variant, processes LIST/GET/PUT/MKDIR/DELETE/STAT/QUIT against VFS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
scan_playlist() now recursively descends into subdirectories (up to 4 levels deep) when scanning the music directory. Also increases playlist capacity from 16 to 32 tracks and max path length from 80 to 128 bytes to accommodate deeper folder structures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix wrong NID for sceMp3NotifyAddStreamData: was 0x0DB149F4 (duplicate of sceMp3ReleaseMp3Handle), now correct 0x29BFF3EC - Fix MP3 module dependency load order: mpegbase -> mpeg -> libmp3 (was loading mpeg before its dependency mpegbase) - Add mpeg_vsh.prx as fallback for some firmware versions - Add sceAudio_Service module name variant for wider firmware compat - Add libmp3 module name variant for sceMp3 resolution - Prefer kernel-mode library names (_driver) first for sceAudio Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sceMp3 is a user-mode library that sctrlHENFindFunction cannot resolve from kernel context. Replace with sceAudiocodec (type 0x1002) which is a kernel-accessible codec driver. Also always start the audio thread regardless of autoplay config (start paused if autoplay=false). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sctrlHENFindFunction on PRO-C2 can only find kernel driver modules (sceAudio, sceCtrl, scePower). Neither sceMp3 nor sceAudiocodec were discoverable because the AV modules weren't loaded into the system. Now resolves sceUtilityLoadModule via find_function from sceUtility_Driver and uses it to load PSP_MODULE_AV_AVCODEC (0x0300), PSP_MODULE_AV_MPEGBASE (0x0301), and PSP_MODULE_AV_MP3 (0x0302). Also adds NULL module name fallback search (searches all loaded modules), tries sceMp3 first then sceAudiocodec as fallback, and logs return codes from every module load call for diagnostics. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…alking sctrlHENFindFunction on PRO-C2 6.20 only searches kernel-mode modules, so sceMp3 and sceAudiocodec NIDs were invisible even after successfully loading them via sceUtilityLoadModule. This adds a fallback path that resolves sceKernelFindModuleByName from ModuleMgrForKernel, uses it to locate the loaded user-mode module struct, then walks the SceModule's export table (ent_top/ent_size -> SceLibraryEntryTable entries) to find function pointers by NID. Resolution order: sctrlHENFindFunction (fast, kernel) -> export walk (user). Includes diagnostic logging of found modules and export table addresses. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sceKernelFindModuleByName returned 0x03F0DF67 (unaligned, not in any valid PSP memory range), crashing on dereference. This adds: - Pointer validation: alignment check + memory range check before any dereference (KSEG0/KSEG1/user space ranges) - Alternative API: try sceKernelSearchModuleByName (LoadCoreForKernel, NID 0xF0CAB543) which may behave differently on PRO-C2 - UID fallback: if FindModuleByName returns a non-pointer value, try treating it as a SceUID and converting via FindModuleByUID - Multiple offset probing: try ent_top/ent_size at 0x58/0x5C, 0x40/0x44, and 0x24/0x28 (different firmware SceModule layouts) - Hex dump: dump raw struct bytes at key offsets for remote diagnosis Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… PRXs sceKernelFindModuleByName returns garbage (0x03F12969, unaligned) on PRO-C2 6.20. Replace with two new strategies: 1. Module enumeration: resolve sceKernelGetModuleIdList (NID 0x644CF325) and sceKernelQueryModuleInfo (NID 0x748CBED9) from ModuleMgrForKernel. Enumerate all loaded modules, log their names + text addresses, and match by name pattern to find MP3/codec modules. Use text_addr to locate the embedded SceModuleInfo header and walk its export table (ent_top at +0x24, ent_end at +0x28). 2. Kernel-load flash0 PRXs: after sceUtilityLoadModule (which loads into user space), also try sceKernelLoadModule for flash0:/kd/*.prx files. When loaded from kernel context, modules may register in the kernel module list where sctrlHENFindFunction can discover them. Removed broken FindModuleByName/SearchModuleByName/FindModuleByUID code. Fixed excessive log spam (was logging every find_module_ptr call). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When MP3/codec NIDs can't be resolved via standard kernel APIs, scans user memory (0x08800000-0x0A000000) and kernel memory (0x88000000-0x88800000) for known NID values. Writes results to ms0:/seplugins/oasis_memdump.txt with hex context dumps and module name string search. This gives us hard diagnostic data to understand where sceUtilityLoadModule actually puts the AV modules and their export tables on PRO-C2 6.20. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Based on the NID memory scan results showing sceAudiocodec NIDs in user memory at 0x08B63C44 (the game's import NID table), adds a stub extraction strategy: 1. Scan user memory for a cluster of known sceAudiocodec NIDs 2. Walk backwards/forwards to find the full sorted NID table 3. Scan for a pointer to the NID table (SceLibraryStubTable entry) 4. Read the stub table and decode MIPS J-instructions to extract the real function addresses 5. Populate sceAudiocodec function pointer statics Handles both J-instruction format (user-to-user) and logs syscall stubs (user-to-kernel) for separate handling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The NID memory scan (32MB, checking 13 NIDs per word + string search) takes ~5+ seconds and was causing the PSP to crash before the fast stub extraction code could run. Swap order: run try_codec_stub_extraction() first (~0.1s, two targeted scans) then fall back to the diagnostic NID dump only if extraction fails. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the text-based NID scan with raw binary dumps of user memory (24MB, 0x08800000-0x0A000000) and kernel memory (8MB, 0x88000000- 0x88800000) to ms0:/seplugins/. Writes in 64KB chunks with 1MB progress logging. These dumps can be analyzed on PC with Python to find the syscall table and resolve sceAudiocodec function pointers. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the unsuccessful direct stub decoding with a syscall table lookup approach discovered through offline memory dump analysis: 1. Extract sceAudiocodec syscall numbers from game's import stubs (jr $ra + syscall N format, N = insn1 >> 6) 2. Find sceAudio's import stubs nearby in user memory 3. Extract sceAudioChReserve's syscall number as a reference 4. Cross-reference with the already-resolved kernel function pointer (from sctrlHENFindFunction) to compute the syscall table base: table_base = kernel_ptr - syscall_num * 4 5. Look up sceAudiocodec kernel function pointers from the table This is firmware-independent since it computes the table base dynamically rather than hardcoding it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The sceAudio and sceAudiocodec import tables can be far apart in the game binary (1.75MB observed). Expand the NID search from +/-64KB to the full user memory range (0x08800000-0x0A000000). The stub table pointer search remains localized to +/-16KB from the NID table since it's always in the same ELF section. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of trying to resolve kernel function pointers via the syscall table (which failed because sctrlHENFindFunction returns export table entries, not syscall table entries), use the game's import stub addresses directly as function pointers. Each stub is `jr $ra; syscall N` -- when called from kernel mode, the syscall instruction traps to the kernel's syscall handler which dispatches to the actual codec function and returns to our caller. This is the same mechanism the game uses to call these functions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three fixes for PRX audio playback: 1. Reserve channel 7 (fallback 6, then -1) instead of auto-select to avoid stealing game audio channels and causing crashes 2. Increase CODEC_BUF_WORDS from 32 to 65 -- sceAudiocodec needs 65 u32 entries and was overflowing into adjacent memory 3. Add error logging for all codec init and decode calls to diagnose playback issues Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… module name Two key fixes: 1. Allocate codec, PCM, and read buffers in user memory partition via sceKernelAllocPartitionMemory. Codec functions called through syscall stubs validate pointers are in user range (0x08800000+), so kernel-space statics (0x88xxxxxx) get rejected with 0x807F00FD. 2. Add "sceAvcodec_wrapper" as first module name to try for sctrlHENFindFunction -- this is the actual kernel driver name used by the working mp3play.prx reference plugin. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sceAudiocodecDecode needs codec[7]=input bytes available and codec[9]=output buffer size before each call. Missing these caused every decode to fail with 0x807F00FD. Also cap consecutive decode failures at 100 to prevent infinite spin and log flooding. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PSP has 8 regular PCM channels (0-7) that games claim exclusively. Reserving ANY of them causes games to crash when they init audio. Switch to the SRC (Sample Rate Conversion) channel -- a dedicated output path separate from the 8 regular channels. This is the standard approach for PSP plugins doing background audio. API: sceAudioSRCChReserve/sceAudioSRCOutputBlocking instead of sceAudioChReserve/sceAudioOutputBlocking. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
From mp3play.prx disassembly, sceAudiocodecDecode requires codec[10] set to the input data size (same as codec[7]). Also dumps codec buffer state after init and first-frame MP3 header bytes for diagnosis. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sceAudiocodecGetEDRAM allocates from the PSP's shared 2MB eDRAM pool (used by both GE and media engine). When games try to use their own audio/video codecs, they find the eDRAM already claimed and crash. Instead, we allocate 16KB of working memory from the user RAM partition and set codec[5] manually, completely avoiding the eDRAM pool. This lets games use their full eDRAM allocation while our MP3 decoder works from regular RAM. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The codec requires actual EDRAM for decode (user RAM fails with 0x807F0001). Reverts to sceAudiocodecGetEDRAM. The likely cause of game crashes is sceUtilityLoadModule loading AVCODEC/MPEGBASE/MP3 modules that conflict with the game's own module loading. Now tries sceAudiocodec first (resolves via sctrlHENFindFunction without loading any modules). Only falls back to LoadModule + sceMp3 as a last resort. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
sceAudiocodec is only available via sctrlHENFindFunction after the AVCODEC module is loaded. Instead of loading it ourselves (which can conflict with the game's own module loading and cause crashes), wait for the game to load it during its init sequence. Retries sceAudiocodec resolution every 2s for up to 15s. Only falls back to sceUtilityLoadModule if the game never loaded the modules (low conflict risk since the game doesn't use audio codecs). Also increases initial audio thread delay from 1s to 3s. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Increase read buffer from 32KB to 64KB (fewer I/O stalls during decode) - Bump audio thread priority from 0x1E to 0x18 for smoother scheduling - Strip all diagnostic logging from decode loop (was causing file I/O contention with MP3 reads on every track init and first 5 frames) - Only error paths still log Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Some games take longer to activate their audio subsystem. Retry every 2s for up to 60s before falling back to loading modules ourselves. Also removed the per-retry log line to keep output clean. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Instead of waiting until the buffer is nearly empty then doing one large blocking read (~60KB), stream 4KB chunks on every decode cycle. This spreads I/O across many frames so no single read stalls the audio output. Buffer compaction happens when half consumed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Updates the psp dependency to include the merged rust-psp PR #7 which contributes our reverse-engineered abstractions back to the SDK: SrcChannel, AudiocodecDecoder, find_sync, skip_id3v2, and removes the incorrect kernel feature gate from sys::codec. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Gemini AI Code ReviewIssues
Previous Issues(none) Suggestions
Notes
Generated by Gemini AI (gemini-3-flash-preview). Supplementary to human reviews. |
Automated fix by Claude in response to Gemini/Codex review. Iteration: 1/5 Co-Authored-By: AI Review Agent <noreply@anthropic.com>
Review Response Agent (Iteration 1)Status: Changes committed and pushed Commit: Fixed Issues
Ignored Issues
Deferred to Human
Notes
Automated summary of agent fixes. |
Gemini AI Incremental ReviewThis is an incremental review focusing on changes since the last review. Issues
Previous Issues
Suggestions
Notes
Generated by Gemini AI (gemini-3-flash-preview). Supplementary to human reviews. |
On malformed MP3 files, sceAudiocodec can return success (ret >= 0) but consume 0 bytes. Without a limit, the decode loop would advance buf_pos by 1 byte at a time while the refill logic keeps topping up the buffer, effectively scanning the entire file byte-by-byte. Cap at 100 consecutive zero-consumed iterations matching the existing error limit. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Review Response Agent (Iteration 2)Status: No changes needed Fixed Issues
Ignored Issues
Deferred to Human
Notes
The agent reviewed feedback but determined no code changes were required. |
Gemini AI Incremental ReviewThis is an incremental review focusing on changes since the last review. Issues
Previous Issues
Suggestions
Notes
Generated by Gemini AI (gemini-3-flash-preview). Supplementary to human reviews. |
Review Response Agent (Iteration 3)Status: No changes needed Fixed Issues
Ignored Issues
Deferred to Human
Notes
The agent reviewed feedback but determined no code changes were required. |
Summary
ms0:/MUSIC/while games are running, usingsceAudiocodecfor hardware decoding and the SRC audio channel to avoid conflicting with game audioSrcChannel,AudiocodecDecoder,find_sync,skip_id3v2abstractions contributed back from this workKey technical details
The codec buffer layout was reverse-engineered from Sony's
mp3play.prx:sceAudiocodecGetEDRAMis required (user RAM rejected with 0x807F0001)Test plan
RUST_PSP_BUILD_STD=1 cargo +nightly psp --release)Generated with Claude Code