feat(libSceAppContent): answer add-content mounts without installed content

No additional content is installed, so sceAppContentAddcontMount returns
SCE_APP_CONTENT_ERROR_NOT_FOUND with the mount point untouched, and
sceAppContentAddcontUnmount finds no mount to release. A missing label
or mount point is a parameter error. The library gets its own
CMakeLists for the new guest_appcontent test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chad 2026-10-05 20:19:09 -05:00
parent 9cafa33f94
commit ebe0515180
4 changed files with 54 additions and 7 deletions

View File

@ -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:$<TARGET_FILE_DIR:libSceAppContent>")
endif()
endif()

View File

@ -10,6 +10,7 @@
#include <string>
static constexpr int SCE_APP_CONTENT_ERROR_PARAMETER = static_cast<int>(0x80D90002);
static constexpr int SCE_APP_CONTENT_ERROR_NOT_FOUND = static_cast<int>(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) {

View File

@ -0,0 +1,27 @@
#include "SceTypes.hpp"
#include <cstdlib>
#include <cstring>
extern "C" {
int APS5_VABI sceAppContentAddcontMount(uint32_t, const NpUnifiedEntitlementLabel*, AppContentMountPoint*);
int APS5_VABI sceAppContentAddcontUnmount(const AppContentMountPoint*);
}
static constexpr int ErrorParameter = static_cast<int>(0x80D90002);
static constexpr int ErrorNotFound = static_cast<int>(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);
}

View File

@ -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.