From 95ea06e80b543946f5566dd37dab89db503ab1a5 Mon Sep 17 00:00:00 2001 From: erickerxhalli <313650569+erickerxhalli@users.noreply.github.com> Date: Mon, 19 Oct 2026 22:31:58 +0000 Subject: [PATCH] fix(libSceMove): throw when opening for another user or an open slot sceMoveOpen returned a new handle for any user, type and index, although the console's result for a user other than the initial user and for a type and index that are already open is not known. Both now throw, as libSceHmd2 does for another user. Record which shadPS4 behaviour the handle and open checks add to. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Arbzg9c2DA4DEFKVSvRgYv --- core/libs/prx/libSceMove/Export.cpp | 20 +++++++++++++------- core/libs/tests/GuestMove.cpp | 7 ++++++- docs/dev/debt/libSceMove.md | 5 +++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/core/libs/prx/libSceMove/Export.cpp b/core/libs/prx/libSceMove/Export.cpp index 340a60d887..df4c7fcf1a 100644 --- a/core/libs/prx/libSceMove/Export.cpp +++ b/core/libs/prx/libSceMove/Export.cpp @@ -1,10 +1,10 @@ -#include #include #include #include -#include +#include #include "SceTypes.hpp" #include "prx/libc/include/General.hpp" +#include "prx/libSceUserService/UserService.hpp" namespace { @@ -19,7 +19,12 @@ constexpr int32_t MOVE_HANDLE_STEP = 0x100; std::mutex stateMutex; bool initialized = false; int32_t nextHandle = MOVE_FIRST_HANDLE; -std::unordered_set openHandles; +struct Slot { + int32_t type; + int32_t index; +}; + +std::unordered_map openHandles; int checkHandle(const char* function, int32_t handle) { std::lock_guard lock(stateMutex); @@ -48,14 +53,15 @@ int APS5_VABI sceMoveTerm(void) { } int APS5_VABI sceMoveOpen(int32_t userId, int32_t type, int32_t index) { - (void)userId; - (void)type; - (void)index; std::lock_guard lock(stateMutex); if (!initialized) return MOVE_ERROR_NOT_INIT; + if (userId != USER_SERVICE_INITIAL_USER_ID) NotImplemented_nid_no_patch(__func__); + for (const auto& [openHandle, slot] : openHandles) { + if (slot.type == type && slot.index == index) NotImplemented_nid_no_patch(__func__); + } const int32_t handle = nextHandle; nextHandle += MOVE_HANDLE_STEP; - openHandles.insert(handle); + openHandles.emplace(handle, Slot{type, index}); return handle; } diff --git a/core/libs/tests/GuestMove.cpp b/core/libs/tests/GuestMove.cpp index 6d35288a5a..db2b1bc998 100644 --- a/core/libs/tests/GuestMove.cpp +++ b/core/libs/tests/GuestMove.cpp @@ -101,6 +101,8 @@ int main() { const std::int32_t second = sceMoveOpen(kUserId, 0, 1); Require(first == kFirstHandle); Require(second == kFirstHandle + kHandleStep); + Require(Throws([] { sceMoveOpen(kUserId, 0, 0); })); + Require(Throws([] { sceMoveOpen(kUserId + 1, 0, 2); })); Require(sceMoveGetDeviceInfo(first, nullptr) == kInvalidArg); Require(sceMoveGetExtensionPortInfo(first, nullptr) == kInvalidArg); @@ -125,6 +127,9 @@ int main() { Require(sceMoveClose(first) == kOk); RequireUnknownHandleThrows(first); + const std::int32_t reopened = sceMoveOpen(kUserId, 0, 0); + Require(reopened == kFirstHandle + 2 * kHandleStep); + Require(sceMoveClose(reopened) == kOk); Require(sceMoveSetVibration(second, 0) == kNoControllerConnected); Require(sceMoveTerm() == kOk); @@ -133,7 +138,7 @@ int main() { Require(sceMoveInit() == kOk); RequireUnknownHandleThrows(second); const std::int32_t third = sceMoveOpen(kUserId, 0, 0); - Require(third == kFirstHandle + 2 * kHandleStep); + Require(third == kFirstHandle + 3 * kHandleStep); Require(sceMoveClose(third) == kOk); Require(sceMoveTerm() == kOk); } diff --git a/docs/dev/debt/libSceMove.md b/docs/dev/debt/libSceMove.md index 8ef70c92b6..ec7666f106 100644 --- a/docs/dev/debt/libSceMove.md +++ b/docs/dev/debt/libSceMove.md @@ -2,8 +2,9 @@ ### Silent stubs -- [libSceMove](../../../core/libs/prx/libSceMove/Export.cpp) implements only the no-controller path: initialization and termination succeed, `sceMoveOpen` returns a new handle for any user, type and index, `sceMoveClose` and `sceMoveResetLightSphere` succeed for an open handle, and the device info, state, extension port, vibration and `sceMoveSetLightSphere` calls return 1 (no controller connected) and write nothing +- [libSceMove](../../../core/libs/prx/libSceMove/Export.cpp) implements only the no-controller path: initialization and termination succeed, `sceMoveOpen` returns a new handle, `sceMoveClose` and `sceMoveResetLightSphere` succeed for an open handle, and the device info, state, extension port, vibration and `sceMoveSetLightSphere` calls return 1 (no controller connected) and write nothing ### Unknown function info -- [libSceMove](../../../core/libs/prx/libSceMove/Export.cpp) (libSceMove) - behaviour, error codes and the handle values (`0x30B0100`, then every `0x100`) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/move/move.cpp) (PS4), not measured on a PS5. Signatures follow [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json), except `sceMoveGetExtensionPortInfo`, which follows shadPS4. Before `sceMoveInit` and after `sceMoveTerm` every other call returns `NOT_INIT` (`0x80EE0001`); a null output or a negative timestamp returns `INVALID_ARG` (`0x80EE0003`). A handle that was never opened, is closed or was opened before `sceMoveTerm` throws: the console's `INVALID_HANDLE` (`0x80EE0004`) result and its order against `INVALID_ARG` are not known. Whether `sceMoveOpen` rejects an invalid user or a slot that is already open is not known. Closes when: measured on a PS5 +- [libSceMove](../../../core/libs/prx/libSceMove/Export.cpp) (libSceMove) - behaviour, error codes and the handle values (`0x30B0100`, then every `0x100`) follow [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/f68923fe807f657348e6719ce658bcbd0a2b1c72/src/core/libraries/move/move.cpp) (PS4), which checks no handle or open arguments; none is measured on a PS5. Signatures follow [Orbital](https://github.com/AlexAltea/orbital/blob/master/tools/ida/db_types.json), except `sceMoveGetExtensionPortInfo`, which follows shadPS4. Before `sceMoveInit` and after `sceMoveTerm` every other call returns `NOT_INIT` (`0x80EE0001`); a null output or a negative timestamp returns `INVALID_ARG` (`0x80EE0003`). Closes when: measured on a PS5 +- [libSceMove](../../../core/libs/prx/libSceMove/Export.cpp) (libSceMove) - a handle that was never opened, is closed or was opened before `sceMoveTerm` throws: the console's `INVALID_HANDLE` (`0x80EE0004`) result and its order against `INVALID_ARG` are not known. `sceMoveOpen` throws for a user other than the initial user and for a type and index that are already open, whose console results are not known, and accepts any type and index. Closes when: measured on a PS5