diff --git a/core/libs/prx/libSceAppContent/Export.cpp b/core/libs/prx/libSceAppContent/Export.cpp index 478f72de02..9d44d2f253 100644 --- a/core/libs/prx/libSceAppContent/Export.cpp +++ b/core/libs/prx/libSceAppContent/Export.cpp @@ -61,6 +61,14 @@ bool IsTemporaryMountPoint(const AppContentMountPoint* mountPoint) { return std::strncmp(mountPoint->data, TEMPORARY_MOUNT_POINT, sizeof(mountPoint->data)) == 0; } +bool IsDownloadMountPoint(const AppContentMountPoint* mountPoint) { + if (!mountPoint) return false; + for (const char* prefix : {"/download0", "/download1", "/download2"}) { + if (std::strncmp(mountPoint->data, prefix, sizeof(mountPoint->data)) == 0) return true; + } + return false; +} + } static void ClearDirectory(const std::filesystem::path& directory) { @@ -135,8 +143,8 @@ int APS5_VABI sceAppContentAppParamGetInt(uint32_t param_id, int32_t* value) { int APS5_VABI sceAppContentDownloadDataFormat(const AppContentMountPoint* mount_point) { if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; if (!mount_point) return SCE_APP_CONTENT_ERROR_PARAMETER; - if (std::strncmp(mount_point->data, DOWNLOAD_MOUNT_POINT, sizeof(mount_point->data)) != 0) return SCE_APP_CONTENT_ERROR_NOT_MOUNTED; - const auto directory = ResolvePath_nid_no_patch(DOWNLOAD_MOUNT_POINT); + if (!IsDownloadMountPoint(mount_point)) return SCE_APP_CONTENT_ERROR_NOT_MOUNTED; + const auto directory = ResolvePath_nid_no_patch(mount_point->data); if (std::filesystem::create_directories(directory)) RecordWrittenPath_nid_no_patch(directory); ClearDirectory(directory); return 0; @@ -145,13 +153,14 @@ int APS5_VABI sceAppContentDownloadDataFormat(const AppContentMountPoint* mount_ int APS5_VABI sceAppContentDownloadDataGetAvailableSpaceKb(const AppContentMountPoint* mountPoint, size_t* availableSpaceKb) { if (!initialized.load()) return SCE_APP_CONTENT_ERROR_NOT_INITIALIZED; if (!mountPoint || !availableSpaceKb) return SCE_APP_CONTENT_ERROR_PARAMETER; - if (std::strncmp(mountPoint->data, DOWNLOAD_MOUNT_POINT, sizeof(mountPoint->data)) != 0) return SCE_APP_CONTENT_ERROR_NOT_MOUNTED; + if (!IsDownloadMountPoint(mountPoint)) return SCE_APP_CONTENT_ERROR_NOT_MOUNTED; + if (std::strncmp(mountPoint->data, DOWNLOAD_MOUNT_POINT, sizeof(mountPoint->data)) != 0) NotImplemented_nid_no_patch(__func__); const std::uint64_t quotaKb = GetAppDownloadDataSizeMiB_nid_postfix() * 1024u; if (quotaKb == 0) { *availableSpaceKb = 0; return 0; } - const auto directory = ResolvePath_nid_no_patch(DOWNLOAD_MOUNT_POINT); + const auto directory = ResolvePath_nid_no_patch(mountPoint->data); if (std::filesystem::create_directories(directory)) RecordWrittenPath_nid_no_patch(directory); std::uint64_t usedKb = 0; for (const auto& entry : std::filesystem::recursive_directory_iterator(directory)) { diff --git a/core/libs/tests/GuestAppContent.cpp b/core/libs/tests/GuestAppContent.cpp index 709b87bd6a..c08d8f688c 100644 --- a/core/libs/tests/GuestAppContent.cpp +++ b/core/libs/tests/GuestAppContent.cpp @@ -104,6 +104,15 @@ int main() { Require(std::filesystem::exists(downloadData / "index.dat")); Require(sceAppContentDownloadDataFormat(&download) == 0); Require(std::filesystem::is_directory(downloadData) && std::filesystem::is_empty(downloadData)); + AppContentMountPoint download2Mount{}; + std::memcpy(download2Mount.data, "/download2", 11); + const auto download2Data = ResolvePath_nid_no_patch("/download2"); + std::filesystem::create_directories(download2Data); + { std::ofstream file(downloadData / "preserve.bin"); file << "download0"; } + { std::ofstream file(download2Data / "old.bin"); file << "download2"; } + Require(sceAppContentDownloadDataFormat(&download2Mount) == 0); + Require(std::filesystem::is_directory(download2Data) && std::filesystem::is_empty(download2Data)); + Require(std::filesystem::exists(downloadData / "preserve.bin")); const int32_t expectedParams[] = {3, 23566, -7, 0, 2147483647}; for (uint32_t id = 0; id < 5; ++id) { diff --git a/docs/dev/debt/libSceAppContent.md b/docs/dev/debt/libSceAppContent.md index c75f621a36..d54210cc47 100644 --- a/docs/dev/debt/libSceAppContent.md +++ b/docs/dev/debt/libSceAppContent.md @@ -11,8 +11,8 @@ | `sceAppContentGetGameTrialsFlag` | `1saJukIkcKw` | | `sceAppContentGetPlayableStatus` | `y8meQn-Qy5c` | -- [sceAppContentDownloadDataGetAvailableSpaceKb](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - a title whose `param.json` declares `downloadDataSize` 0 gets 0 KB and OK; the console's behaviour for a title without a download data area is unverified (it may return an error instead). KytyPS5 does not special-case it, and shadPS4 stubs 1048576 KB. One disassembled title (PPSA15246) calls it with the `/download0` mount point and a zeroed 64-bit output, ignores the return value and multiplies the output by 1024, so for that title an error that leaves the output untouched and an OK with 0 KB look the same -- [sceAppContentDownloadDataFormat](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - empties the `/download0` directory, creating it if needed. Imported by PPSA01317. A null mount point returns `PARAMETER` and another mount point returns `NOT_MOUNTED` as in `sceAppContentTemporaryDataFormat` and `sceAppContentDownloadDataGetAvailableSpaceKb`; the console's error codes for both and its behaviour with files still open are unverified +- [sceAppContentDownloadDataGetAvailableSpaceKb](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - a title whose `param.json` declares `downloadDataSize` 0 gets 0 KB and OK; the console's behaviour for a title without a download data area is unverified (it may return an error instead). KytyPS5 does not special-case it, and shadPS4 stubs 1048576 KB. One disassembled title (PPSA15246) calls it with the `/download0` mount point and a zeroed 64-bit output, ignores the return value and multiplies the output by 1024, so for that title an error that leaves the output untouched and an OK with 0 KB look the same. `/download1` and `/download2` queries remain unimplemented because their quota model is unverified. +- [sceAppContentDownloadDataFormat](../../../core/libs/prx/libSceAppContent/Export.cpp) (libSceAppContent) - empties the `/download0`, `/download1`, or `/download2` directory, creating it if needed. Imported by PPSA01317. A null mount point returns `PARAMETER` and another mount point returns `NOT_MOUNTED` as in `sceAppContentTemporaryDataFormat` and `sceAppContentDownloadDataGetAvailableSpaceKb`; the console's error codes for both and its behaviour with files still open are unverified - [libSceAppContent pending exports](../../../core/libs/prx/libSceAppContent/Export.cpp) - 8 named callable exports are listed in the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceAppContent.c) and were cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`.