From fa94b2cfc8a236ae61fed42863e2ef9925d1a40e Mon Sep 17 00:00:00 2001 From: fogydes Date: Thu, 15 Oct 2026 16:04:41 +0530 Subject: [PATCH] feat(libSceNpGameIntent): export missing firmware game intent interfaces --- core/libs/CMakeLists.txt | 10 +++ core/libs/SceTypes.hpp | 5 ++ core/libs/prx/libSceNpGameIntent/Export.cpp | 27 +++++++++ core/libs/tests/GuestNpGameIntent.cpp | 67 +++++++++++++++++++++ docs/dev/debt/libSceNpGameIntent.md | 10 +++ 5 files changed, 119 insertions(+) create mode 100644 core/libs/tests/GuestNpGameIntent.cpp create mode 100644 docs/dev/debt/libSceNpGameIntent.md diff --git a/core/libs/CMakeLists.txt b/core/libs/CMakeLists.txt index fa973fa7e4..b93c7d607e 100644 --- a/core/libs/CMakeLists.txt +++ b/core/libs/CMakeLists.txt @@ -606,4 +606,14 @@ if(BUILD_TESTING) set_tests_properties(guest_voice_chat PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") endif() + + add_test_executable(guest_np_game_intent_tests tests/GuestNpGameIntent.cpp) + target_include_directories(guest_np_game_intent_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(guest_np_game_intent_tests PRIVATE libSceNpGameIntent libc) + configure_windows_unwind(guest_np_game_intent_tests) + add_test(NAME guest_np_game_intent COMMAND guest_np_game_intent_tests) + if(WIN32) + set_tests_properties(guest_np_game_intent PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() endif() diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index 4250fda744..d46f23177a 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -1439,6 +1439,11 @@ struct NpGameIntentData { std::uint8_t padding[7]; }; +struct NpGameIntentInitParam { + std::size_t size; + std::uint8_t reserved[32]; +}; + struct NpGameIntentInfo { std::size_t size; std::int32_t user_id; diff --git a/core/libs/prx/libSceNpGameIntent/Export.cpp b/core/libs/prx/libSceNpGameIntent/Export.cpp index 4df4d87fd4..22fd576fae 100644 --- a/core/libs/prx/libSceNpGameIntent/Export.cpp +++ b/core/libs/prx/libSceNpGameIntent/Export.cpp @@ -46,4 +46,31 @@ int APS5_VABI sceNpGameIntentTerminate(void) { return 0; } +int APS5_VABI sceNpGameIntentInitParamInit(NpGameIntentInitParam* param) { + if (param == nullptr) { + APS5_INVALID_ARG_EX; + } + std::memset(param, 0, sizeof(NpGameIntentInitParam)); + param->size = sizeof(NpGameIntentInitParam); + return 0; +} + +int APS5_VABI sceNpGameIntentInfoInit(NpGameIntentInfo* info) { + if (info == nullptr) { + APS5_INVALID_ARG_EX; + } + std::memset(info, 0, sizeof(NpGameIntentInfo)); + info->size = sizeof(NpGameIntentInfo); + info->user_id = NpGameIntentUserIdInvalid; + return 0; +} + +int APS5_VABI sceNpGameIntentVshInitialize(void) { + return 0; +} + +int APS5_VABI sceNpGameIntentVshTerminate(void) { + return 0; +} + } diff --git a/core/libs/tests/GuestNpGameIntent.cpp b/core/libs/tests/GuestNpGameIntent.cpp new file mode 100644 index 0000000000..8d63fecbe1 --- /dev/null +++ b/core/libs/tests/GuestNpGameIntent.cpp @@ -0,0 +1,67 @@ +#include "SceTypes.hpp" +#include "prx/libc/include/general/VabiMacros.hpp" + +#include +#include +#include + +extern "C" { +int APS5_VABI sceNpGameIntentInitParamInit(NpGameIntentInitParam* param); +int APS5_VABI sceNpGameIntentInfoInit(NpGameIntentInfo* info); +int APS5_VABI sceNpGameIntentInitialize(const void* initParam); +int APS5_VABI sceNpGameIntentReceiveIntent(NpGameIntentInfo* intentInfo); +int APS5_VABI sceNpGameIntentGetPropertyValueString(const NpGameIntentData* intentData, const char* key, char* valueBuf, size_t bufSize); +int APS5_VABI sceNpGameIntentVshInitialize(void); +int APS5_VABI sceNpGameIntentVshTerminate(void); +int APS5_VABI sceNpGameIntentTerminate(void); +} + +static void Require(bool value, const char* what) { + if (value) return; + std::printf("NpGameIntent: %s\n", what); + std::abort(); +} + +template +static bool Throws(TCall call) { + try { + call(); + } catch (const TError&) { + return true; + } + return false; +} + +int main() { + constexpr int kIntentNotFound = -2141898746; + constexpr int kValueNotFound = -2141898745; + + Require(Throws([] { sceNpGameIntentInitParamInit(nullptr); }), + "null param init did not throw"); + + NpGameIntentInitParam initParam{}; + Require(sceNpGameIntentInitParamInit(&initParam) == 0, "param init failed"); + Require(initParam.size == sizeof(NpGameIntentInitParam), "param size mismatch"); + + Require(Throws([] { sceNpGameIntentInfoInit(nullptr); }), + "null info init did not throw"); + + NpGameIntentInfo info{}; + Require(sceNpGameIntentInfoInit(&info) == 0, "info init failed"); + Require(info.size == sizeof(NpGameIntentInfo), "info size mismatch"); + Require(info.user_id == -1, "info user_id mismatch"); + + Require(sceNpGameIntentInitialize(&initParam) == 0, "initialization failed"); + Require(sceNpGameIntentReceiveIntent(&info) == kIntentNotFound, "receive intent mismatch"); + + char buf[64]{}; + Require(sceNpGameIntentGetPropertyValueString(&info.intent_data, "key", buf, sizeof(buf)) == kValueNotFound, + "get property value mismatch"); + + Require(sceNpGameIntentVshInitialize() == 0, "vsh init failed"); + Require(sceNpGameIntentVshTerminate() == 0, "vsh terminate failed"); + Require(sceNpGameIntentTerminate() == 0, "terminate failed"); + + std::puts("NpGameIntent tests passed"); + return 0; +} diff --git a/docs/dev/debt/libSceNpGameIntent.md b/docs/dev/debt/libSceNpGameIntent.md new file mode 100644 index 0000000000..681c6dbf8f --- /dev/null +++ b/docs/dev/debt/libSceNpGameIntent.md @@ -0,0 +1,10 @@ +# libSceNpGameIntent technical debt + +### 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` + +### Unknown function info + +- [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