From 8ef51df3037fae9502cb3ac82b494685d7edde1e Mon Sep 17 00:00:00 2001 From: Aryan Rawat <235018975+aryanrawatispro-ship-it@users.noreply.github.com> Date: Tue, 6 Oct 2026 11:42:02 +0000 Subject: [PATCH] feat(playerinvitationdialog): implement scePlayerInvitationDialogGetResult scePlayerInvitationDialogGetResult makes the common dialog checks of sceWebBrowserDialogGetResult: NOT_INITIALIZED before the dialog is initialized, ARG_NULL for a null result and NOT_FINISHED until the dialog finishes. The dialog shows no UI and sends no invitation, so the finished dialog reports USER_CANCELED with error code 0. The 40-byte result layout (error code, result, 32 reserved bytes) is taken from SharpProspero. Co-Authored-By: Claude Opus 5.5 --- .../libScePlayerInvitationDialog.cpp | 16 +++++++++++++--- .../libScePlayerInvitationDialog.h | 9 ++++++++- core/libs/tests/PlayerInvitationDialog.cpp | 16 ++++++++++++++++ docs/dev/TechnicalDebt.md | 4 ++-- 4 files changed, 39 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libScePlayerInvitationDialog/libScePlayerInvitationDialog.cpp b/core/libs/prx/libScePlayerInvitationDialog/libScePlayerInvitationDialog.cpp index 104080043..36965cc8e 100644 --- a/core/libs/prx/libScePlayerInvitationDialog/libScePlayerInvitationDialog.cpp +++ b/core/libs/prx/libScePlayerInvitationDialog/libScePlayerInvitationDialog.cpp @@ -6,6 +6,11 @@ namespace { +constexpr std::int32_t COMMON_DIALOG_RESULT_USER_CANCELED = 1; +constexpr auto COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); +constexpr auto COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); +constexpr auto COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); + std::mutex g_dialog_mutex; PlayerInvitationDialogStatus g_dialog_status = PlayerInvitationDialogStatus::None; @@ -67,9 +72,14 @@ std::int32_t APS5_VABI scePlayerInvitationDialogTerminate(void) { return 0; } -std::int32_t APS5_VABI scePlayerInvitationDialogGetResult(void* result) { - (void)result; - NotImplemented_nid_no_patch(__func__); +std::int32_t APS5_VABI scePlayerInvitationDialogGetResult(ScePlayerInvitationDialogResult* result) { + std::lock_guard lock(g_dialog_mutex); + APS5_LOG_OUT("result=%p status=%d", static_cast(result), status()); + if (g_dialog_status == PlayerInvitationDialogStatus::None) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (g_dialog_status != PlayerInvitationDialogStatus::Finished) return COMMON_DIALOG_ERROR_NOT_FINISHED; + *result = ScePlayerInvitationDialogResult{}; + result->result = COMMON_DIALOG_RESULT_USER_CANCELED; return 0; } diff --git a/core/libs/prx/libScePlayerInvitationDialog/libScePlayerInvitationDialog.h b/core/libs/prx/libScePlayerInvitationDialog/libScePlayerInvitationDialog.h index a8a4d2724..a52d8dd90 100644 --- a/core/libs/prx/libScePlayerInvitationDialog/libScePlayerInvitationDialog.h +++ b/core/libs/prx/libScePlayerInvitationDialog/libScePlayerInvitationDialog.h @@ -16,6 +16,13 @@ struct ScePlayerInvitationDialogParam { std::uint8_t reserved[1]; }; +struct ScePlayerInvitationDialogResult { + std::int32_t errorCode; + std::int32_t result; + std::uint8_t reserved[32]; +}; +static_assert(sizeof(ScePlayerInvitationDialogResult) == 40); + extern "C" { std::int32_t APS5_VABI scePlayerInvitationDialogInitialize(void); @@ -24,7 +31,7 @@ std::int32_t APS5_VABI scePlayerInvitationDialogUpdateStatus(void); std::int32_t APS5_VABI scePlayerInvitationDialogGetStatus(void); std::int32_t APS5_VABI scePlayerInvitationDialogClose(void); std::int32_t APS5_VABI scePlayerInvitationDialogTerminate(void); -std::int32_t APS5_VABI scePlayerInvitationDialogGetResult(void* result); +std::int32_t APS5_VABI scePlayerInvitationDialogGetResult(ScePlayerInvitationDialogResult* result); } diff --git a/core/libs/tests/PlayerInvitationDialog.cpp b/core/libs/tests/PlayerInvitationDialog.cpp index 34fa83ede..4cbd37064 100644 --- a/core/libs/tests/PlayerInvitationDialog.cpp +++ b/core/libs/tests/PlayerInvitationDialog.cpp @@ -1,6 +1,7 @@ #include "prx/libScePlayerInvitationDialog/libScePlayerInvitationDialog.h" #include +#include namespace { @@ -14,6 +15,12 @@ void expect(std::int32_t actual, std::int32_t expected) { int main() { using enum PlayerInvitationDialogStatus; + constexpr auto notInitialized = static_cast(0x80B80003u); + constexpr auto notFinished = static_cast(0x80B80005u); + constexpr auto argNull = static_cast(0x80B8000Du); + constexpr std::int32_t userCanceled = 1; + ScePlayerInvitationDialogResult result{}; + expect(scePlayerInvitationDialogGetResult(&result), notInitialized); expect(scePlayerInvitationDialogUpdateStatus(), static_cast(None)); expect(scePlayerInvitationDialogClose(), 0); expect(scePlayerInvitationDialogTerminate(), 0); @@ -22,17 +29,26 @@ int main() { expect(scePlayerInvitationDialogInitialize(), 0); expect(scePlayerInvitationDialogGetStatus(), static_cast(Initialized)); expect(scePlayerInvitationDialogUpdateStatus(), static_cast(Initialized)); + expect(scePlayerInvitationDialogGetResult(nullptr), argNull); + expect(scePlayerInvitationDialogGetResult(&result), notFinished); ScePlayerInvitationDialogParam param{}; expect(scePlayerInvitationDialogOpen(¶m), 0); expect(scePlayerInvitationDialogGetStatus(), static_cast(Running)); + expect(scePlayerInvitationDialogGetResult(&result), notFinished); expect(scePlayerInvitationDialogUpdateStatus(), static_cast(Finished)); + std::memset(&result, 0xFF, sizeof(result)); + expect(scePlayerInvitationDialogGetResult(&result), 0); + expect(result.errorCode, 0); + expect(result.result, userCanceled); + for (const std::uint8_t byte : result.reserved) expect(byte, 0); expect(scePlayerInvitationDialogUpdateStatus(), static_cast(Finished)); expect(scePlayerInvitationDialogOpen(nullptr), 0); expect(scePlayerInvitationDialogClose(), 0); expect(scePlayerInvitationDialogGetStatus(), static_cast(Finished)); expect(scePlayerInvitationDialogTerminate(), 0); expect(scePlayerInvitationDialogGetStatus(), static_cast(None)); + expect(scePlayerInvitationDialogGetResult(&result), notInitialized); expect(scePlayerInvitationDialogOpen(nullptr), 0); expect(scePlayerInvitationDialogUpdateStatus(), static_cast(Finished)); diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 645d976a4..46e321b56 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -93,7 +93,7 @@ Throughout the project, every function at every stage either **does exactly what - [BnMAMrsfVWo](../../core/libs/prx/libc/src/HeapDiagnostics.cpp) (libc) - unknown name, signature - [scePngEncEncode](../../core/libs/prx/libScePngEnc/Export.cpp) (libScePngEnc) - whether rows may stay unfiltered when `filter_type` names a subset of the filters is unknown; as in shadPS4, they may only for 0 and for the all-filters mask - [sceVoiceQoSInit](../../core/libs/prx/libSceVoiceQoS/Export.cpp) (libSceVoiceQoS) - the error codes are unknown: a null or empty memory block, an app type other than 0x20000000 (PPSA14632) or 0x10000000 (PPSA26344) and a second initialization throw. The minimum memory size is unknown; the app type and the block are not used, as no endpoint is implemented -- AudioIn, NpSessionSignaling and PlayerInvitationDialog exports added without an implementation have assumed signatures +- AudioIn and NpSessionSignaling exports added without an implementation have assumed signatures - [vieBRwlh1Lw](../../core/libs/prx/libSceAgc/Unimplemented.cpp) (libSceAgc) - unknown name, signature - [fCWdlnmB1Ks](../../core/libs/prx/libScePad/Export.cpp) (libScePad) - unknown name, signature - [sceKernelGetOperationMode](../../core/libs/prx/libkernel/System/src/Process.cpp) (libkernel) - the signature `(int* mode, int* submode)` comes from PPSA12544, which logs both values after the call; the values are unknown and 0 is reported for both, as in the [prosper](https://github.com/mattias800/prosper/pull/4153) reimplementation. Null outputs throw @@ -166,7 +166,7 @@ Throughout the project, every function at every stage either **does exactly what - [sceAjmDecMp3ParseFrame](../../core/libs/prx/libSceAjm.native/src/Ajm.cpp) (libSceAjm.native) throws when the original file length is requested (`parse_ofl` non-zero) for a layer other than III, for a CRC-protected frame, or for a VBRI header outside MPEG-1 stereo: shadPS4 looks for the tag after the CRC and after the side information, while LAME and FFmpeg place it without the CRC and VBRI always at byte 36. - The [output modifier](../../core/shader/recompiler/Translation/src/TranslationContext/OperandAccess.cpp) (`mul:2`, `mul:4`, `div:2`) follows the hardware with the IEEE mode off and f32 denormals flushed, the LLVM graphics default; the shader's MODE register is not read, and the hardware ignores the modifier in the other modes. On f16 results it is ignored, as on the hardware while f16 denormals are enabled; with them flushed the hardware applies it. - [Comparison sampling of a color texture](../../core/shader/recompiler/SpirvBackend/src/SpirvImageEmitter.cpp) (`image_sample_c`/`image_sample_c_lz` on a texture that is not R32 float or R16 unorm) is emulated in the shader, since Vulkan compares only depth formats: the red channel is fetched, compared and, for bilinear filtering, blended (compare, then filter). Only 2D and 2D array views, float/unorm/snorm formats, wrap, clamp-to-edge and clamp-to-border addressing (border red 0 for the black borders, 1 for opaque white) and point or bilinear filtering at one level are implemented; half-border addressing, the border color table, gradients, LOD bias, offsets, LOD clamps, gathers, other dimensions, anisotropy and mip filtering throw. Under bilinear filtering, taps past a clamp-to-border edge compare the border value and blend with the texel taps, so results near the edge depend on the filter weights. AMD documentation does not say how a color format's reference is handled: it is clamped to [0, 1] for unorm and [-1, 1] for snorm, as Vulkan does for unorm depth, and not clamped for float. Bilinear weights are full float, not the hardware's fixed-point subtexel weights -- [libScePlayerInvitationDialog](../../core/libs/prx/libScePlayerInvitationDialog/libScePlayerInvitationDialog.cpp) simulates dialog completion without displaying UI or sending invitations; its parameter ABI remains unverified. +- [libScePlayerInvitationDialog](../../core/libs/prx/libScePlayerInvitationDialog/libScePlayerInvitationDialog.cpp) simulates dialog completion without displaying UI or sending invitations, so `scePlayerInvitationDialogGetResult` reports `USER_CANCELED`; its parameter ABI remains unverified. The 40-byte result layout (error code, result, 32 reserved bytes) is taken from [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/Dialog/PlayerInvitationDialog.cs) and the error codes from the other common dialogs, not confirmed on a title. - [64-bit LDS atomics](../../core/shader/recompiler/SpirvBackend/src/SpirvMemory/SpirvMemoryInstructions.cpp) (`ds_*_u64`, `ds_*_b64`, `ds_*_f64`) read and write the two dwords under a workgroup lock stored after the guest LDS, as LDS is a 32-bit array without 64-bit atomics. They are atomic with respect to each other only: 32-bit accesses to the same dwords issued concurrently are not ordered by the lock. 64-bit GDS atomics throw. `ds_min/max(_rtn)_f64` and `ds_cmpst(_rtn)_f64` follow the NaN and signed-zero rules measured for the f32 forms; the f64 forms were not measured on hardware. - [wcsrtombs_s](../../core/libs/prx/libc/src/Multibyte.cpp) (libc) follows the console's `libc.prx`, but a runtime-constraint violation only returns its error: the default handler there writes the message and a newline to `stderr` before returning, which is not done here. Conversion uses the C locale of `wcrtomb` (`MB_CUR_MAX` 1), so the path where a multibyte character does not fit the remaining space is not reached - [Thread exit destructors](../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) run when a libkernel thread finishes; on the main or a host thread only `_sceLibcInternalThreadDtors` runs them, as `exit` does, else they are dropped