diff --git a/core/libs/prx/libSceGameCustomDataDialog/CMakeLists.txt b/core/libs/prx/libSceGameCustomDataDialog/CMakeLists.txt new file mode 100644 index 0000000000..6f19d7891e --- /dev/null +++ b/core/libs/prx/libSceGameCustomDataDialog/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(libSceGameCustomDataDialog SHARED EXCLUDE_FROM_ALL Export.cpp) +target_include_directories(libSceGameCustomDataDialog PRIVATE ${LIBS_INCLUDE_DIR}) +configure_windows_unwind(libSceGameCustomDataDialog) + +if(BUILD_TESTING) + add_test_executable(guest_game_custom_data_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestGameCustomDataDialog.cpp) + target_include_directories(guest_game_custom_data_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_game_custom_data_dialog_tests PRIVATE libSceGameCustomDataDialog) + add_test(NAME guest_game_custom_data_dialog COMMAND guest_game_custom_data_dialog_tests) + if(WIN32) + set_tests_properties(guest_game_custom_data_dialog PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() diff --git a/core/libs/prx/libSceGameCustomDataDialog/Export.cpp b/core/libs/prx/libSceGameCustomDataDialog/Export.cpp index 5b647ad000..594d4adfac 100644 --- a/core/libs/prx/libSceGameCustomDataDialog/Export.cpp +++ b/core/libs/prx/libSceGameCustomDataDialog/Export.cpp @@ -1,14 +1,74 @@ +#include +#include +#include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +namespace { + +constexpr int COMMON_DIALOG_STATUS_NONE = 0; +constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1; +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; + +std::atomic g_status{COMMON_DIALOG_STATUS_NONE}; + +} + extern "C" { -int APS5_VABI sceGameCustomDataDialogGetResult() { - NotImplemented_nid_no_patch(__func__); +int APS5_VABI sceGameCustomDataDialogInitialize(void) { + int expected = COMMON_DIALOG_STATUS_NONE; + if (!g_status.compare_exchange_strong(expected, COMMON_DIALOG_STATUS_INITIALIZED)) return COMMON_DIALOG_ERROR_ALREADY_INITIALIZED; return 0; } -int APS5_VABI sceGameCustomDataDialogInitialize() { +int APS5_VABI sceGameCustomDataDialogOpenA(const void* param) { + const int status = g_status.load(); + if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; + if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + g_status = COMMON_DIALOG_STATUS_FINISHED; + return 0; +} + +int APS5_VABI sceGameCustomDataDialogUpdateStatus(void) { + return g_status.load(); +} + +int APS5_VABI sceGameCustomDataDialogGetStatus(void) { + return g_status.load(); +} + +int APS5_VABI sceGameCustomDataDialogGetResultA(void* result) { + const int status = g_status.load(); + if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; + const std::int32_t canceled = COMMON_DIALOG_RESULT_USER_CANCELED; + std::memcpy(result, &canceled, sizeof(canceled)); + return 0; +} + +int APS5_VABI sceGameCustomDataDialogClose(void) { + 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 sceGameCustomDataDialogTerminate(void) { + if (g_status.exchange(COMMON_DIALOG_STATUS_NONE) == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + return 0; +} + +int APS5_VABI sceGameCustomDataDialogGetResult() { NotImplemented_nid_no_patch(__func__); return 0; } @@ -18,14 +78,4 @@ int APS5_VABI sceGameCustomDataDialogOpen() { return 0; } -int APS5_VABI sceGameCustomDataDialogTerminate() { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceGameCustomDataDialogUpdateStatus() { - NotImplemented_nid_no_patch(__func__); - return 0; -} - } diff --git a/core/libs/prx/libSceNpSnsFacebookDialog/CMakeLists.txt b/core/libs/prx/libSceNpSnsFacebookDialog/CMakeLists.txt new file mode 100644 index 0000000000..5445d0dddd --- /dev/null +++ b/core/libs/prx/libSceNpSnsFacebookDialog/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(libSceNpSnsFacebookDialog SHARED EXCLUDE_FROM_ALL Export.cpp) +target_include_directories(libSceNpSnsFacebookDialog PRIVATE ${LIBS_INCLUDE_DIR}) +configure_windows_unwind(libSceNpSnsFacebookDialog) + +if(BUILD_TESTING) + add_test_executable(guest_np_sns_facebook_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestNpSnsFacebookDialog.cpp) + target_include_directories(guest_np_sns_facebook_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_np_sns_facebook_dialog_tests PRIVATE libSceNpSnsFacebookDialog) + add_test(NAME guest_np_sns_facebook_dialog COMMAND guest_np_sns_facebook_dialog_tests) + if(WIN32) + set_tests_properties(guest_np_sns_facebook_dialog PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() diff --git a/core/libs/prx/libSceNpSnsFacebookDialog/Export.cpp b/core/libs/prx/libSceNpSnsFacebookDialog/Export.cpp index 9a4fe0e8cd..e61b490597 100644 --- a/core/libs/prx/libSceNpSnsFacebookDialog/Export.cpp +++ b/core/libs/prx/libSceNpSnsFacebookDialog/Export.cpp @@ -1,42 +1,70 @@ -#include +#include #include +#include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +namespace { + +constexpr int COMMON_DIALOG_STATUS_NONE = 0; +constexpr int COMMON_DIALOG_STATUS_INITIALIZED = 1; +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; + +std::atomic g_status{COMMON_DIALOG_STATUS_NONE}; + +} + extern "C" { -int APS5_VABI sceNpSnsFacebookDialogClose(void) { - NotImplemented_nid_no_patch(__func__); +int APS5_VABI sceNpSnsFacebookDialogInitialize(void) { + int expected = COMMON_DIALOG_STATUS_NONE; + if (!g_status.compare_exchange_strong(expected, COMMON_DIALOG_STATUS_INITIALIZED)) return COMMON_DIALOG_ERROR_ALREADY_INITIALIZED; return 0; } +int APS5_VABI sceNpSnsFacebookDialogOpen(const void* param) { + const int status = g_status.load(); + if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (status == COMMON_DIALOG_STATUS_RUNNING) return COMMON_DIALOG_ERROR_BUSY; + if (param == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + g_status = COMMON_DIALOG_STATUS_FINISHED; + return 0; +} + +int APS5_VABI sceNpSnsFacebookDialogUpdateStatus(void) { + return g_status.load(); +} + int APS5_VABI sceNpSnsFacebookDialogGetStatus(void) { - NotImplemented_nid_no_patch(__func__); + return g_status.load(); +} + +int APS5_VABI sceNpSnsFacebookDialogGetResult(void* result) { + const int status = g_status.load(); + if (status == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + if (result == nullptr) return COMMON_DIALOG_ERROR_ARG_NULL; + if (status != COMMON_DIALOG_STATUS_FINISHED) return COMMON_DIALOG_ERROR_NOT_FINISHED; + const std::int32_t canceled = COMMON_DIALOG_RESULT_USER_CANCELED; + std::memcpy(result, &canceled, sizeof(canceled)); return 0; } -int APS5_VABI sceNpSnsFacebookDialogGetResult() { - NotImplemented_nid_no_patch(__func__); +int APS5_VABI sceNpSnsFacebookDialogClose(void) { + 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 sceNpSnsFacebookDialogInitialize() { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceNpSnsFacebookDialogOpen() { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceNpSnsFacebookDialogTerminate() { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceNpSnsFacebookDialogUpdateStatus() { - NotImplemented_nid_no_patch(__func__); +int APS5_VABI sceNpSnsFacebookDialogTerminate(void) { + if (g_status.exchange(COMMON_DIALOG_STATUS_NONE) == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; return 0; } diff --git a/core/libs/tests/GuestGameCustomDataDialog.cpp b/core/libs/tests/GuestGameCustomDataDialog.cpp new file mode 100644 index 0000000000..f410bc031b --- /dev/null +++ b/core/libs/tests/GuestGameCustomDataDialog.cpp @@ -0,0 +1,72 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include + +extern "C" { +int APS5_VABI sceGameCustomDataDialogInitialize(void); +int APS5_VABI sceGameCustomDataDialogOpenA(const void* param); +int APS5_VABI sceGameCustomDataDialogUpdateStatus(void); +int APS5_VABI sceGameCustomDataDialogGetStatus(void); +int APS5_VABI sceGameCustomDataDialogGetResultA(void* result); +int APS5_VABI sceGameCustomDataDialogClose(void); +int APS5_VABI sceGameCustomDataDialogTerminate(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[2] = {-1, -1}; + + Require(sceGameCustomDataDialogTerminate() == kErrNotInitialized); + Require(sceGameCustomDataDialogOpenA(¶m) == kErrNotInitialized); + Require(sceGameCustomDataDialogClose() == kErrNotInitialized); + Require(sceGameCustomDataDialogGetResultA(result) == kErrNotInitialized); + Require(sceGameCustomDataDialogGetStatus() == kStatusNone); + Require(sceGameCustomDataDialogUpdateStatus() == kStatusNone); + + Require(sceGameCustomDataDialogInitialize() == 0); + Require(sceGameCustomDataDialogInitialize() == kErrAlreadyInitialized); + Require(sceGameCustomDataDialogGetStatus() == kStatusInitialized); + Require(sceGameCustomDataDialogUpdateStatus() == kStatusInitialized); + + Require(sceGameCustomDataDialogOpenA(nullptr) == kErrArgNull); + Require(sceGameCustomDataDialogGetResultA(result) == kErrNotFinished); + + Require(sceGameCustomDataDialogOpenA(¶m) == 0); + Require(sceGameCustomDataDialogGetStatus() == kStatusFinished); + Require(sceGameCustomDataDialogUpdateStatus() == kStatusFinished); + + Require(sceGameCustomDataDialogGetResultA(nullptr) == kErrArgNull); + Require(sceGameCustomDataDialogGetResultA(result) == 0); + Require(result[0] == kResultUserCanceled); + Require(result[1] == -1); + + Require(sceGameCustomDataDialogClose() == 0); + Require(sceGameCustomDataDialogTerminate() == 0); + Require(sceGameCustomDataDialogTerminate() == kErrNotInitialized); + Require(sceGameCustomDataDialogGetStatus() == kStatusNone); + Require(sceGameCustomDataDialogUpdateStatus() == kStatusNone); + + Require(sceGameCustomDataDialogInitialize() == 0); + Require(sceGameCustomDataDialogUpdateStatus() == kStatusInitialized); + Require(sceGameCustomDataDialogClose() == 0); + Require(sceGameCustomDataDialogUpdateStatus() == kStatusFinished); + result[0] = -1; + Require(sceGameCustomDataDialogGetResultA(result) == 0); + Require(result[0] == kResultUserCanceled); + Require(sceGameCustomDataDialogTerminate() == 0); +} diff --git a/core/libs/tests/GuestNpSnsFacebookDialog.cpp b/core/libs/tests/GuestNpSnsFacebookDialog.cpp new file mode 100644 index 0000000000..2b78b0007c --- /dev/null +++ b/core/libs/tests/GuestNpSnsFacebookDialog.cpp @@ -0,0 +1,72 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include + +extern "C" { +int APS5_VABI sceNpSnsFacebookDialogInitialize(void); +int APS5_VABI sceNpSnsFacebookDialogOpen(const void* param); +int APS5_VABI sceNpSnsFacebookDialogUpdateStatus(void); +int APS5_VABI sceNpSnsFacebookDialogGetStatus(void); +int APS5_VABI sceNpSnsFacebookDialogGetResult(void* result); +int APS5_VABI sceNpSnsFacebookDialogClose(void); +int APS5_VABI sceNpSnsFacebookDialogTerminate(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[2] = {-1, -1}; + + Require(sceNpSnsFacebookDialogTerminate() == kErrNotInitialized); + Require(sceNpSnsFacebookDialogOpen(¶m) == kErrNotInitialized); + Require(sceNpSnsFacebookDialogClose() == kErrNotInitialized); + Require(sceNpSnsFacebookDialogGetResult(result) == kErrNotInitialized); + Require(sceNpSnsFacebookDialogGetStatus() == kStatusNone); + Require(sceNpSnsFacebookDialogUpdateStatus() == kStatusNone); + + Require(sceNpSnsFacebookDialogInitialize() == 0); + Require(sceNpSnsFacebookDialogInitialize() == kErrAlreadyInitialized); + Require(sceNpSnsFacebookDialogGetStatus() == kStatusInitialized); + Require(sceNpSnsFacebookDialogUpdateStatus() == kStatusInitialized); + + Require(sceNpSnsFacebookDialogOpen(nullptr) == kErrArgNull); + Require(sceNpSnsFacebookDialogGetResult(result) == kErrNotFinished); + + Require(sceNpSnsFacebookDialogOpen(¶m) == 0); + Require(sceNpSnsFacebookDialogGetStatus() == kStatusFinished); + Require(sceNpSnsFacebookDialogUpdateStatus() == kStatusFinished); + + Require(sceNpSnsFacebookDialogGetResult(nullptr) == kErrArgNull); + Require(sceNpSnsFacebookDialogGetResult(result) == 0); + Require(result[0] == kResultUserCanceled); + Require(result[1] == -1); + + Require(sceNpSnsFacebookDialogClose() == 0); + Require(sceNpSnsFacebookDialogTerminate() == 0); + Require(sceNpSnsFacebookDialogTerminate() == kErrNotInitialized); + Require(sceNpSnsFacebookDialogGetStatus() == kStatusNone); + Require(sceNpSnsFacebookDialogUpdateStatus() == kStatusNone); + + Require(sceNpSnsFacebookDialogInitialize() == 0); + Require(sceNpSnsFacebookDialogUpdateStatus() == kStatusInitialized); + Require(sceNpSnsFacebookDialogClose() == 0); + Require(sceNpSnsFacebookDialogUpdateStatus() == kStatusFinished); + result[0] = -1; + Require(sceNpSnsFacebookDialogGetResult(result) == 0); + Require(result[0] == kResultUserCanceled); + Require(sceNpSnsFacebookDialogTerminate() == 0); +} diff --git a/docs/dev/debt/libSceGameCustomDataDialog.md b/docs/dev/debt/libSceGameCustomDataDialog.md new file mode 100644 index 0000000000..09743a0781 --- /dev/null +++ b/docs/dev/debt/libSceGameCustomDataDialog.md @@ -0,0 +1,9 @@ +# libSceGameCustomDataDialog technical debt + +### Silent stubs + +- [libSceGameCustomDataDialog](../../../core/libs/prx/libSceGameCustomDataDialog/Export.cpp) - the game custom data dialog (`sceGameCustomDataDialogOpenA` and `sceGameCustomDataDialogGetResultA`) runs the common-dialog state machine but shows nothing and reports the user as having cancelled, as there is no PSN game custom data. The signatures are from [Orbital's type database](https://github.com/AlexAltea/orbital/blob/b321fcdbd700cc02da1cb1ad25412781d5ec73c6/tools/ida/db_types.json); the result layout beyond the leading `int32_t` result code is not known, so nothing else is written. + +### Unknown function info + +- [sceGameCustomDataDialogGetResult and sceGameCustomDataDialogOpen](../../../core/libs/prx/libSceGameCustomDataDialog/Export.cpp) (libSceGameCustomDataDialog) - names and NIDs are present in the [PS5 3.20 export inventory](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceGameCustomDataDialog.c), but Orbital has prototypes only for the `A` variants and no credible prototypes were found for these. The declarations have no parameters solely to resolve known imports; this is not an ABI claim and callers must not call them until signatures are verified diff --git a/docs/dev/debt/libSceNpSnsFacebookDialog.md b/docs/dev/debt/libSceNpSnsFacebookDialog.md index 3009c1d373..5ec4954b21 100644 --- a/docs/dev/debt/libSceNpSnsFacebookDialog.md +++ b/docs/dev/debt/libSceNpSnsFacebookDialog.md @@ -1,5 +1,5 @@ # libSceNpSnsFacebookDialog technical debt -### Unknown function info +### Silent stubs -- The following pending declarations have unverified signatures and behavior; they are parameterless import-resolution declarations and throw through `NotImplemented_nid_no_patch`: [`sceNpSnsFacebookDialogClose`](../../../core/libs/prx/libSceNpSnsFacebookDialog/Export.cpp), [`sceNpSnsFacebookDialogGetStatus`](../../../core/libs/prx/libSceNpSnsFacebookDialog/Export.cpp). +- [libSceNpSnsFacebookDialog](../../../core/libs/prx/libSceNpSnsFacebookDialog/Export.cpp) - the Facebook dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled, as there is no Facebook account link. The signatures are from [Orbital's type database](https://github.com/AlexAltea/orbital/blob/b321fcdbd700cc02da1cb1ad25412781d5ec73c6/tools/ida/db_types.json); the result layout beyond the leading `int32_t` result code is not known, so nothing else is written.