diff --git a/core/libs/prx/libSceAppContent/CMakeLists.txt b/core/libs/prx/libSceAppContent/CMakeLists.txt new file mode 100644 index 000000000..ee3ad98c7 --- /dev/null +++ b/core/libs/prx/libSceAppContent/CMakeLists.txt @@ -0,0 +1,21 @@ +add_library(libSceAppContent SHARED EXCLUDE_FROM_ALL Export.cpp) +target_include_directories(libSceAppContent PRIVATE ${LIBS_INCLUDE_DIR}) +configure_windows_unwind(libSceAppContent) + +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_appcontent_tests ${LIBS_INCLUDE_DIR}/tests/GuestAppContent.cpp) + target_include_directories(guest_appcontent_tests PRIVATE ${LIBS_INCLUDE_DIR}) + target_link_libraries(guest_appcontent_tests PRIVATE libSceAppContent libc) + configure_windows_unwind(guest_appcontent_tests) + add_test(NAME guest_appcontent COMMAND guest_appcontent_tests) + if(WIN32) + set_tests_properties(guest_appcontent PROPERTIES ENVIRONMENT_MODIFICATION + "PATH=path_list_prepend:$") + endif() +endif() diff --git a/core/libs/prx/libSceAppContent/Export.cpp b/core/libs/prx/libSceAppContent/Export.cpp index 1d72d4913..dfc8300ad 100644 --- a/core/libs/prx/libSceAppContent/Export.cpp +++ b/core/libs/prx/libSceAppContent/Export.cpp @@ -10,6 +10,7 @@ #include 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 uint32_t APPPARAM_ID_SKU_FLAG = 1; static constexpr int32_t SKU_FLAG_FULL = 3; @@ -30,16 +31,13 @@ extern "C" { int APS5_VABI sceAppContentAddcontMount(uint32_t service_label, const NpUnifiedEntitlementLabel* entitlement_label, AppContentMountPoint* mount_point) { (void)service_label; - (void)entitlement_label; - (void)mount_point; - NotImplemented_nid_no_patch(__func__); - return 0; + if (!entitlement_label || !mount_point) return SCE_APP_CONTENT_ERROR_PARAMETER; + return SCE_APP_CONTENT_ERROR_NOT_FOUND; } int APS5_VABI sceAppContentAddcontUnmount(const AppContentMountPoint* mount_point) { - (void)mount_point; - NotImplemented_nid_no_patch(__func__); - return 0; + if (!mount_point) return SCE_APP_CONTENT_ERROR_PARAMETER; + return SCE_APP_CONTENT_ERROR_NOT_FOUND; } int APS5_VABI sceAppContentAppParamGetInt(uint32_t param_id, int32_t* value) { diff --git a/core/libs/tests/GuestAppContent.cpp b/core/libs/tests/GuestAppContent.cpp new file mode 100644 index 000000000..d78335075 --- /dev/null +++ b/core/libs/tests/GuestAppContent.cpp @@ -0,0 +1,27 @@ +#include "SceTypes.hpp" +#include +#include + +extern "C" { +int APS5_VABI sceAppContentAddcontMount(uint32_t, const NpUnifiedEntitlementLabel*, AppContentMountPoint*); +int APS5_VABI sceAppContentAddcontUnmount(const AppContentMountPoint*); +} + +static constexpr int ErrorParameter = static_cast(0x80D90002); +static constexpr int ErrorNotFound = static_cast(0x80D90005); +static void Require(bool value) { if (!value) std::abort(); } + +int main() { + NpUnifiedEntitlementLabel label{}; + std::memcpy(&label, "ADDCONT000000001", 16); + AppContentMountPoint mountPoint{}; + std::memset(&mountPoint, 0x5a, sizeof(mountPoint)); + const AppContentMountPoint untouched = mountPoint; + Require(sceAppContentAddcontMount(0, &label, &mountPoint) == ErrorNotFound); + Require(std::memcmp(&mountPoint, &untouched, sizeof(mountPoint)) == 0); + Require(sceAppContentAddcontMount(0, nullptr, &mountPoint) == ErrorParameter); + Require(sceAppContentAddcontMount(0, &label, nullptr) == ErrorParameter); + std::memcpy(mountPoint.data, "/addcont0", 10); + Require(sceAppContentAddcontUnmount(&mountPoint) == ErrorNotFound); + Require(sceAppContentAddcontUnmount(nullptr) == ErrorParameter); +} diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index b57c3f774..7083e9275 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -120,6 +120,7 @@ Throughout the project, every function at every stage either **does exactly what ### Functional +- [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 - [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`. - [MIMG `tfe` and `lwe`](../../core/shader/recompiler/RdnaDecoder/src/RdnaImageOpDecoder.cpp) never write the status VGPR after the data. On hardware it is written only when a texel fetch hits an unmapped page of a partially resident texture, or, for `lwe` on sample/gather, when the LOD is below the T# `MIN_LOD_WARN`; texture memory is always resident here, and `lwe` on sample, gather and `image_get_lod` throws. - [Image atomics](../../core/shader/recompiler/Optimization/src/ResourceMaterializer.cpp) on 32_SINT and 32_FLOAT surfaces act on the raw dword through an R32_UINT storage view; their float results are assumed to match those measured on 32_UINT surfaces. Other formats throw.