From 75c2ea854845db9bb670b3f791f5b8e86a83dbf7 Mon Sep 17 00:00:00 2001 From: Phi1ow <331962174+Phi1ow@users.noreply.github.com> Date: Sun, 4 Oct 2026 21:56:29 +0200 Subject: [PATCH] feat(sysmodule): implement sceSysmoduleLoadModuleInternal and sceSysmoduleUnloadModuleInternal Two exports were missing. System modules are linked statically, so load and unload are reference counts over the known module table, exactly like the existing sceSysmoduleLoadModuleInternalWithArg and sceSysmoduleUnloadModule: - sceSysmoduleLoadModuleInternal(uint32_t) -> increments the module's load count, returns 0 - sceSysmoduleUnloadModuleInternal(uint32_t) -> decrements it, or 0x80A90003 when the module was not loaded Unknown or zero ids throw, like the sibling internal loader. Adds a CMakeLists.txt so the library can carry its guest test. Signatures follow the public OpenOrbis SDK headers. New test core/libs/tests/GuestSysmodule.cpp loads a module twice, unloads it twice and checks the not-loaded error on a third unload; it fails without the change (the symbols are undefined) and passes with it. --- core/libs/prx/libSceSysmodule/CMakeLists.txt | 20 ++++++++++++++ core/libs/prx/libSceSysmodule/Export.cpp | 28 ++++++++++++++++++++ core/libs/tests/GuestSysmodule.cpp | 25 +++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 core/libs/prx/libSceSysmodule/CMakeLists.txt create mode 100644 core/libs/tests/GuestSysmodule.cpp diff --git a/core/libs/prx/libSceSysmodule/CMakeLists.txt b/core/libs/prx/libSceSysmodule/CMakeLists.txt new file mode 100644 index 000000000..afbcf4cd6 --- /dev/null +++ b/core/libs/prx/libSceSysmodule/CMakeLists.txt @@ -0,0 +1,20 @@ +add_library(libSceSysmodule SHARED EXCLUDE_FROM_ALL Export.cpp) +target_include_directories(libSceSysmodule PRIVATE ${LIBS_INCLUDE_DIR}) +configure_windows_unwind(libSceSysmodule) + +if(MINGW) + foreach(runtimeLinkFlag IN ITEMS -static-libgcc -static-libstdc++ -static) + string(REPLACE "${runtimeLinkFlag}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") + endforeach() +endif() + +if(BUILD_TESTING) + add_test_executable(guest_sysmodule_tests ${LIBS_INCLUDE_DIR}/tests/GuestSysmodule.cpp) + target_include_directories(guest_sysmodule_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_sysmodule_tests PRIVATE libSceSysmodule) + add_test(NAME guest_sysmodule COMMAND guest_sysmodule_tests) + if(WIN32) + set_tests_properties(guest_sysmodule PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() diff --git a/core/libs/prx/libSceSysmodule/Export.cpp b/core/libs/prx/libSceSysmodule/Export.cpp index 76f993459..85b64a2ed 100644 --- a/core/libs/prx/libSceSysmodule/Export.cpp +++ b/core/libs/prx/libSceSysmodule/Export.cpp @@ -166,4 +166,32 @@ int APS5_VABI sceSysmoduleUnloadModule(std::uint16_t id) { return 0; } +int APS5_VABI sceSysmoduleLoadModuleInternal(std::uint32_t id) { + if ((id & 0x7fffffffu) == 0) { + throw std::runtime_error("sceSysmoduleLoadModuleInternal: invalid id 0"); + } + if (!findModuleName(id)) { + throw std::runtime_error(std::string("sceSysmoduleLoadModuleInternal: unknown id ") + std::to_string(id)); + } + std::lock_guard lock(gMutex); + gLoadCount[id]++; + return 0; +} + +int APS5_VABI sceSysmoduleUnloadModuleInternal(std::uint32_t id) { + if ((id & 0x7fffffffu) == 0) { + throw std::runtime_error("sceSysmoduleUnloadModuleInternal: invalid id 0"); + } + if (!findModuleName(id)) { + throw std::runtime_error(std::string("sceSysmoduleUnloadModuleInternal: unknown id ") + std::to_string(id)); + } + std::lock_guard lock(gMutex); + auto it = gLoadCount.find(id); + if (it == gLoadCount.end() || it->second < 1) { + return 0x80A90003; + } + it->second--; + return 0; +} + } diff --git a/core/libs/tests/GuestSysmodule.cpp b/core/libs/tests/GuestSysmodule.cpp new file mode 100644 index 000000000..bf6dbaa00 --- /dev/null +++ b/core/libs/tests/GuestSysmodule.cpp @@ -0,0 +1,25 @@ +#include "prx/libc/include/general/VabiMacros.hpp" +#include +#include + +extern "C" { +int APS5_VABI sceSysmoduleLoadModuleInternal(std::uint32_t id); +int APS5_VABI sceSysmoduleUnloadModuleInternal(std::uint32_t id); +} + +namespace { + +void Require(bool value) { if (!value) std::abort(); } + +constexpr std::uint32_t kFiberModuleId = 0x00000006; +constexpr int kModuleNotLoaded = static_cast(0x80A90003); + +} + +int main() { + Require(sceSysmoduleLoadModuleInternal(kFiberModuleId) == 0); + Require(sceSysmoduleLoadModuleInternal(kFiberModuleId) == 0); + Require(sceSysmoduleUnloadModuleInternal(kFiberModuleId) == 0); + Require(sceSysmoduleUnloadModuleInternal(kFiberModuleId) == 0); + Require(sceSysmoduleUnloadModuleInternal(kFiberModuleId) == kModuleNotLoaded); +}