From cb8ad7c7776bcfcb16b8271e91f6baefb96616ee Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 21 Oct 2026 11:48:29 +0300 Subject: [PATCH] fix(npcommerce): restore dialog result data and return value --- .../prx/libSceCommonDialog/CommonDialog.hpp | 11 ++++++ core/libs/prx/libSceNpCommerce/Export.cpp | 17 +++++---- core/libs/tests/GuestNpCommerceDialog.cpp | 35 +++++++++++-------- docs/dev/debt/libSceNpCommerce.md | 2 +- 4 files changed, 42 insertions(+), 23 deletions(-) diff --git a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp index a9bea03571..b430b8ec87 100644 --- a/core/libs/prx/libSceCommonDialog/CommonDialog.hpp +++ b/core/libs/prx/libSceCommonDialog/CommonDialog.hpp @@ -29,6 +29,17 @@ struct PlayerReviewDialogParam { std::uint8_t reserved[64]; }; +struct NpCommerceDialogResult { + std::int32_t result; + std::uint8_t authorized; + std::uint8_t padding[3]; + std::uint64_t userData; + std::uint8_t reserved[32]; +}; + +static_assert(sizeof(NpCommerceDialogResult) == 48); +static_assert(offsetof(NpCommerceDialogResult, userData) == 8); + struct NpCommerceDialogParam { CommonDialogBaseParam base; std::uint32_t size; diff --git a/core/libs/prx/libSceNpCommerce/Export.cpp b/core/libs/prx/libSceNpCommerce/Export.cpp index b77de4d0b5..e224383c46 100644 --- a/core/libs/prx/libSceNpCommerce/Export.cpp +++ b/core/libs/prx/libSceNpCommerce/Export.cpp @@ -24,6 +24,7 @@ constexpr int COMMON_DIALOG_ERROR_INVALID_ARGUMENT = static_cast(0x80B8000A constexpr int COMMON_DIALOG_ERROR_NOT_RUNNING = static_cast(0x80B8000Bu); int g_status = COMMON_DIALOG_STATUS_NONE; +std::uint64_t g_userData = 0; std::uint32_t compiledSdkVersion() { const auto* process = static_cast(ApplicationProcessParameters_nid_no_patch()); @@ -106,6 +107,7 @@ int APS5_VABI sceNpCommerceDialogOpen(const NpCommerceDialogParam* param) { if (!ValidateCommonDialogBaseParam(param->base)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; if (g_status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; if (!validateParam(*param)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + g_userData = param->userData; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } @@ -118,16 +120,16 @@ int APS5_VABI sceNpCommerceDialogGetStatus(void) { return g_status; } -int APS5_VABI sceNpCommerceDialogGetResult(void* result) { +int APS5_VABI sceNpCommerceDialogGetResult(NpCommerceDialogResult* result) { if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; if (g_status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; if (g_status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; - // SceNpCommerceDialogResult: int32 result, bool authorized, then reserved bytes. - std::int32_t value = COMMON_DIALOG_RESULT_USER_CANCELED; - std::memcpy(result, &value, sizeof(value)); - std::uint8_t authorized = 0; - std::memcpy(static_cast(result) + sizeof(value), &authorized, sizeof(authorized)); - return 0; + const auto sdkVersion = compiledSdkVersion(); + result->result = COMMON_DIALOG_RESULT_USER_CANCELED; + result->authorized = 0; + result->userData = g_userData; + if (sdkVersion >= 0x01700000) std::fill(std::begin(result->reserved), std::end(result->reserved), 0); + return COMMON_DIALOG_RESULT_USER_CANCELED; } int APS5_VABI sceNpCommerceDialogClose(void) { @@ -162,6 +164,7 @@ int APS5_VABI sceNpCommerceDialogOpen2(const NpCommerceDialogParam2* param) { if (!ValidateCommonDialogBaseParam(param->base)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; if (g_status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; if (!validateParam2(*param)) return COMMON_DIALOG_ERROR_INVALID_ARGUMENT; + g_userData = param->userData; g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } diff --git a/core/libs/tests/GuestNpCommerceDialog.cpp b/core/libs/tests/GuestNpCommerceDialog.cpp index 1b2a6c9ed6..9cb602b2b0 100644 --- a/core/libs/tests/GuestNpCommerceDialog.cpp +++ b/core/libs/tests/GuestNpCommerceDialog.cpp @@ -17,7 +17,7 @@ int APS5_VABI sceNpCommerceDialogOpen(const NpCommerceDialogParam* param); int APS5_VABI sceNpCommerceDialogOpen2(const NpCommerceDialogParam2* param); int APS5_VABI sceNpCommerceDialogUpdateStatus(void); int APS5_VABI sceNpCommerceDialogGetStatus(void); -int APS5_VABI sceNpCommerceDialogGetResult(void* result); +int APS5_VABI sceNpCommerceDialogGetResult(NpCommerceDialogResult* result); int APS5_VABI sceNpCommerceDialogClose(void); int APS5_VABI sceNpCommerceDialogTerminate(); } @@ -33,15 +33,15 @@ constexpr int statusInitialized = 1; constexpr int statusFinished = 3; constexpr std::int32_t resultUserCanceled = 1; -void RequireCanceledResult() { - std::uint8_t result[0x20]; - std::memset(result, 0x33, sizeof(result)); - Require(sceNpCommerceDialogGetResult(result) == 0); - std::int32_t value = 0; - std::memcpy(&value, result, sizeof(value)); - Require(value == resultUserCanceled); - Require(result[4] == 0); - Require(result[5] == 0x33); +void RequireCanceledResult(std::uint64_t userData, bool clearsReserved) { + NpCommerceDialogResult result; + std::memset(&result, 0x33, sizeof(result)); + Require(sceNpCommerceDialogGetResult(&result) == resultUserCanceled); + Require(result.result == resultUserCanceled); + Require(result.authorized == 0); + Require(result.userData == userData); + for (const auto value : result.padding) Require(value == 0x33); + for (const auto value : result.reserved) Require(value == (clearsReserved ? 0 : 0x33)); } } @@ -56,6 +56,7 @@ int main() { param2.base.magic = static_cast(reinterpret_cast(¶m2)) - 1060003575u; param2.size = sizeof(param2); param2.userId = USER_SERVICE_INITIAL_USER_ID; + param2.userData = 0x123456789abcdef0; Require(sceNpCommerceDialogGetStatus() == statusNone); Require(sceNpCommerceDialogOpen(nullptr) == static_cast(0x80B8000Du)); Require(sceNpCommerceDialogOpen2(nullptr) == static_cast(0x80B8000Du)); @@ -69,18 +70,18 @@ int main() { Require(sceNpCommerceDialogTerminate() == static_cast(0x80B80003u)); Require(sceNpCommerceDialogClose() == static_cast(0x80B80003u)); Require(sceNpCommerceDialogGetResult(nullptr) == static_cast(0x80B8000Du)); - std::uint8_t result[48]{}; - Require(sceNpCommerceDialogGetResult(result) == static_cast(0x80B80003u)); + NpCommerceDialogResult result{}; + Require(sceNpCommerceDialogGetResult(&result) == static_cast(0x80B80003u)); Require(sceNpCommerceDialogInitialize() == 0); Require(sceNpCommerceDialogClose() == static_cast(0x80B8000Bu)); - Require(sceNpCommerceDialogGetResult(result) == static_cast(0x80B80005u)); + Require(sceNpCommerceDialogGetResult(&result) == static_cast(0x80B80005u)); Require(sceNpCommerceDialogGetResult(nullptr) == static_cast(0x80B8000Du)); Require(sceNpCommerceDialogGetStatus() == statusInitialized); Require(sceNpCommerceDialogOpen2(¶m2) == 0); Require(sceNpCommerceDialogUpdateStatus() == statusFinished); Require(sceNpCommerceDialogGetStatus() == statusFinished); - RequireCanceledResult(); + RequireCanceledResult(param2.userData, true); Require(sceNpCommerceDialogClose() == static_cast(0x80B8000Bu)); Require(sceNpCommerceDialogGetStatus() == statusFinished); @@ -158,6 +159,7 @@ int main() { param2.userId = -1; Require(sceNpCommerceDialogOpen2(¶m2) == static_cast(0x80B8000Au)); param2.userId = USER_SERVICE_INITIAL_USER_ID; + param2.userData = 0x123456789abcdef0; for (std::uint32_t mode : {1000u, 1001u}) { param.mode = mode; param.productCount = 0; @@ -174,7 +176,9 @@ int main() { sdkVersion = 0x01700000; std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); param.argument = 3; + param.userData = 0xfedcba9876543210; Require(sceNpCommerceDialogOpen(¶m) == 0); + RequireCanceledResult(param.userData, true); sdkVersion = 0x01600000; std::memcpy(process.data() + 16, &sdkVersion, sizeof(sdkVersion)); param.size = 0; @@ -183,9 +187,10 @@ int main() { param.reserved[31] = 1; param.argument = 7; Require(sceNpCommerceDialogOpen(¶m) == 0); + RequireCanceledResult(param.userData, false); Require(sceNpCommerceDialogOpen2(¶m2) == 0); Require(sceNpCommerceDialogUpdateStatus() == statusFinished); - RequireCanceledResult(); + RequireCanceledResult(param2.userData, false); Require(sceNpCommerceDialogTerminate() == 0); Require(sceNpCommerceDialogGetStatus() == statusNone); Require(sceUserServiceTerminate() == 0); diff --git a/docs/dev/debt/libSceNpCommerce.md b/docs/dev/debt/libSceNpCommerce.md index 33da178d0b..02559d2c86 100644 --- a/docs/dev/debt/libSceNpCommerce.md +++ b/docs/dev/debt/libSceNpCommerce.md @@ -7,6 +7,6 @@ ### Unknown function info - [sceNpCommerceDialogInitializeInternal](../../../core/libs/prx/libSceNpCommerce/Export.cpp) (libSceNpCommerce) - its name and NID are in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceNpCommerce.c); the no-parameter prototype follows [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/np/np_commerce/np_commerce.cpp#L61) and is not measured on a console. It is declared without parameters solely to resolve known imports; this is not an ABI claim, and every call throws. -- [sceNpCommerceDialogOpen2](../../../core/libs/prx/libSceNpCommerce/Export.cpp): extended content combinations outside content mode 0 and content mode 1 with dialog mode 1 throw when otherwise valid. The native restriction reads process sysctl field 68; its relation to host process metadata is unresolved. Result-buffer semantics and mode-specific meanings of the option and 64-bit argument remain unresolved. +- [sceNpCommerceDialogOpen2](../../../core/libs/prx/libSceNpCommerce/Export.cpp): extended content combinations outside content mode 0 and content mode 1 with dialog mode 1 throw when otherwise valid. The native restriction reads process sysctl field 68; its relation to host process metadata is unresolved. Mode-specific meanings of the option and 64-bit argument remain unresolved. - [sceNpCommerceDialogOpen](../../../core/libs/prx/libSceNpCommerce/Export.cpp): Product copying and service submission remain part of the cancelled UI stub.