From 0b0526c4d1449b21fdb57e9d5b9459a5d68556db Mon Sep 17 00:00:00 2001 From: tototomate123 Date: Thu, 8 Oct 2026 08:22:22 +0200 Subject: [PATCH] fix(prx): add missing offline HFW imports --- core/libs/CMakeLists.txt | 9 +++ core/libs/SceTypes.hpp | 4 + core/libs/prx/libSceHttp/Export.cpp | 7 ++ .../prx/libSceNpEntitlementAccess/Export.cpp | 10 +++ core/libs/prx/libSceRudp/Export.cpp | 7 ++ core/libs/tests/GuestOfflineImports.cpp | 79 +++++++++++++++++++ docs/dev/TechnicalDebt.md | 4 + 7 files changed, 120 insertions(+) create mode 100644 core/libs/tests/GuestOfflineImports.cpp diff --git a/core/libs/CMakeLists.txt b/core/libs/CMakeLists.txt index f6c6df5e0..04ef68e3e 100644 --- a/core/libs/CMakeLists.txt +++ b/core/libs/CMakeLists.txt @@ -131,6 +131,15 @@ endforeach() add_custom_target(libs DEPENDS ${PATCHED_OUTPUTS}) if(BUILD_TESTING) + add_test_executable(guest_offline_imports_tests tests/GuestOfflineImports.cpp) + target_include_directories(guest_offline_imports_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(guest_offline_imports_tests PRIVATE libSceHttp libSceNpEntitlementAccess libSceRudp libc) + configure_windows_unwind(guest_offline_imports_tests) + add_test(NAME guest_offline_imports COMMAND guest_offline_imports_tests) + if(WIN32) + set_tests_properties(guest_offline_imports PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() add_test_executable(savedata_memory_metadata_tests tests/SaveDataMemoryMetadata.cpp) target_include_directories(savedata_memory_metadata_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(savedata_memory_metadata_tests PRIVATE libSceSaveData.native) diff --git a/core/libs/SceTypes.hpp b/core/libs/SceTypes.hpp index bf657c237..fa33935ad 100644 --- a/core/libs/SceTypes.hpp +++ b/core/libs/SceTypes.hpp @@ -1274,6 +1274,10 @@ struct NpEntitlementAccessAddcontEntitlementInfo { std::uint32_t download_status; }; +struct NpEntitlementAccessEntitlementKey { + std::uint8_t data[16]; +}; + struct NpUniversalDataSystemInitParam { diff --git a/core/libs/prx/libSceHttp/Export.cpp b/core/libs/prx/libSceHttp/Export.cpp index 16eca0b4a..8daad9e20 100644 --- a/core/libs/prx/libSceHttp/Export.cpp +++ b/core/libs/prx/libSceHttp/Export.cpp @@ -141,6 +141,13 @@ int APS5_VABI sceHttpSetAuthEnabled(int id, int enable) { return 0; } +int APS5_VABI sceHttpSetCookieEnabled(int id, int enable) { + (void)id; + if (static_cast(enable) > 1) return ERROR_INVALID_VALUE; + if (enable != 0) NotImplemented_nid_no_patch(__func__); + return 0; +} + int APS5_VABI sceHttpSetAutoRedirect(int id, int enable) { (void)id; (void)enable; diff --git a/core/libs/prx/libSceNpEntitlementAccess/Export.cpp b/core/libs/prx/libSceNpEntitlementAccess/Export.cpp index ccf92707c..69b128037 100644 --- a/core/libs/prx/libSceNpEntitlementAccess/Export.cpp +++ b/core/libs/prx/libSceNpEntitlementAccess/Export.cpp @@ -91,6 +91,16 @@ int APS5_VABI sceNpEntitlementAccessGetSkuFlag(uint32_t* sku_flag) { return 0; } +int APS5_VABI sceNpEntitlementAccessGetEntitlementKey( + uint32_t serviceLabel, const NpUnifiedEntitlementLabel* entitlementLabel, + NpEntitlementAccessEntitlementKey* key) { + (void)serviceLabel; + constexpr int errorParameter = static_cast(0x817D0002); + constexpr int errorNoEntitlement = static_cast(0x817D0007); + if (!entitlementLabel || !key) return errorParameter; + return errorNoEntitlement; +} + int APS5_VABI sceNpEntitlementAccessInitialize(const NpEntitlementAccessInitParam* init_param, NpEntitlementAccessBootParam* boot_param) { (void)init_param; (void)boot_param; diff --git a/core/libs/prx/libSceRudp/Export.cpp b/core/libs/prx/libSceRudp/Export.cpp index 01da15ba6..7b8eb5082 100644 --- a/core/libs/prx/libSceRudp/Export.cpp +++ b/core/libs/prx/libSceRudp/Export.cpp @@ -51,6 +51,13 @@ int APS5_VABI sceRudpActivate() { return 0; } +int APS5_VABI sceRudpGetStatus(void* status, std::size_t size) { + (void)status; + (void)size; + NotImplemented_nid_no_patch(__func__); + return 0; +} + int APS5_VABI sceRudpEnd(int ctx_id) { std::lock_guard lk(g_mutex); if (!g_inited) { diff --git a/core/libs/tests/GuestOfflineImports.cpp b/core/libs/tests/GuestOfflineImports.cpp new file mode 100644 index 000000000..c2c258810 --- /dev/null +++ b/core/libs/tests/GuestOfflineImports.cpp @@ -0,0 +1,79 @@ +#include "SceTypes.hpp" + +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +int APS5_VABI sceHttpSetCookieEnabled(int, int); +int APS5_VABI sceHttpSendRequest(int, const void*, std::size_t); +int APS5_VABI sceNpEntitlementAccessGetEntitlementKey( + std::uint32_t, const NpUnifiedEntitlementLabel*, NpEntitlementAccessEntitlementKey*); +int APS5_VABI sceRudpInit_nid_postfix(void*, int); +int APS5_VABI sceRudpGetStatus(void*, std::size_t); +int APS5_VABI sceRudpTerminate(); +} + +static void Require(bool value) { if (!value) std::abort(); } + +int main() { + constexpr int invalidValue = static_cast(0x804311FE); + constexpr int network = static_cast(0x80431063); + Require(sceHttpSetCookieEnabled(1, 0) == 0); + Require(sceHttpSendRequest(1, nullptr, 0) == network); + bool cookieUnsupported = false; + try { + sceHttpSetCookieEnabled(1, 1); + } catch (const std::runtime_error& error) { + cookieUnsupported = std::string_view(error.what()) == "sceHttpSetCookieEnabled not implemented"; + } + Require(cookieUnsupported); + for (int enabled : {-1, 2, 0x100}) { + Require(sceHttpSetCookieEnabled(1, enabled) == invalidValue); + } + + constexpr int parameter = static_cast(0x817D0002); + constexpr int noEntitlement = static_cast(0x817D0007); + static_assert(sizeof(NpEntitlementAccessEntitlementKey) == 16); + NpUnifiedEntitlementLabel label{}; + std::memcpy(label.data, "unowned-addon", 14); + struct KeyBuffer { + std::uint64_t before; + NpEntitlementAccessEntitlementKey key; + std::uint64_t after; + } output; + std::memset(&output, 0xa5, sizeof(output)); + std::array original{}; + std::memcpy(original.data(), &output, sizeof(output)); + Require(sceNpEntitlementAccessGetEntitlementKey(0, nullptr, &output.key) == parameter); + Require(sceNpEntitlementAccessGetEntitlementKey(0, &label, nullptr) == parameter); + Require(sceNpEntitlementAccessGetEntitlementKey(0, nullptr, nullptr) == parameter); + for (std::uint32_t serviceLabel : {0u, 1u, 0xffffffffu}) { + Require(sceNpEntitlementAccessGetEntitlementKey(serviceLabel, &label, &output.key) == noEntitlement); + Require(std::memcmp(&output, original.data(), sizeof(output)) == 0); + } + + std::array status; + status.fill(0x5a); + const auto originalStatus = status; + auto unsupported = [](void* data, std::size_t size) { + try { + sceRudpGetStatus(data, size); + } catch (const std::runtime_error& error) { + return std::string_view(error.what()) == "sceRudpGetStatus not implemented"; + } + return false; + }; + Require(unsupported(status.data(), status.size())); + Require(status == originalStatus); + Require(sceRudpInit_nid_postfix(nullptr, 0) == 0); + Require(unsupported(status.data(), status.size())); + Require(status == originalStatus); + Require(unsupported(nullptr, 0)); + Require(sceRudpTerminate() == 0); + return 0; +} diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 7b9455c33..81d0e516a 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -37,6 +37,10 @@ Throughout the project, every function at every stage either **does exactly what ### Unknown function info +- [sceNpEntitlementAccessGetEntitlementKey](../../core/libs/prx/libSceNpEntitlementAccess/Export.cpp) - the three-argument signature, 16-byte key and errors `0x817D0002`/`0x817D0007` follow [KytyPS5](https://github.com/KytyPS5/KytyPS5/blob/main/src/libs/libNet.cpp); PPSA01521 passes a service label, entitlement label and output pointer. Only the no-key path is implemented: local entitlement labels carry no keys, and the output is untouched on failure. Console error precedence and key provisioning are unverified +- [sceRudpGetStatus](../../core/libs/prx/libSceRudp/Export.cpp) - PPSA01521 passes an output pointer and byte count `0xF8`; the status layout, size rules and error behavior are unknown. The export throws without writing output, including after initialization. [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/rudp/rudp.cpp) declares it without arguments and returns success without writing status +- [sceHttpSetCookieEnabled](../../core/libs/prx/libSceHttp/Export.cpp) - the two-argument signature and 0/1 enable values follow [VitaSDK](https://github.com/vitasdk/vita-headers/blob/master/include/psp2/net/http.h) and [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/main/src/core/libraries/network/http.cpp); PPSA01521 passes 0 to disable cookies. Disabling succeeds because the offline HTTP backend has no cookie engine; enabling throws. Invalid enable values use the same error as `sceHttpSetInflateGZIPEnabled`. Like the existing offline HTTP setters, the handle is not validated; requests still fail with the network error. Console behavior is unverified + - [sceHmd2Initialize](../../core/libs/prx/libSceHmd2/Export.cpp) (libSceHmd2) - the error the console returns when the PS VR2 runtime is unavailable is unknown; it returns libSceHmd's `UNSUPPORTED_FEATURE` (0x81110016). PPSA01317 passes a pointer to 16 zeroed bytes and only tests the result for non-zero - PPSA01317 imports declared without parameters, signatures unknown: the remaining [libSceHmd2](../../core/libs/prx/libSceHmd2/Export.cpp) and [libSceVrTracker2](../../core/libs/prx/libSceVrTracker2/Export.cpp) exports. Names from the aerolib NID list - [libSceMbus](../../core/libs/prx/libSceMbus/Export.cpp) (`libSceDeviceService`) - signatures taken from PPSA01317, which calls `sceDeviceServiceInitialize(3, 0)`, polls `sceDeviceServiceGetEventState(1)` and queries again when bit 0 or 1 is set, and calls `sceDeviceServiceQueryDeviceInfo_(0x7001, 0, 0, infos, 1, &count, &unknown, 0x70)`, treating a negative result as an error and `count` as the number of entries. The host has no devices: the event state is 0 and a query returns 0 with `count` 0