diff --git a/core/libs/prx/libSceSystemService/Export.cpp b/core/libs/prx/libSceSystemService/Export.cpp index 0b9e30b645..f2fa29f8de 100644 --- a/core/libs/prx/libSceSystemService/Export.cpp +++ b/core/libs/prx/libSceSystemService/Export.cpp @@ -260,6 +260,28 @@ void CopyRunningTitleId(const std::string& caller, bool running, char* destinati std::memcpy(destination, titleId.c_str(), titleId.size() + 1); } +int RunningAppId(const std::string& caller, const char* titleId) { + if (titleId == nullptr) NotImplemented_nid_no_patch((caller + ": null title ID").c_str()); + if (RunningTitleId() != titleId) NotImplemented_nid_no_patch((caller + ": title other than the running title").c_str()); + return SYSTEM_SERVICE_RUNNING_APP_ID; +} + +template +int AnswerForRunningApp(const std::string& caller, std::int32_t appId, TValue* destination, TValue value) { + if (appId != SYSTEM_SERVICE_RUNNING_APP_ID) NotImplemented_nid_no_patch((caller + ": application other than the running title").c_str()); + if (destination == nullptr) NotImplemented_nid_no_patch((caller + ": null output").c_str()); + *destination = value; + return SYSTEM_SERVICE_OK; +} + +int ListRunningBigApp(const std::string& caller, std::int32_t* appIds, std::uint32_t capacity, std::uint32_t* count) { + if (appIds == nullptr || count == nullptr) NotImplemented_nid_no_patch((caller + ": null list or count").c_str()); + if (capacity == 0) NotImplemented_nid_no_patch((caller + ": a list with no room for the running title").c_str()); + appIds[0] = SYSTEM_SERVICE_RUNNING_APP_ID; + *count = 1; + return SYSTEM_SERVICE_OK; +} + } extern "C" { @@ -455,13 +477,7 @@ int APS5_VABI sceSystemServiceGetAppIdOfRunningBigApp(void) { } int APS5_VABI sceSystemServiceGetAppId(const char* titleId) { - if (titleId == nullptr) { - NotImplemented_nid_no_patch("sceSystemServiceGetAppId: null title ID"); - } - if (RunningTitleId() != titleId) { - NotImplemented_nid_no_patch("sceSystemServiceGetAppId: title other than the running title"); - } - return SYSTEM_SERVICE_RUNNING_APP_ID; + return RunningAppId("sceSystemServiceGetAppId", titleId); } int APS5_VABI sceSystemServiceGetAppTitleId(int appId, char* titleId) { @@ -474,6 +490,26 @@ int APS5_VABI sceLncUtilGetAppTitleId(std::int32_t appId, char* titleId) { return SYSTEM_SERVICE_OK; } +int APS5_VABI sceLncUtilGetAppId(const char* titleId) { + return RunningAppId("sceLncUtilGetAppId", titleId); +} + +int APS5_VABI sceLncUtilIsAppLaunched(const char* titleId, bool* launched) { + return AnswerForRunningApp("sceLncUtilIsAppLaunched", RunningAppId("sceLncUtilIsAppLaunched", titleId), launched, true); +} + +int APS5_VABI sceLncUtilGetAppIdOfRunningBigApp() { + return sceSystemServiceGetAppIdOfRunningBigApp(); +} + +int APS5_VABI sceLncUtilGetAppIdListOfBigApp(std::int32_t* appIds, std::uint32_t capacity, std::uint32_t* count) { + return ListRunningBigApp("sceLncUtilGetAppIdListOfBigApp", appIds, capacity, count); +} + +int APS5_VABI sceSystemServiceGetAppIdListOfBigApp(std::int32_t* appIds, std::uint32_t capacity, std::uint32_t* count) { + return ListRunningBigApp("sceSystemServiceGetAppIdListOfBigApp", appIds, capacity, count); +} + int APS5_VABI sceSystemServiceKillApp(int appId, int how, int reason, int coreDump) { if (appId != SYSTEM_SERVICE_RUNNING_APP_ID) { NotImplemented_nid_no_patch("sceSystemServiceKillApp: application other than the running title"); diff --git a/core/libs/prx/libSceSystemService/PendingExports.cpp b/core/libs/prx/libSceSystemService/PendingExports.cpp index d2b7fca08e..fde1468222 100644 --- a/core/libs/prx/libSceSystemService/PendingExports.cpp +++ b/core/libs/prx/libSceSystemService/PendingExports.cpp @@ -234,21 +234,6 @@ int APS5_VABI sceLncUtilGetAppFocusedAppStatus(LncAppStatus* status) { return 0; } -int APS5_VABI sceLncUtilGetAppId(const char* titleId) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceLncUtilGetAppIdListOfBigApp(std::int32_t* appIds, std::uint32_t capacity, std::uint32_t* count) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceLncUtilGetAppIdOfRunningBigApp() { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceLncUtilGetAppLaunchedUser(std::int32_t appId, std::int32_t* userId) { NotImplemented_nid_no_patch(__func__); return 0; @@ -344,11 +329,6 @@ int APS5_VABI sceLncUtilIsActiveCdlg(bool* active) { return 0; } -int APS5_VABI sceLncUtilIsAppLaunched(const char* titleId, bool* launched) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceLncUtilIsAppSuspended(std::int32_t appId, bool* suspended) { NotImplemented_nid_no_patch(__func__); return 0; @@ -1835,11 +1815,6 @@ int APS5_VABI sceSystemServiceGetAppFocusedAppStatus(SystemServiceAppStatus* sta return 0; } -int APS5_VABI sceSystemServiceGetAppIdListOfBigApp(std::int32_t* appIds, std::uint32_t capacity, std::uint32_t* count) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceSystemServiceGetAppLaunchedUser(std::int32_t appId, std::int32_t* userId) { NotImplemented_nid_no_patch(__func__); return 0; diff --git a/core/libs/tests/GuestSystemService.cpp b/core/libs/tests/GuestSystemService.cpp index 8a197d97b5..411b4f33bb 100644 --- a/core/libs/tests/GuestSystemService.cpp +++ b/core/libs/tests/GuestSystemService.cpp @@ -2,6 +2,7 @@ #include "SceTypes.hpp" #include "prx/libSceSystemService/SystemService.hpp" #include +#include #include #include #include @@ -37,6 +38,31 @@ bool LaunchAppThrows(const char* titleId, const char* const* arguments, const vo namespace { +template +void TestBigAppList(TList list) { + const int runningAppId = sceSystemServiceGetAppIdOfRunningBigApp(); + constexpr std::int32_t Sentinel = 0x5a5a5a5a; + for (const std::uint32_t capacity : {1u, 4u}) { + std::int32_t appIds[4] = {Sentinel, Sentinel, Sentinel, Sentinel}; + std::uint32_t count = 0xdeadbeefu; + Require(list(appIds, capacity, &count) == SYSTEM_SERVICE_OK); + Require(count == 1 && appIds[0] == runningAppId); + for (std::size_t index = 1; index < 4; ++index) Require(appIds[index] == Sentinel); + } + std::int32_t appIds[2] = {Sentinel, Sentinel}; + std::uint32_t count = 0xdeadbeefu; + Require(Throws([&] { return list(appIds, 0, &count); })); + Require(Throws([&] { return list(nullptr, 2, &count); })); + Require(Throws([&] { return list(appIds, 2, nullptr); })); + Require(appIds[0] == Sentinel && appIds[1] == Sentinel && count == 0xdeadbeefu); +} + +void TestRunningBigApp() { + Require(sceLncUtilGetAppIdOfRunningBigApp() == sceSystemServiceGetAppIdOfRunningBigApp()); + TestBigAppList(sceLncUtilGetAppIdListOfBigApp); + TestBigAppList(sceSystemServiceGetAppIdListOfBigApp); +} + void TestPlayerDialogParam() { alignas(8) unsigned char buffer[0x100]; std::memset(buffer, 0xab, sizeof(buffer)); @@ -150,5 +176,6 @@ int main() { SystemServiceEvent event; event.event_type = 7; Require(sceSystemServiceReceiveEvent(&event) == SYSTEM_SERVICE_ERROR_NO_EVENT); + TestRunningBigApp(); TestPlayerDialogParam(); } diff --git a/core/libs/tests/GuestSystemServiceAppTitleId.cpp b/core/libs/tests/GuestSystemServiceAppTitleId.cpp index 7fee52ec8e..02d338199c 100644 --- a/core/libs/tests/GuestSystemServiceAppTitleId.cpp +++ b/core/libs/tests/GuestSystemServiceAppTitleId.cpp @@ -42,7 +42,19 @@ bool Throws(TCall call) { } bool GetAppIdThrows(const char* titleId) { - return Throws([&] { static_cast(sceSystemServiceGetAppId(titleId)); }); + return Throws([&] { static_cast(sceSystemServiceGetAppId(titleId)); }) && Throws([&] { static_cast(sceLncUtilGetAppId(titleId)); }); +} + +bool IsAppLaunchedThrows(const char* titleId) { + bool launched = false; + return Throws([&] { static_cast(sceLncUtilIsAppLaunched(titleId, &launched)); }) && !launched; +} + +void CheckRunningTitleLookups(const char* titleId, int runningAppId) { + Require(sceLncUtilGetAppId(titleId) == runningAppId); + bool launched = false; + Require(sceLncUtilIsAppLaunched(titleId, &launched) == 0 && launched); + Require(Throws([&] { static_cast(sceLncUtilIsAppLaunched(titleId, nullptr)); })); } bool GetAppTitleIdThrows(int appId, GuardedBuffer& buffer) { @@ -70,6 +82,7 @@ void CheckNullBuffers(int runningAppId) { Require(Throws([&] { static_cast(sceLncUtilGetAppTitleId(runningAppId, nullptr)); })); Require(Throws([&] { static_cast(sceSystemServiceGetAppTitleId(runningAppId + 1, nullptr)); })); Require(GetAppIdThrows(nullptr)); + Require(IsAppLaunchedThrows(nullptr)); } void CheckKnownTitle(int runningAppId) { @@ -80,13 +93,16 @@ void CheckKnownTitle(int runningAppId) { const char* const otherTitles[] = {"PPSA00001", "PPSA00010", "ppsa00000", "PPSA0000", "PPSA000000", "PPSA00000 ", " PPSA00000", "CUSA00000", ""}; for (const char* other : otherTitles) { Require(GetAppIdThrows(other)); + Require(IsAppLaunchedThrows(other)); } + CheckRunningTitleLookups("PPSA00000", runningAppId); GuardedBuffer buffer; Require(sceSystemServiceGetAppTitleId(runningAppId, buffer.bytes) == 0); Require(std::memcmp(buffer.bytes, "PPSA00000", titleId.size() + 1) == 0); Require(buffer.GuardIntact()); Require(sceSystemServiceGetAppId(buffer.bytes) == runningAppId); + Require(sceLncUtilGetAppId(buffer.bytes) == runningAppId); GuardedBuffer lncBuffer; Require(sceLncUtilGetAppTitleId(runningAppId, lncBuffer.bytes) == 0); @@ -104,8 +120,10 @@ void CheckKnownTitle(int runningAppId) { void CheckLongTitle(int runningAppId) { Require(sceSystemServiceGetAppId("PPSA000000") == runningAppId); + CheckRunningTitleLookups("PPSA000000", runningAppId); Require(GetAppIdThrows("PPSA00000")); Require(GetAppIdThrows("PPSA0000000")); + Require(IsAppLaunchedThrows("PPSA00000")); GuardedBuffer buffer; Require(GetAppTitleIdThrows(runningAppId, buffer)); Require(LncGetAppTitleIdThrows(static_cast(runningAppId), buffer)); @@ -114,6 +132,7 @@ void CheckLongTitle(int runningAppId) { void CheckMissingMetadata(int runningAppId) { Require(GetAppIdThrows("PPSA00000")); Require(GetAppIdThrows(nullptr)); + Require(IsAppLaunchedThrows("PPSA00000")); GuardedBuffer buffer; Require(GetAppTitleIdThrows(runningAppId, buffer)); Require(LncGetAppTitleIdThrows(static_cast(runningAppId), buffer)); diff --git a/docs/dev/debt/libSceSystemService.md b/docs/dev/debt/libSceSystemService.md index 40dfa92cfa..997be9375e 100644 --- a/docs/dev/debt/libSceSystemService.md +++ b/docs/dev/debt/libSceSystemService.md @@ -15,6 +15,7 @@ - [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. - [sceSystemServiceGetAppId, sceSystemServiceGetAppTitleId, sceLncUtilGetAppTitleId](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the console's results for a title ID or app ID other than the running title are unknown, and so are its errors for null pointers, so those calls throw. `sceSystemServiceGetAppId` compares the title ID exactly with the one in `param.json` or `param.sfo`. The title ID buffer is 10 bytes, so a running title ID of 10 or more characters throws too. The signatures follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/SystemService/SystemService.cs#L236-L260), except that `sceLncUtilGetAppTitleId` returns an `int`, as declared in the pending exports, and 0 on success like `sceSystemServiceGetAppTitleId`; SharpProspero names `ErrorNoAppInfo` (0x80A10009) only for `sceSystemServiceGetAppFocusedAppStatus`. +- [sceLncUtilGetAppId, sceLncUtilIsAppLaunched, sceLncUtilGetAppIdListOfBigApp, sceSystemServiceGetAppIdListOfBigApp](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - only the running title answers. `sceLncUtilGetAppId` returns its app ID as `sceSystemServiceGetAppId` does, `sceLncUtilIsAppLaunched` reports it launched, and both big-app lists write its app ID as their only entry with a count of 1, because AnyPS5 runs one title and cannot start another. Another title ID, a null pointer and a list capacity of 0 throw: whether the console reports another installed title as not launched, and its errors for these arguments, are unknown. The signatures are the ones the pending exports recovered - [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): the native export takes one URI argument. Null input, the 6015-byte length limit and supported scheme checks are recovered. The host still returns kernel `EOPNOTSUPP` for valid input; a missing browser service's native error is unresolved. - [sceSystemServiceParamGetString](../../../core/libs/prx/libSceSystemService/Export.cpp) (libSceSystemService) - the system name (parameter 6) is the fixed `PS5`, not the console's name. As in a payload process on a PS5 (firmware 13.60), the name is written NUL-padded to the whole buffer, and a buffer under 65 bytes, a null buffer or any other parameter returns `SYSTEM_SERVICE_ERROR_PARAMETER` without writing. Parameters 0 to 40, 100 to 110, 1000 to 1010, -1 and `INT_MAX` were tried there; the others are assumed to fail the same way. PPSA26344 at `0xA87935` passes parameter 6 with a 65-byte buffer