From f69e6c09347bbf37d6f47d41dbbd4bc797bc7a4b Mon Sep 17 00:00:00 2001 From: milescoviello Date: Fri, 16 Oct 2026 20:14:07 +0000 Subject: [PATCH] fix(libSceSystemService): declare the PsmKit local process exports Forever Launcher (PPSA99030) imports sceSystemServiceAddLocalProcessForPsmKit and sceSystemServiceKillLocalProcessForPsmKit and stops at load without them. AnyPS5 runs one process and starts no other, so both throw without reading their arguments. The parameters follow the title's own firmware probes (path, argv and a 0x90-byte parameter block; the kill takes the returned handle), and the unknown results are recorded as technical debt. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceSystemService/Export.cpp | 10 ++++++++++ core/libs/tests/GuestSystemService.cpp | 19 +++++++++++++++++++ docs/dev/debt/libSceSystemService.md | 1 + 3 files changed, 30 insertions(+) diff --git a/core/libs/prx/libSceSystemService/Export.cpp b/core/libs/prx/libSceSystemService/Export.cpp index 6480b49dfc..28df9285a8 100644 --- a/core/libs/prx/libSceSystemService/Export.cpp +++ b/core/libs/prx/libSceSystemService/Export.cpp @@ -390,6 +390,16 @@ int APS5_VABI sceSystemServiceOpenTournamentOccurrence(void) { return 0; } +int APS5_VABI sceSystemServiceAddLocalProcessForPsmKit(const char*, const char* const*, const void*) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + +int APS5_VABI sceSystemServiceKillLocalProcessForPsmKit(int) { + NotImplemented_nid_no_patch(__func__); + return 0; +} + int APS5_VABI sceSystemServiceReenableMusicPlayer(void) { return SYSTEM_SERVICE_OK; } diff --git a/core/libs/tests/GuestSystemService.cpp b/core/libs/tests/GuestSystemService.cpp index e4e181e204..ea1f51b383 100644 --- a/core/libs/tests/GuestSystemService.cpp +++ b/core/libs/tests/GuestSystemService.cpp @@ -8,6 +8,8 @@ extern "C" int APS5_VABI sceSystemServiceGetHdrToneMapLuminance(SystemServiceHdrToneMapLuminance* luminance); extern "C" int APS5_VABI sceSystemServiceParamGetString(int paramId, char* buf, std::size_t bufSize); extern "C" int APS5_VABI sceSystemServiceLaunchApp(const char* titleId, const char* const* arguments, const void* param); +extern "C" int APS5_VABI sceSystemServiceAddLocalProcessForPsmKit(const char* path, const char* const* arguments, const void* param); +extern "C" int APS5_VABI sceSystemServiceKillLocalProcessForPsmKit(int handle); namespace { @@ -24,6 +26,16 @@ bool ParamGetStringThrows(int paramId, char* buf, std::size_t bufSize) { return false; } +template +bool Throws(TCall call) { + try { + static_cast(call()); + } catch (const std::runtime_error&) { + return true; + } + return false; +} + bool LaunchAppThrows(const char* titleId, const char* const* arguments, const void* param) { try { static_cast(sceSystemServiceLaunchApp(titleId, arguments, param)); @@ -76,6 +88,13 @@ int main() { Require(LaunchAppThrows("PPSA99110", launchArguments, launchParam)); for (unsigned char byte : launchParam) Require(byte == 0x5a); Require(std::strcmp(launchArguments[0], "--backup") == 0 && launchArguments[1] == nullptr); + const char* childArguments[] = {"/app0/child.bin", nullptr}; + unsigned char processParam[0x90]; + std::memset(processParam, 0x5a, sizeof(processParam)); + Require(Throws([&] { return sceSystemServiceAddLocalProcessForPsmKit("/app0/child.bin", childArguments, processParam); })); + for (unsigned char byte : processParam) Require(byte == 0x5a); + Require(Throws([] { return sceSystemServiceAddLocalProcessForPsmKit(nullptr, nullptr, nullptr); })); + Require(Throws([] { return sceSystemServiceKillLocalProcessForPsmKit(1); })); Require(sceSystemServiceGetHdrToneMapLuminance(nullptr) == SYSTEM_SERVICE_ERROR_PARAMETER); SystemServiceHdrToneMapLuminance luminance{-1.0f, -1.0f, -1.0f}; Require(sceSystemServiceGetHdrToneMapLuminance(&luminance) == SYSTEM_SERVICE_OK); diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 8d8ea62347..10a4e0c17c 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -9,6 +9,7 @@ ### Unknown function info +- [sceSystemServiceAddLocalProcessForPsmKit, sceSystemServiceKillLocalProcessForPsmKit](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - AnyPS5 runs one process and starts or stops no other, so both throw without reading their arguments. The parameters follow Forever Launcher's own firmware 5.50 probes (`ps5/probes/localproc` in [bizkut/ps5-forever](https://github.com/bizkut/ps5-forever)): a path, a null-terminated argv and a 0x90-byte parameter block in, a handle out; the kill takes that handle. Their results on the console are unknown. Forever Launcher (PPSA99030) imports both and stops at load without them. - [sceSystemServiceLaunchApp](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - AnyPS5 runs one title and cannot start another, so every call throws without reading its arguments. The (title ID, null-terminated argv, launch parameters) signature follows [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/SystemService/SystemService.cs#L141-L148), whose `AppLauncher` Prospero Explorer (PPSA99109) and Prospero Multi Tools (PPSA99110) import it through and which passes a 0x20-byte launch context or null; the console's argument checks and errors are unknown - [sceSystemServiceLaunchWebBrowser](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the console's error for an unavailable web browser is unknown; it returns the kernel's `EOPNOTSUPP` (0x8002002D) for any arguments. The (uri, param) signature follows [RommPS](https://github.com/s0liton/RommPS/blob/754d4d60f25938a5136b9e2efcd074313c4386c5/platform/ps5/app/rommps/examples/rommps/kit/credits.cpp#L86-L95), which passes a URL and a null param and only tests the result for zero; the param layout and the console's argument checks are unknown - [sceSystemServiceParamGetString](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the system name (parameter 6) is the fixed `PS5`, not the console's name; other parameters and buffers of 1 to 64 bytes throw. PPSA26344 at `0xA87935` passes parameter 6 with a 65-byte buffer