From 4b91755d9a4081d5cb0223261a379d859998ef5f Mon Sep 17 00:00:00 2001 From: virp-mta Date: Sun, 11 Oct 2026 19:01:00 +0400 Subject: [PATCH] feat(libSceSigninDialog): complete offline sign-in dialog lifecycle --- core/libs/CMakeLists.txt | 10 +++ core/libs/prx/libSceSigninDialog/Export.cpp | 22 ++++-- core/libs/tests/GuestSigninDialog.cpp | 77 +++++++++++++++++++++ docs/dev/TechnicalDebt.md | 1 + 4 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 core/libs/tests/GuestSigninDialog.cpp diff --git a/core/libs/CMakeLists.txt b/core/libs/CMakeLists.txt index 2da76ed8e..f02604ae4 100644 --- a/core/libs/CMakeLists.txt +++ b/core/libs/CMakeLists.txt @@ -188,6 +188,16 @@ if(BUILD_TESTING) "PATH=path_list_prepend:$") endif() + add_test_executable(guest_signin_dialog_tests tests/GuestSigninDialog.cpp) + target_include_directories(guest_signin_dialog_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(guest_signin_dialog_tests PRIVATE libSceSigninDialog libc) + configure_windows_unwind(guest_signin_dialog_tests) + add_test(NAME guest_signin_dialog COMMAND guest_signin_dialog_tests) + if(WIN32) + set_tests_properties(guest_signin_dialog PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() + add_test_executable(guest_rudp_status_tests tests/GuestRudpStatus.cpp) target_include_directories(guest_rudp_status_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(guest_rudp_status_tests PRIVATE libSceRudp libc) diff --git a/core/libs/prx/libSceSigninDialog/Export.cpp b/core/libs/prx/libSceSigninDialog/Export.cpp index ea21b63b2..761bf3330 100644 --- a/core/libs/prx/libSceSigninDialog/Export.cpp +++ b/core/libs/prx/libSceSigninDialog/Export.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" @@ -12,8 +13,15 @@ constexpr int COMMON_DIALOG_STATUS_RUNNING = 2; constexpr int COMMON_DIALOG_STATUS_FINISHED = 3; constexpr int COMMON_DIALOG_ERROR_NOT_INITIALIZED = static_cast(0x80B80003u); constexpr int COMMON_DIALOG_ERROR_ALREADY_INITIALIZED = static_cast(0x80B80004u); +constexpr int COMMON_DIALOG_ERROR_NOT_FINISHED = static_cast(0x80B80005u); constexpr int COMMON_DIALOG_ERROR_BUSY = static_cast(0x80B80007u); constexpr int COMMON_DIALOG_ERROR_ARG_NULL = static_cast(0x80B8000Du); +constexpr int COMMON_DIALOG_RESULT_USER_CANCELED = 1; + +struct SceSigninDialogResult { + std::int32_t result = COMMON_DIALOG_RESULT_USER_CANCELED; + std::int32_t reserved[3] = {}; +}; std::atomic g_status{COMMON_DIALOG_STATUS_NONE}; @@ -22,19 +30,23 @@ std::atomic g_status{COMMON_DIALOG_STATUS_NONE}; extern "C" { int APS5_VABI sceSigninDialogClose(void) { - NotImplemented_nid_no_patch(__func__); + if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + g_status = COMMON_DIALOG_STATUS_FINISHED; return 0; } int APS5_VABI sceSigninDialogGetResult(void* result) { - (void)result; - NotImplemented_nid_no_patch(__func__); + const int status = g_status.load(); + if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; + if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + const SceSigninDialogResult dialogResult{}; + std::memcpy(result, &dialogResult, sizeof(dialogResult)); return 0; } int APS5_VABI sceSigninDialogGetStatus(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return g_status.load(); } int APS5_VABI sceSigninDialogInitialize(void) { diff --git a/core/libs/tests/GuestSigninDialog.cpp b/core/libs/tests/GuestSigninDialog.cpp new file mode 100644 index 000000000..d8410fcbd --- /dev/null +++ b/core/libs/tests/GuestSigninDialog.cpp @@ -0,0 +1,77 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include +#include + +extern "C" { +int APS5_VABI sceSigninDialogInitialize(void); +int APS5_VABI sceSigninDialogOpen(const void* param); +int APS5_VABI sceSigninDialogUpdateStatus(void); +int APS5_VABI sceSigninDialogGetStatus(void); +int APS5_VABI sceSigninDialogGetResult(void* result); +int APS5_VABI sceSigninDialogClose(void); +int APS5_VABI sceSigninDialogTerminate(void); +} + +namespace { + +void Require(bool value) { if (!value) std::abort(); } + +constexpr int kStatusNone = 0; +constexpr int kStatusInitialized = 1; +constexpr int kStatusFinished = 3; +constexpr int kErrNotInitialized = static_cast(0x80B80003); +constexpr int kErrAlreadyInitialized = static_cast(0x80B80004); +constexpr int kErrNotFinished = static_cast(0x80B80005); +constexpr int kErrArgNull = static_cast(0x80B8000D); +constexpr int kResultUserCanceled = 1; + +} + +int main() { + std::int64_t param = 0; + std::int32_t result[4]; + std::memset(result, 0x5a, sizeof(result)); + + Require(sceSigninDialogTerminate() == kErrNotInitialized); + Require(sceSigninDialogOpen(¶m) == kErrNotInitialized); + Require(sceSigninDialogClose() == kErrNotInitialized); + Require(sceSigninDialogGetResult(result) == kErrNotInitialized); + Require(sceSigninDialogGetStatus() == kStatusNone); + Require(sceSigninDialogUpdateStatus() == kStatusNone); + + Require(sceSigninDialogInitialize() == 0); + Require(sceSigninDialogInitialize() == kErrAlreadyInitialized); + Require(sceSigninDialogGetStatus() == kStatusInitialized); + + Require(sceSigninDialogOpen(nullptr) == kErrArgNull); + Require(sceSigninDialogGetResult(result) == kErrNotFinished); + + Require(sceSigninDialogOpen(¶m) == 0); + Require(sceSigninDialogGetStatus() == kStatusFinished); + Require(sceSigninDialogUpdateStatus() == kStatusFinished); + + Require(sceSigninDialogGetResult(nullptr) == kErrArgNull); + Require(sceSigninDialogGetResult(result) == 0); + Require(result[0] == kResultUserCanceled); + Require(result[1] == 0); + Require(result[2] == 0); + Require(result[3] == 0); + + Require(sceSigninDialogClose() == 0); + Require(sceSigninDialogTerminate() == 0); + Require(sceSigninDialogTerminate() == kErrNotInitialized); + Require(sceSigninDialogGetStatus() == kStatusNone); + + Require(sceSigninDialogInitialize() == 0); + Require(sceSigninDialogGetStatus() == kStatusInitialized); + Require(sceSigninDialogClose() == 0); + Require(sceSigninDialogGetStatus() == kStatusFinished); + std::memset(result, 0xa5, sizeof(result)); + Require(sceSigninDialogGetResult(result) == 0); + Require(result[0] == kResultUserCanceled); + Require(result[1] == 0); + Require(result[2] == 0); + Require(result[3] == 0); + Require(sceSigninDialogTerminate() == 0); +} diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 0c72b62a7..4e22d6d95 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -79,6 +79,7 @@ Throughout the project, every function at every stage either **does exactly what - PPSA01341 imports declared without parameters, signatures unknown: [sceAgcSetSemaphoreMemory](../../core/libs/prx/libSceAgc/Unimplemented.cpp). Name from the shadPS4 aerolib NID list - [sceAppContentDownloadDataGetAvailableSpaceKb](../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - a title whose `param.json` declares `downloadDataSize` 0 gets 0 KB and OK; the console's behaviour for a title without a download data area is unverified (it may return an error instead). KytyPS5 does not special-case it, and shadPS4 stubs 1048576 KB - [sceNpEAAccessInitialize, sceNpEAAccessTerminate](../../core/libs/prx/libSceNpPartner001/Export.cpp) (libSceNpPartner001) - names and the no-argument signatures follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/np/np_partner.cpp); imported by PPSA23566, not confirmed on a PS5 title +- [sceSigninDialogClose, sceSigninDialogGetResult](../../core/libs/prx/libSceSigninDialog/Export.cpp) (libSceSigninDialog) - SceSigninDialogResult layout (int32 result and 3 reserved int32 words, zeroed) follows [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/dialog.cpp) and [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/main/src/SharpProspero/Interop/SigninDialog/SigninDialog.cs); error codes follow libSceLoginDialog's 0x80B8xxxx common-dialog codes rather than KytyPS5's 0x8135xxxx, and offline calls finish as user cancelled without opening a dialog. Neither choice is verified on hardware - [libSceBluetoothHid](../../core/libs/prx/libSceBluetoothHid/Export.cpp) (libSceBluetoothHid) - signatures taken from PPSA01317: `sceBluetoothHidInit()` and `sceBluetoothHidUnregisterCallback()` without arguments, `sceBluetoothHidRegisterCallback(callback, 0, param)` with a 16-byte param, and `sceBluetoothHidRegisterDevice` / `sceBluetoothHidUnregisterDevice(vendorId, productId)`; the report and device name exports are declared without parameters - [sceAgcWaitRegMemPatchMask](../../core/libs/prx/libSceAgc/Patch/src/WaitRegMem.cpp) (libSceAgc) - follows `sceAgcWaitRegMemPatchReference`: the mask is taken as a 32-bit value and written to the low mask word of a 32- or 64-bit wait, as [OpenAGC](https://github.com/OpenAGC/OpenAGC/blob/main/src/game_compat.c) does; [sharpemu](https://github.com/sharpemu/sharpemu/blob/main/src/SharpEmu.Libs/Agc/AgcExports.PacketPatching.cs) writes both words of a 64-bit wait's mask. A mask above 32 bits throws - [scePsmlMfsr2CreateContext, scePsmlMfsr2CreateSharedResources, scePsmlMfsr2GetContextInitRequirement, scePsmlMfsr2GetDispatchPackets, scePsmlMfsr2GetDispatchPacketsSizeInDwords, scePsmlMfsr2GetSharedResourcesInitRequirement, scePsmlMfsr2Init, scePsmlMfsr2ReleaseContext, scePsmlMfsr2ReleaseSharedResources](../../core/libs/prx/libScePsml_debug/Export.cpp) (libScePsml_mfsr2) - unknown signatures, imported by PPSA23566; declared without parameters