diff --git a/core/libs/prx/libSceAppContent/Export.cpp b/core/libs/prx/libSceAppContent/Export.cpp index dfc8300ad..3a025d498 100644 --- a/core/libs/prx/libSceAppContent/Export.cpp +++ b/core/libs/prx/libSceAppContent/Export.cpp @@ -11,6 +11,7 @@ static constexpr int SCE_APP_CONTENT_ERROR_PARAMETER = static_cast(0x80D90002); static constexpr int SCE_APP_CONTENT_ERROR_NOT_FOUND = static_cast(0x80D90005); +static constexpr int SCE_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT = static_cast(0x80D90007); static constexpr uint32_t APPPARAM_ID_SKU_FLAG = 1; static constexpr int32_t SKU_FLAG_FULL = 3; @@ -18,6 +19,11 @@ static constexpr char TEMPORARY_MOUNT_POINT[] = "/temp0"; static constexpr char DOWNLOAD_MOUNT_POINT[] = "/download0"; static constexpr uint32_t TEMPORARY_DATA_OPTION_FORMAT = 1; +struct AppContentAddcontInfo { + NpUnifiedEntitlementLabel entitlement_label; + uint32_t status; +}; + static std::filesystem::path TemporaryDirectory(const AppContentMountPoint* mount_point) { if (!mount_point || std::strncmp(mount_point->data, TEMPORARY_MOUNT_POINT, sizeof(mount_point->data)) != 0) APS5_INVALID_ARG_EX; return ResolvePath_nid_no_patch(TEMPORARY_MOUNT_POINT); @@ -40,6 +46,19 @@ int APS5_VABI sceAppContentAddcontUnmount(const AppContentMountPoint* mount_poin return SCE_APP_CONTENT_ERROR_NOT_FOUND; } +int APS5_VABI sceAppContentGetAddcontInfo(uint32_t service_label, const NpUnifiedEntitlementLabel* entitlement_label, AppContentAddcontInfo* info) { + (void)service_label; + if (!entitlement_label || !info) return SCE_APP_CONTENT_ERROR_PARAMETER; + return SCE_APP_CONTENT_ERROR_DRM_NO_ENTITLEMENT; +} + +int APS5_VABI sceAppContentGetAddcontInfoList(uint32_t service_label, AppContentAddcontInfo* list, uint32_t list_num, uint32_t* hit_num) { + (void)service_label; + if ((!list || list_num == 0) && !hit_num) return SCE_APP_CONTENT_ERROR_PARAMETER; + if (hit_num) *hit_num = 0; + return 0; +} + int APS5_VABI sceAppContentAppParamGetInt(uint32_t param_id, int32_t* value) { if (!value) return SCE_APP_CONTENT_ERROR_PARAMETER; switch (param_id) { diff --git a/core/libs/tests/GuestAppContent.cpp b/core/libs/tests/GuestAppContent.cpp index d78335075..67b4a6c68 100644 --- a/core/libs/tests/GuestAppContent.cpp +++ b/core/libs/tests/GuestAppContent.cpp @@ -5,10 +5,13 @@ extern "C" { int APS5_VABI sceAppContentAddcontMount(uint32_t, const NpUnifiedEntitlementLabel*, AppContentMountPoint*); int APS5_VABI sceAppContentAddcontUnmount(const AppContentMountPoint*); +int APS5_VABI sceAppContentGetAddcontInfo(uint32_t, const NpUnifiedEntitlementLabel*, void*); +int APS5_VABI sceAppContentGetAddcontInfoList(uint32_t, void*, uint32_t, uint32_t*); } static constexpr int ErrorParameter = static_cast(0x80D90002); static constexpr int ErrorNotFound = static_cast(0x80D90005); +static constexpr int ErrorDrmNoEntitlement = static_cast(0x80D90007); static void Require(bool value) { if (!value) std::abort(); } int main() { @@ -24,4 +27,31 @@ int main() { std::memcpy(mountPoint.data, "/addcont0", 10); Require(sceAppContentAddcontUnmount(&mountPoint) == ErrorNotFound); Require(sceAppContentAddcontUnmount(nullptr) == ErrorParameter); + + unsigned char info[24]; + std::memset(info, 0x5a, sizeof(info)); + unsigned char untouchedInfo[24]; + std::memcpy(untouchedInfo, info, sizeof(info)); + Require(sceAppContentGetAddcontInfo(0, &label, info) == ErrorDrmNoEntitlement); + Require(std::memcmp(info, untouchedInfo, sizeof(info)) == 0); + Require(sceAppContentGetAddcontInfo(0, nullptr, info) == ErrorParameter); + Require(sceAppContentGetAddcontInfo(0, &label, nullptr) == ErrorParameter); + + uint32_t hitNum = 0x5a5a5a5a; + Require(sceAppContentGetAddcontInfoList(0, nullptr, 0, &hitNum) == 0); + Require(hitNum == 0); + hitNum = 0x5a5a5a5a; + Require(sceAppContentGetAddcontInfoList(0, info, 0, &hitNum) == 0); + Require(hitNum == 0); + hitNum = 0x5a5a5a5a; + Require(sceAppContentGetAddcontInfoList(0, nullptr, 1, &hitNum) == 0); + Require(hitNum == 0); + hitNum = 0x5a5a5a5a; + Require(sceAppContentGetAddcontInfoList(0, info, 1, &hitNum) == 0); + Require(hitNum == 0); + Require(std::memcmp(info, untouchedInfo, sizeof(info)) == 0); + Require(sceAppContentGetAddcontInfoList(0, info, 1, nullptr) == 0); + Require(sceAppContentGetAddcontInfoList(0, nullptr, 0, nullptr) == ErrorParameter); + Require(sceAppContentGetAddcontInfoList(0, info, 0, nullptr) == ErrorParameter); + Require(sceAppContentGetAddcontInfoList(0, nullptr, 1, nullptr) == ErrorParameter); } diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index 9a5bbf0d2..073e574f6 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -200,7 +200,7 @@ Throughout the project, every function at every stage either **does exactly what ### Functional - [sceNetSendmsg and sceNetRecvmsg](../../core/libs/prx/libSceNet/Export.cpp) (libSceNet) copy the scatter/gather list through one host buffer and accept only the flags `sceNetSend`/`sceNetRecv` accept. Sending control data throws; a receive never returns control data (`msg_controllen` 0), since no socket option that queues it is supported -- [Additional content](../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) is not installed or mounted: `sceAppContentAddcontMount` answers `NOT_FOUND` for every entitlement label and `sceAppContentAddcontUnmount` for every mount point, as no add-content mount is ever handed out +- [Additional content](../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) is not installed or mounted: `sceAppContentAddcontMount` answers `NOT_FOUND` for every entitlement label and `sceAppContentAddcontUnmount` for every mount point, as no add-content mount is ever handed out; `sceAppContentGetAddcontInfo` answers `DRM_NO_ENTITLEMENT` for every label and `sceAppContentGetAddcontInfoList` reports 0 entries - [sceKernelInstallExceptionHandler](../../core/libs/prx/libkernel/System/src/Exception.cpp) records the handler but nothing delivers to it: a host fault still ends the process, and `sceKernelRaiseException` is not implemented. PPSA12544's PS5Util module (Unity) installs a SIGUSR1 handler that reads `uc_mcontext.mc_rsp` (offset 0xf8 of the context) and raises SIGUSR1 on other threads to stop them for garbage collection, so delivery must run the handler on the target thread with a FreeBSD `ucontext` - [libSceUlt queues](../../core/libs/prx/libSceUlt/Export.cpp) use preallocated host storage instead of the supplied guest work area; its existing size formula and the 512-byte object layouts remain unverified. Waiting-queue pool thread limits and waiter priority ordering are not implemented. Finalization wakes blocked queue callers with `ULT_ERROR_STATE`. - [Thread cancellation](../../core/libs/prx/libkernel/Pthread/src/Thread.cpp) (libkernel) is not modelled: `scePthreadCancel` throws, so no cancel request is ever pending and `scePthreadTestcancel` and `pthread_testcancel` return without acting; the cancel state and type set by `scePthreadSetcancelstate` and `scePthreadSetcanceltype` are stored and not used