Merge pull request #10573 from akkadbakkad89/feat/np-game-intent-launch-app

feat(libSceNpGameIntent): recover sceNpAppLauncherLaunchApp validation
This commit is contained in:
Dean authored and GitHub committed 2026-10-27 10:54:31 +13:00
commit 4c95b14b71
4 files changed
+59 -5

No files matched your search

+13 -1
View File
@@ -14,12 +14,19 @@ constexpr int NpGameIntentErrorInvalidArgument = -2141898748;
constexpr int NpGameIntentErrorIntentNotFound = -2141898746;
constexpr int NpGameIntentErrorValueNotFound = -2141898745;
constexpr int NpGameIntentUserIdInvalid = -1;
constexpr int NpAppLauncherErrorNotInitialized = -2141898238;
constexpr int NpAppLauncherErrorInvalidArgument = -2141898236;
std::atomic_bool& NpGameIntentInitialized() {
static std::atomic_bool initialized{false};
return initialized;
}
std::atomic_bool& AppLauncherInitialized() {
static std::atomic_bool initialized{false};
return initialized;
}
}
extern "C" {
@@ -91,15 +98,20 @@ int APS5_VABI sceNpGameIntentVshTerminate(void) {
int APS5_VABI sceNpAppLauncherInitialize(const void* initParam) {
(void)initParam;
AppLauncherInitialized() = true;
return 0;
}
int APS5_VABI sceNpAppLauncherLaunchApp() {
int APS5_VABI sceNpAppLauncherLaunchApp(std::int32_t arg0, const char* arg2, const char* arg3) {
(void)arg0;
if (!AppLauncherInitialized()) return NpAppLauncherErrorNotInitialized;
if (arg2 == nullptr || *arg2 == '\0' || (arg3 != nullptr && std::memchr(arg3, '\0', 0x2001) == nullptr)) return NpAppLauncherErrorInvalidArgument;
NotImplemented_nid_no_patch(__func__);
return 0;
}
int APS5_VABI sceNpAppLauncherTerminate() {
AppLauncherInitialized() = false;
return 0;
}
@@ -13,7 +13,7 @@ int APS5_VABI sceNpGameIntentInfoInit(NpGameIntentInfo* info);
int APS5_VABI sceNpGameIntentVshInitialize(void);
int APS5_VABI sceNpGameIntentVshTerminate(void);
int APS5_VABI sceNpAppLauncherInitialize(const void* initParam);
int APS5_VABI sceNpAppLauncherLaunchApp();
int APS5_VABI sceNpAppLauncherLaunchApp(std::int32_t arg0, const char* arg2, const char* arg3);
int APS5_VABI sceNpAppLauncherTerminate();
int APS5_VABI sceNpGameIntentGetUdsNpComId();
int APS5_VABI sceNpGameIntentLaunchApp();
+41
View File
@@ -2,10 +2,14 @@
#include "SceTypes.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <atomic>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stdexcept>
#include <thread>
#include <vector>
static void Require(bool value, const char* what) {
if (value) return;
@@ -28,6 +32,8 @@ int main() {
constexpr int kInvalidArgument = -2141898748;
constexpr int kIntentNotFound = -2141898746;
constexpr int kValueNotFound = -2141898745;
constexpr int kLauncherInvalidArgument = -2141898236;
constexpr int kLauncherNotInitialized = -2141898238;
Require(Throws<std::invalid_argument>([] { sceNpGameIntentInitParamInit(nullptr); }),
"null param init did not throw");
@@ -112,12 +118,47 @@ int main() {
unsigned char appLauncherParam[0x28]{};
*reinterpret_cast<std::uint64_t*>(appLauncherParam) = 0x28;
Require(sceNpAppLauncherLaunchApp(0, "app", nullptr) == kLauncherNotInitialized,
"launch before init must report not-initialized");
Require(sceNpAppLauncherInitialize(appLauncherParam) == 0, "app launcher init failed");
Require(sceNpAppLauncherLaunchApp(0, nullptr, nullptr) == kLauncherInvalidArgument,
"null launch argument must be invalid");
Require(sceNpAppLauncherLaunchApp(0, "", nullptr) == kLauncherInvalidArgument,
"empty launch argument must be invalid");
std::vector<char> unterminated(0x2001, 'x');
Require(sceNpAppLauncherLaunchApp(0, "app", unterminated.data()) == kLauncherInvalidArgument,
"non-terminated 0x2001-byte argument must be invalid");
Require(sceNpAppLauncherTerminate() == 0, "app launcher terminate failed");
Require(sceNpAppLauncherLaunchApp(0, "app", nullptr) == kLauncherNotInitialized,
"launch after terminate must report not-initialized");
*reinterpret_cast<std::uint64_t*>(appLauncherParam) = 0x10;
Require(sceNpAppLauncherInitialize(appLauncherParam) == 0,
"app launcher init must accept any init parameter");
std::atomic_bool stop{false};
std::thread writer([&] {
while (!stop.load(std::memory_order_relaxed)) {
sceNpAppLauncherInitialize(nullptr);
sceNpAppLauncherTerminate();
}
});
std::thread reader([&] {
while (!stop.load(std::memory_order_relaxed)) {
try {
(void)sceNpAppLauncherLaunchApp(0, "app", nullptr);
} catch (const std::runtime_error&) {
}
}
});
std::this_thread::sleep_for(std::chrono::milliseconds(50));
stop.store(true);
writer.join();
reader.join();
Require(sceNpAppLauncherTerminate() == 0, "final terminate failed");
Require(sceNpAppLauncherLaunchApp(0, "app", nullptr) == kLauncherNotInitialized,
"final launch must report not-initialized");
std::puts("NpGameIntent tests passed");
return 0;
}
+4 -3
View File
@@ -2,12 +2,13 @@
### Silent stubs
- [sceNpGameIntentVshInitialize, sceNpGameIntentVshTerminate](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - VSH initialization and termination return 0 without performing shell operations
- [sceNpAppLauncherInitialize, sceNpAppLauncherTerminate](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - app-launcher lifecycle returns success without tracking launch state; `sceNpAppLauncherInitialize` accepts its init parameter without validation — a title calls it early in boot, and returning success lets it continue
- [sceNpGameIntentVshInitialize, sceNpGameIntentVshTerminate](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - VSH initialization and termination return 0 without performing shell operations, matching `sceNpGameIntentInitialize` and `sceNpGameIntentTerminate`
- [sceNpAppLauncherInitialize, sceNpAppLauncherTerminate](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - app-launcher lifecycle returns success without performing launch work; `sceNpAppLauncherInitialize` accepts its init parameter without validation, following the silent-stub precedent of `sceNpGameIntentInitialize` — a title calls it early in boot, and returning success lets it continue; an atomic flag tracks initialized state so `sceNpAppLauncherLaunchApp` can reject calls made outside the lifecycle
### Unknown function info
- [sceNpGameIntentReceiveIntent, sceNpGameIntentGetPropertyValueString](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - firmware 13.42 traces report `0x80553802` before `sceNpGameIntentInitialize` (precedence over argument validity), and `0x80553804` for invalid arguments (null pointer, empty key, or zero buffer size); `sceNpGameIntentTerminate` resetting initialized state is modeled lifecycle behavior rather than hardware-verified. `sceNpGameIntentReceiveIntent` reportedly requires `info->size == 0x4140` on firmware 13.42, while AnyPS5 `NpGameIntentInfo` is `0x560` bytes (matching fpPS4) and set by `sceNpGameIntentInfoInit`, so initialized objects are rejected by the receiver's measured size guard. Complete retail layout, output field offsets, and retail behavior of `sceNpGameIntentInfoInit` are unverified; expanding the type with unverified reserved bytes is not an evidence-backed solution.
- [sceNpAppLauncherLaunchApp, sceNpGameIntentGetUdsNpComId, sceNpGameIntentLaunchApp, sceNpGameIntentLaunchApp2, sceNpGameIntentNotifyAppLaunched, sceNpSessionServiceGetJoinedPartySession, sceNpSessionServiceGetRunningGamePlayerSession and sceNpSessionServiceSendPlayerSessionInvitations](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpGameIntent.c). Call sites in a title's executable give a partial prototype: `sceNpAppLauncherLaunchApp` takes `(user_id, uri struct, context)` and returns int — throws because no launcher-service semantics are derivable. The rest are declared without parameters solely to resolve known imports and make no ABI claim.
- [sceNpAppLauncherLaunchApp](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent, NID `GbayQ7DO8jA`) - its recovered signature is `(int32_t, const char*, const char*)`: an unhandled first argument, a required non-empty string and an optional string rejected past 0x2000 bytes, returning `0x80553A02` before `sceNpAppLauncherInitialize` and `0x80553A04` on invalid arguments. The launch itself is not implemented and throws; the firmware tail calls an internal launch through an extra library context and remaps its `0x805538xx` errors into `0x80553Axx`
- [sceNpGameIntentGetUdsNpComId, sceNpGameIntentLaunchApp, sceNpGameIntentLaunchApp2, sceNpGameIntentNotifyAppLaunched, sceNpSessionServiceGetJoinedPartySession, sceNpSessionServiceGetRunningGamePlayerSession and sceNpSessionServiceSendPlayerSessionInvitations](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpGameIntent.c). They are declared without parameters solely to resolve known imports and make no ABI claim.
- [sceNpGameIntentInitParamInit](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - struct layout matches [fpPS4](https://github.com/red-prig/fpPS4/blob/master/src/np/ps4_libscenpgameintent.pas#L17) `SceNpGameIntentInitParam`; setting `size`, zeroing reserved bytes, and throwing on null pointer follow Sony SDK `*ParamInit` conventions but are unverified on retail hardware
- [sceNpGameIntentInfoInit](../../../core/libs/prx/libSceNpGameIntent/Export.cpp) (libSceNpGameIntent) - struct layout matches [fpPS4](https://github.com/red-prig/fpPS4/blob/master/src/np/ps4_libscenpgameintent.pas#L28) `SceNpGameIntentInfo`; setting `size = 0x560`, `user_id = -1`, zeroing fields, and throwing on null pointer follow fpPS4 defaults, but reported firmware 13.42 behavior requires `0x4140` for `sceNpGameIntentReceiveIntent`, so the helper's `0x560` size does not match retail receiver defaults; the complete retail structure layout and retail behavior of `sceNpGameIntentInfoInit` remain unverified