Merge pull request #10239 from voidlakhan/fix/npgameintent-invalid-argument-error

fix(libSceNpGameIntent): return errors for invalid arguments and uninitialized calls
This commit is contained in:
Dean authored and GitHub committed 2026-10-26 16:23:39 +13:00
commit 034b60a2d4
3 files changed
+94 -22

No files matched your search

+30 -15
View File
@@ -1,4 +1,5 @@
#include "prx/libSceNpGameIntent/include/Api.hpp"
#include <atomic>
#include <cstdint>
#include <cstddef>
#include <cstring>
@@ -8,43 +9,57 @@
namespace {
constexpr int NpGameIntentErrorNotInitialized = -2141898750;
constexpr int NpGameIntentErrorInvalidArgument = -2141898748;
constexpr int NpGameIntentErrorIntentNotFound = -2141898746;
constexpr int NpGameIntentErrorValueNotFound = -2141898745;
constexpr int NpGameIntentUserIdInvalid = -1;
std::atomic_bool& NpGameIntentInitialized() {
static std::atomic_bool initialized{false};
return initialized;
}
}
extern "C" {
int APS5_VABI sceNpGameIntentGetPropertyValueString(const NpGameIntentData* intentData, const char* key, char* valueBuf, size_t bufSize) {
if (intentData == nullptr || key == nullptr || valueBuf == nullptr || bufSize == 0) {
APS5_INVALID_ARG_EX;
}
if (!NpGameIntentInitialized()) {
return NpGameIntentErrorNotInitialized;
}
if (intentData == nullptr || key == nullptr || key[0] == '\0' || valueBuf == nullptr || bufSize == 0) {
return NpGameIntentErrorInvalidArgument;
}
valueBuf[0] = '\0';
return NpGameIntentErrorValueNotFound;
valueBuf[0] = '\0';
return NpGameIntentErrorValueNotFound;
}
int APS5_VABI sceNpGameIntentInitialize(const void* initParam) {
(void)initParam;
return 0;
(void)initParam;
NpGameIntentInitialized() = true;
return 0;
}
int APS5_VABI sceNpGameIntentReceiveIntent(NpGameIntentInfo* intentInfo) {
if (intentInfo == nullptr) {
APS5_INVALID_ARG_EX;
}
if (!NpGameIntentInitialized()) {
return NpGameIntentErrorNotInitialized;
}
if (intentInfo == nullptr || intentInfo->size != 0x4140) {
return NpGameIntentErrorInvalidArgument;
}
intentInfo->user_id = NpGameIntentUserIdInvalid;
std::memset(intentInfo->intent_type, 0, sizeof(intentInfo->intent_type));
std::memset(&intentInfo->intent_data, 0, sizeof(intentInfo->intent_data));
intentInfo->user_id = NpGameIntentUserIdInvalid;
std::memset(intentInfo->intent_type, 0, sizeof(intentInfo->intent_type));
std::memset(&intentInfo->intent_data, 0, sizeof(intentInfo->intent_data));
return NpGameIntentErrorIntentNotFound;
return NpGameIntentErrorIntentNotFound;
}
int APS5_VABI sceNpGameIntentTerminate(void) {
return 0;
NpGameIntentInitialized() = false;
return 0;
}
int APS5_VABI sceNpGameIntentInitParamInit(NpGameIntentInitParam* param) {
+60 -4
View File
@@ -4,6 +4,7 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <stdexcept>
static void Require(bool value, const char* what) {
@@ -23,6 +24,8 @@ static bool Throws(TCall call) {
}
int main() {
constexpr int kNotInitialized = -2141898750;
constexpr int kInvalidArgument = -2141898748;
constexpr int kIntentNotFound = -2141898746;
constexpr int kValueNotFound = -2141898745;
@@ -41,22 +44,75 @@ int main() {
Require(info.size == sizeof(NpGameIntentInfo), "info size mismatch");
Require(info.user_id == -1, "info user_id mismatch");
char preBuf[64]{};
Require(sceNpGameIntentGetPropertyValueString(nullptr, "key", preBuf, sizeof(preBuf)) == kNotInitialized,
"uninitialized get property string with null data must report not-initialized");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", preBuf, sizeof(preBuf)) == kNotInitialized,
"uninitialized get property string with valid data must report not-initialized");
Require(sceNpGameIntentReceiveIntent(nullptr) == kNotInitialized,
"uninitialized receive intent with null pointer must report not-initialized");
Require(sceNpGameIntentReceiveIntent(&info) == kNotInitialized,
"uninitialized receive intent with valid pointer must report not-initialized");
Require(sceNpGameIntentInitialize(&initParam) == 0, "initialization failed");
Require(sceNpGameIntentReceiveIntent(&info) == kIntentNotFound, "receive intent mismatch");
Require(sceNpGameIntentReceiveIntent(nullptr) == kInvalidArgument, "null receive intent did not fail");
Require(sceNpGameIntentReceiveIntent(&info) == kInvalidArgument, "mismatched struct size must report invalid argument");
info.size = 0;
Require(sceNpGameIntentReceiveIntent(&info) == kInvalidArgument, "zero size must report invalid argument");
info.size = 0x413f;
Require(sceNpGameIntentReceiveIntent(&info) == kInvalidArgument, "0x413f size must report invalid argument");
info.size = 0x4141;
Require(sceNpGameIntentReceiveIntent(&info) == kInvalidArgument, "0x4141 size must report invalid argument");
info.size = sizeof(NpGameIntentInfo);
char buf[64]{};
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", buf, sizeof(buf)) == kValueNotFound,
Require(sceNpGameIntentGetPropertyValueString(nullptr, "key", buf, sizeof(buf)) == kInvalidArgument,
"null intent data did not fail");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, nullptr, buf, sizeof(buf)) == kInvalidArgument,
"null key did not fail");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "", buf, sizeof(buf)) == kInvalidArgument,
"empty key did not fail");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", nullptr, sizeof(buf)) == kInvalidArgument,
"null value buf did not fail");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", buf, 0) == kInvalidArgument,
"zero buf size did not fail");
char canaryBuf[64]{};
std::memset(canaryBuf, 'X', sizeof(canaryBuf));
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", canaryBuf, 0) == kInvalidArgument,
"zero buf size canary call did not fail");
Require(canaryBuf[0] == 'X', "buffer was modified on zero size error");
Require(sceNpGameIntentGetPropertyValueString(nullptr, "key", canaryBuf, sizeof(canaryBuf)) == kInvalidArgument,
"null data canary call did not fail");
Require(canaryBuf[0] == 'X', "buffer was modified on null data error");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, nullptr, canaryBuf, sizeof(canaryBuf)) == kInvalidArgument,
"null key canary call did not fail");
Require(canaryBuf[0] == 'X', "buffer was modified on null key error");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "", canaryBuf, sizeof(canaryBuf)) == kInvalidArgument,
"empty key canary call did not fail");
Require(canaryBuf[0] == 'X', "buffer was modified on empty key error");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", canaryBuf, sizeof(canaryBuf)) == kValueNotFound,
"get property value mismatch");
Require(canaryBuf[0] == '\0', "valid call did not write terminator");
canaryBuf[0] = 'Y';
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", canaryBuf, 1) == kValueNotFound,
"bufSize 1 did not succeed");
Require(canaryBuf[0] == '\0', "bufSize 1 did not write terminator");
Require(sceNpGameIntentVshInitialize() == 0, "vsh init failed");
Require(sceNpGameIntentVshTerminate() == 0, "vsh terminate failed");
Require(sceNpGameIntentTerminate() == 0, "terminate failed");
Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", buf, sizeof(buf)) == kNotInitialized,
"post-terminate get property value must report not-initialized");
Require(sceNpGameIntentReceiveIntent(&info) == kNotInitialized,
"post-terminate receive intent must report not-initialized");
unsigned char appLauncherParam[0x28]{};
*reinterpret_cast<std::uint64_t*>(appLauncherParam) = 0x28;
Require(sceNpAppLauncherInitialize(appLauncherParam) == 0, "app launcher init failed");
Require(Throws<std::runtime_error>([] { sceNpAppLauncherLaunchApp(); }),
"app launcher launch must throw until implemented");
Require(sceNpAppLauncherTerminate() == 0, "app launcher terminate failed");
*reinterpret_cast<std::uint64_t*>(appLauncherParam) = 0x10;
Require(sceNpAppLauncherInitialize(appLauncherParam) == 0,
+4 -3
View File
@@ -2,11 +2,12 @@
### Silent stubs
- [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 tracking launch state; `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
- [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
### 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.
- [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`, `user_id = -1`, zeroing fields, and throwing on null pointer match `sceNpGameIntentReceiveIntent` defaults 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