diff --git a/core/libs/prx/libSceCdlgG2P/CMakeLists.txt b/core/libs/prx/libSceCdlgG2P/CMakeLists.txt new file mode 100644 index 000000000..9809109f6 --- /dev/null +++ b/core/libs/prx/libSceCdlgG2P/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(libSceCdlgG2P SHARED EXCLUDE_FROM_ALL Export.cpp) +target_include_directories(libSceCdlgG2P PRIVATE ${LIBS_INCLUDE_DIR}) +configure_windows_unwind(libSceCdlgG2P) + +if(BUILD_TESTING) + add_test_executable(guest_g2p_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestG2PDialog.cpp) + target_include_directories(guest_g2p_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_g2p_dialog_tests PRIVATE libSceCdlgG2P) + add_test(NAME guest_g2p_dialog COMMAND guest_g2p_dialog_tests) + if(WIN32) + set_tests_properties(guest_g2p_dialog PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() diff --git a/core/libs/prx/libSceCdlgG2P/Export.cpp b/core/libs/prx/libSceCdlgG2P/Export.cpp index 7ad726f79..d772b2900 100644 --- a/core/libs/prx/libSceCdlgG2P/Export.cpp +++ b/core/libs/prx/libSceCdlgG2P/Export.cpp @@ -1,40 +1,70 @@ +#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 sceG2PDialogInitialize(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + 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 sceG2PDialogTerminate(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceG2PDialogOpen(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceG2PDialogClose(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceG2PDialogGetStatus(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +int APS5_VABI sceG2PDialogOpen(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 sceG2PDialogUpdateStatus(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return g_status.load(); } -int APS5_VABI sceG2PDialogGetResult(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +int APS5_VABI sceG2PDialogGetStatus(void) { + return g_status.load(); } + +int APS5_VABI sceG2PDialogGetResult(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 sceG2PDialogClose(void) { + if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + return 0; +} + +int APS5_VABI sceG2PDialogTerminate(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/prx/libSceVrSetupDialog/CMakeLists.txt b/core/libs/prx/libSceVrSetupDialog/CMakeLists.txt new file mode 100644 index 000000000..1f0ea1700 --- /dev/null +++ b/core/libs/prx/libSceVrSetupDialog/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(libSceVrSetupDialog SHARED EXCLUDE_FROM_ALL Export.cpp) +target_include_directories(libSceVrSetupDialog PRIVATE ${LIBS_INCLUDE_DIR}) +configure_windows_unwind(libSceVrSetupDialog) + +if(BUILD_TESTING) + add_test_executable(guest_vr_setup_dialog_tests ${LIBS_INCLUDE_DIR}/tests/GuestVrSetupDialog.cpp) + target_include_directories(guest_vr_setup_dialog_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_vr_setup_dialog_tests PRIVATE libSceVrSetupDialog) + add_test(NAME guest_vr_setup_dialog COMMAND guest_vr_setup_dialog_tests) + if(WIN32) + set_tests_properties(guest_vr_setup_dialog PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() diff --git a/core/libs/prx/libSceVrSetupDialog/Export.cpp b/core/libs/prx/libSceVrSetupDialog/Export.cpp index 5c9a5da6a..128cbcd16 100644 --- a/core/libs/prx/libSceVrSetupDialog/Export.cpp +++ b/core/libs/prx/libSceVrSetupDialog/Export.cpp @@ -1,38 +1,66 @@ +#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 sceVrSetupDialogClose(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceVrSetupDialogGetResult(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - int APS5_VABI sceVrSetupDialogInitialize(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + 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 sceVrSetupDialogOpen(void) { - NotImplemented_nid_no_patch(__func__); - return 0; -} - -int APS5_VABI sceVrSetupDialogTerminate(void) { - NotImplemented_nid_no_patch(__func__); - return 0; +int APS5_VABI sceVrSetupDialogOpen(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 sceVrSetupDialogUpdateStatus(void) { - NotImplemented_nid_no_patch(__func__); - return 0; + return g_status.load(); +} + +int APS5_VABI sceVrSetupDialogGetResult(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 sceVrSetupDialogClose(void) { + if (g_status.load() == COMMON_DIALOG_STATUS_NONE) return COMMON_DIALOG_ERROR_NOT_INITIALIZED; + return 0; +} + +int APS5_VABI sceVrSetupDialogTerminate(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/GuestG2PDialog.cpp b/core/libs/tests/GuestG2PDialog.cpp new file mode 100644 index 000000000..d1666fd28 --- /dev/null +++ b/core/libs/tests/GuestG2PDialog.cpp @@ -0,0 +1,54 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include + +extern "C" { +int APS5_VABI sceG2PDialogInitialize(void); +int APS5_VABI sceG2PDialogOpen(const void* param); +int APS5_VABI sceG2PDialogUpdateStatus(void); +int APS5_VABI sceG2PDialogGetStatus(void); +int APS5_VABI sceG2PDialogGetResult(void* result); +int APS5_VABI sceG2PDialogClose(void); +int APS5_VABI sceG2PDialogTerminate(void); +} + +namespace { + +void Require(bool value) { if (!value) std::abort(); } + +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(sceG2PDialogTerminate() == kErrNotInitialized); + Require(sceG2PDialogOpen(¶m) == kErrNotInitialized); + Require(sceG2PDialogGetResult(result) == kErrNotInitialized); + + Require(sceG2PDialogInitialize() == 0); + Require(sceG2PDialogInitialize() == kErrAlreadyInitialized); + + Require(sceG2PDialogOpen(nullptr) == kErrArgNull); + Require(sceG2PDialogGetResult(result) == kErrNotFinished); + + Require(sceG2PDialogOpen(¶m) == 0); + Require(sceG2PDialogGetStatus() == kStatusFinished); + Require(sceG2PDialogUpdateStatus() == kStatusFinished); + + Require(sceG2PDialogGetResult(nullptr) == kErrArgNull); + Require(sceG2PDialogGetResult(result) == 0); + Require(result[0] == kResultUserCanceled); + Require(result[1] == -1); + + Require(sceG2PDialogClose() == 0); + Require(sceG2PDialogTerminate() == 0); + Require(sceG2PDialogTerminate() == kErrNotInitialized); +} diff --git a/core/libs/tests/GuestVrSetupDialog.cpp b/core/libs/tests/GuestVrSetupDialog.cpp new file mode 100644 index 000000000..a3afc479f --- /dev/null +++ b/core/libs/tests/GuestVrSetupDialog.cpp @@ -0,0 +1,52 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include + +extern "C" { +int APS5_VABI sceVrSetupDialogInitialize(void); +int APS5_VABI sceVrSetupDialogOpen(const void* param); +int APS5_VABI sceVrSetupDialogUpdateStatus(void); +int APS5_VABI sceVrSetupDialogGetResult(void* result); +int APS5_VABI sceVrSetupDialogClose(void); +int APS5_VABI sceVrSetupDialogTerminate(void); +} + +namespace { + +void Require(bool value) { if (!value) std::abort(); } + +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(sceVrSetupDialogTerminate() == kErrNotInitialized); + Require(sceVrSetupDialogOpen(¶m) == kErrNotInitialized); + Require(sceVrSetupDialogGetResult(result) == kErrNotInitialized); + + Require(sceVrSetupDialogInitialize() == 0); + Require(sceVrSetupDialogInitialize() == kErrAlreadyInitialized); + + Require(sceVrSetupDialogOpen(nullptr) == kErrArgNull); + Require(sceVrSetupDialogGetResult(result) == kErrNotFinished); + + Require(sceVrSetupDialogOpen(¶m) == 0); + Require(sceVrSetupDialogUpdateStatus() == kStatusFinished); + + Require(sceVrSetupDialogGetResult(nullptr) == kErrArgNull); + Require(sceVrSetupDialogGetResult(result) == 0); + Require(result[0] == kResultUserCanceled); + Require(result[1] == -1); + + Require(sceVrSetupDialogClose() == 0); + Require(sceVrSetupDialogTerminate() == 0); + Require(sceVrSetupDialogTerminate() == kErrNotInitialized); +} diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index dfc24f7e1..99146692a 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -17,6 +17,8 @@ Throughout the project, every function at every stage either **does exactly what - [libSceCommonDialog](../../core/libs/prx/libSceCommonDialog/Export.cpp) - [libSceErrorDialog](../../core/libs/prx/libSceErrorDialog/Export.cpp) - the error dialog runs the state machine but shows nothing; the error code passed to `sceErrorDialogOpen` is only logged - [libSceLoginDialog](../../core/libs/prx/libSceLoginDialog/Export.cpp) - the login dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled +- [libSceCdlgG2P](../../core/libs/prx/libSceCdlgG2P/Export.cpp) - the G2P dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled. The signatures (`Open` and `GetResult` take a pointer, the others none) and the leading `int32` result are assumed from the other common dialogs, not verified for G2P; PPSA01289 imports it +- [libSceVrSetupDialog](../../core/libs/prx/libSceVrSetupDialog/Export.cpp) - the VR setup dialog runs the common-dialog state machine but shows nothing and reports the user as having cancelled, as no headset is supported (see libSceHmd2). The signatures are assumed from the other common dialogs; PPSA12544 imports it - [libSceHmd](../../core/libs/prx/libSceHmd/Export.cpp) implements only the disconnected-headset path: initialization succeeds, device queries report `NotDetected`, and opening a device returns `DeviceDisconnected`. Headset support, tracking, rendering and additional HMD exports are not implemented; SDK-level ABI compatibility and in-game behaviour remain unverified. - [pthread_mutexattr_setprotocol, pthread_mutexattr_setprioceiling, pthread_mutexattr_getprioceiling](../../core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp) (libkernel) - the protocol and the priority ceiling are stored and reported as in FreeBSD's [libthr](https://github.com/freebsd/freebsd-src/blob/main/lib/libthr/thread/thr_mutexattr.c) (the ceiling is set or read only under `PTHREAD_PRIO_PROTECT`, and setting a protocol resets it to the highest `SCHED_RR` priority), but mutexes do not apply them: a priority-inherit or priority-protect mutex locks like a normal one and does not change its owner's priority - [libSceHmd2](../../core/libs/prx/libSceHmd2/Export.cpp) has no PS VR2 runtime: `sceHmd2Initialize` fails, and every other export it has (and every [libSceVrTracker2](../../core/libs/prx/libSceVrTracker2/Export.cpp) export) throws. PPSA01317 skips its whole VR set-up when initialization fails @@ -186,8 +188,6 @@ Throughout the project, every function at every stage either **does exactly what - [sceVoiceQoSInit](../../core/libs/prx/libSceVoiceQoS/Export.cpp) (libSceVoiceQoS) - `0x804E0905` for a second initialization comes from [prosper](https://github.com/mattias800/prosper)'s reading of the module and was not measured: on a PS5 a valid call blocks inside a homebrew payload, so it never returns there. The app type and the block are not used, as no endpoint is implemented - AudioIn and NpSessionSignaling exports added without an implementation have assumed signatures - [sceNpSessionSignalingGetConnectionFromPeerAddress](../../core/libs/prx/libSceNpSessionSignaling/Export.cpp) (libSceNpSessionSignaling) - imported by PPSA01289 and added without an implementation; unknown signature -- [libSceCdlgG2P](../../core/libs/prx/libSceCdlgG2P/Export.cpp) - `sceG2PDialogInitialize`, `Terminate`, `Open`, `GetStatus`, `UpdateStatus` and `GetResult`, imported by PPSA01289, are added without an implementation and declared without parameters -- [sceG2PDialogClose](../../core/libs/prx/libSceCdlgG2P/Export.cpp) (libSceCdlgG2P) - throwing stub; the name comes from the aerolib NID list, and the signature `int(void)` is assumed from `sceLoginDialogClose`, not verified for G2P - [vieBRwlh1Lw](../../core/libs/prx/libSceAgc/Unimplemented.cpp) (libSceAgc) - unknown name, signature - [scePadIsRemoteController](../../core/libs/prx/libScePad/Export.cpp) (libScePad) - the name hashes to NID `fCWdlnmB1Ks`; the signature `(int handle, bool* remote)` comes from PPSA16064, which zeroes a byte, passes its address after `scePadGetControllerInformation` and ignores the result. The error codes follow the other pad calls. Controllers are local devices, so the answer is always false - [scePadSetAngularVelocityBiasCorrectionState](../../core/libs/prx/libScePad/Export.cpp) (libScePad) - inferred signature `(int handle, bool enabled)` follows `scePadSetTiltCorrectionState` and `scePadSetAngularVelocityDeadbandState`; an invalid handle returns `PAD_ERROR_INVALID_HANDLE`, disabling is a no-op, and enabling throws because bias correction is not modelled @@ -197,7 +197,7 @@ Throughout the project, every function at every stage either **does exactly what - [sceSslGetCaList, sceSslFreeCaList, sceSslUnloadCert](../../core/libs/prx/libSceSsl/Export.cpp) (libSceSsl) - signatures and the 16-byte `SceSslCaList` layout (an array of certificate handles and a 32-bit count) follow [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/5dfdb4b0b166e83dc9ae275001afb695f12c7174/src/SharpProspero/Interop/Net/Http.cs) and shadPS4's `OrbisSslCaList`, not confirmed for the PS5. On the console the list holds the certificate authorities the SSL service has; [shadPS4](https://github.com/shadps4-emu/shadPS4/pull/4104) returns a placeholder certificate from `sceSslGetCaCerts` because Rise of the Tomb Raider fails its initialization checks without one. No root certificate bundle is shipped and no loaded certificate is kept, so here a context holds none: `sceSslGetCaList` clears the list and returns `NOT_FOUND`, as `sceSslGetCaCerts` does, `sceSslFreeCaList` accepts only that cleared list, and `sceSslUnloadCert` (imported by PPSA15508) has nothing to drop and is assumed to return 0. A null list returns `0x8095177A`, which `sceSslGetCaCerts` and [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/59183dbfa81ed7efdff6069177fed50de0552725/src/libs/network.cpp) return for a null argument (shadPS4, PS4, returns `0x8095F007`). An unknown context throws (KytyPS5 returns `SSL_ERROR_INVALID_ID`, `0x8095F006`, from `sceSslGetCaCerts`), and so does a list `sceSslGetCaList` did not return - [sceHttp2SetResolveRetry](../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - signature `(int id, int32_t retry)` from libSceHttp's `sceHttpSetResolveRetry`; the value is accepted and ignored like the other Http2 settings, since no request reaches the network - [sceHttp2WebSocketCreateRequest, sceHttp2WebSocketCloseAsync, sceHttp2WebSocketSendTextMessageAsync, sceHttp2WebSocketSendDataMessageAsync](../../core/libs/prx/libSceHttp2/Export.cpp) (libSceHttp2) - imported by PPSA16064 and not called during its start-up; unknown signatures -- [libSceVrSetupDialog](../../core/libs/prx/libSceVrSetupDialog/Export.cpp), and the Share, NpTrophy2, NpEntitlementAccess and WebBrowserDialog exports imported by PPSA12544's Unity plugins and added without an implementation, have unknown signatures +- The Share, NpTrophy2, NpEntitlementAccess and WebBrowserDialog exports imported by PPSA12544's Unity plugins and added without an implementation have unknown signatures - [sceNetShowNetstat](../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) - imported by PPSA11386 and not called during its start-up; unknown signature, declared without parameters - [sceAjmDecWVorbisCreateHeaderPacket, sceAjmDecWVorbisCreateSetupPacket](../../core/libs/prx/libSceAjm.native/Export.cpp) (libSceAjmWVorbis, implemented in libSceAjm.native) - unknown signatures - Acm, GameLiveStreaming, Net, NpEntitlementAccess, NpManager, NpWebApi2, VoiceQoS, WebBrowserDialog and libkernel exports imported by PPSA14632 and added without an implementation are declared without parameters