mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-12-09 13:19:00 +00:00
libSceAppContent: Determine entitlement labels from additional content param.sfo (#3405)
* Update app_content.cpp * Use hyphenation to determine entitlement id from folder name The original approach I took had two limitations: it relied on entitlement ids being 16 characters long, and it relied on the end of the folder name containing the entitlement. If the former wasn't the case, my code would throw an exception, while the latter would cause the DLC to not detect. To resolve both issues, I've created a more robust algorithm based on observations from the most commonly used PS4 dumpers for modern firmware. * Use DLC param.sfo to determine entitlement id While the logic ends up slightly more complex, this makes the code more robust for other dumping methods/weird DLC folder names from people installing DLC manually. * Update sceAppContentAddcontMount to properly detect additional content folders Based on what I've done in sceAppContentInitialize, I've added code for detecting the correct folder to mount. I've also removed the redundant check for addcont_info status, since we're marking all additional content as installed during sceAppContentInitialize
This commit is contained in:
@@ -57,24 +57,62 @@ int PS4_SYSV_ABI sceAppContentAddcontMount(u32 service_label,
|
||||
OrbisAppContentMountPoint* mount_point) {
|
||||
LOG_INFO(Lib_AppContent, "called");
|
||||
|
||||
const auto& mount_dir = Config::getAddonInstallDir() / title_id / entitlement_label->data;
|
||||
const auto& addon_path = Config::getAddonInstallDir() / title_id;
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
|
||||
for (int i = 0; i < addcont_count; i++) {
|
||||
// Determine which loaded additional content this entitlement label is for.
|
||||
s32 i = 0;
|
||||
while (i < addcont_count) {
|
||||
if (strncmp(entitlement_label->data, addcont_info[i].entitlement_label,
|
||||
ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) != 0) {
|
||||
continue;
|
||||
ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE - 1) == 0) {
|
||||
snprintf(mount_point->data, ORBIS_APP_CONTENT_MOUNTPOINT_DATA_MAXSIZE, "/addcont%d", i);
|
||||
break;
|
||||
}
|
||||
if (addcont_info[i].status != OrbisAppContentAddcontDownloadStatus::Installed) {
|
||||
return ORBIS_APP_CONTENT_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
snprintf(mount_point->data, ORBIS_APP_CONTENT_MOUNTPOINT_DATA_MAXSIZE, "/addcont%d", i);
|
||||
mnt->Mount(mount_dir, mount_point->data);
|
||||
return ORBIS_OK;
|
||||
++i;
|
||||
}
|
||||
|
||||
return ORBIS_APP_CONTENT_ERROR_NOT_FOUND;
|
||||
if (i == addcont_count) {
|
||||
// None of the loaded additional content match the entitlement label requested.
|
||||
return ORBIS_APP_CONTENT_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// Find which directory corresponds to this entitlement
|
||||
for (const auto& entry : std::filesystem::directory_iterator(addon_path)) {
|
||||
if (!entry.is_directory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Open the param.sfo in this folder
|
||||
PSF* dlc_params = new PSF();
|
||||
const auto& param_sfo_path = entry.path() / "sce_sys/param.sfo";
|
||||
if (!std::filesystem::exists(param_sfo_path)) {
|
||||
// This folder doesn't have a param.sfo
|
||||
continue;
|
||||
}
|
||||
dlc_params->Open(param_sfo_path);
|
||||
|
||||
// Validate the available params
|
||||
auto category = dlc_params->GetString("CATEGORY");
|
||||
auto content_id = dlc_params->GetString("CONTENT_ID");
|
||||
if (!category.has_value() || strncmp(category.value().data(), "ac", 2) != 0 ||
|
||||
!content_id.has_value() ||
|
||||
content_id.value().length() <= ORBIS_APP_CONTENT_ENTITLEMENT_LABEL_OFFSET) {
|
||||
// This folder fails the error checks performed in sceAppContentInitialize.
|
||||
continue;
|
||||
}
|
||||
|
||||
auto entitlement_id = content_id.value().substr(ORBIS_APP_CONTENT_ENTITLEMENT_LABEL_OFFSET);
|
||||
if (strncmp(entitlement_id.data(), entitlement_label->data, entitlement_id.length()) == 0) {
|
||||
// We've located the correct folder.
|
||||
mnt->Mount(entry.path(), mount_point->data);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
// Hitting this shouldn't be possible, as it would mean the entitlement was loaded,
|
||||
// but the folder it was loaded from doesn't exist.
|
||||
UNREACHABLE_MSG("Folder for loaded entitlement label {} doesn't exist.",
|
||||
entitlement_label->data);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceAppContentAddcontShrink() {
|
||||
@@ -215,7 +253,7 @@ int PS4_SYSV_ABI sceAppContentGetAddcontInfoList(u32 service_label,
|
||||
int PS4_SYSV_ABI sceAppContentGetEntitlementKey(
|
||||
u32 service_label, const OrbisNpUnifiedEntitlementLabel* entitlement_label,
|
||||
OrbisAppContentGetEntitlementKey* key) {
|
||||
LOG_ERROR(Lib_AppContent, "called");
|
||||
LOG_INFO(Lib_AppContent, "called");
|
||||
|
||||
if (entitlement_label == nullptr || key == nullptr) {
|
||||
return ORBIS_APP_CONTENT_ERROR_PARAMETER;
|
||||
@@ -257,15 +295,50 @@ int PS4_SYSV_ABI sceAppContentInitialize(const OrbisAppContentInitParam* initPar
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(addon_path)) {
|
||||
if (entry.is_directory()) {
|
||||
auto entitlement_label = entry.path().filename().string();
|
||||
auto& info = addcont_info[addcont_count++];
|
||||
info.status = OrbisAppContentAddcontDownloadStatus::Installed;
|
||||
// Look for a param.sfo in the additional content directory.
|
||||
const auto& param_sfo_path = entry.path() / "sce_sys/param.sfo";
|
||||
if (!std::filesystem::exists(param_sfo_path)) {
|
||||
LOG_WARNING(Lib_AppContent, "Additonal content folder {} has no param.sfo",
|
||||
entry.path().filename().string());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Our recommended dumping method prepends the folder name with the game serial
|
||||
// Since they end the folder name with the entitlement label, copy from the end instead.
|
||||
entitlement_label.copy(info.entitlement_label, sizeof(info.entitlement_label),
|
||||
entitlement_label.length() -
|
||||
ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE + 1);
|
||||
// Open the param.sfo, make sure it's actually for additional content.
|
||||
PSF* dlc_params = new PSF();
|
||||
dlc_params->Open(param_sfo_path);
|
||||
|
||||
auto category = dlc_params->GetString("CATEGORY");
|
||||
if (category.has_value() && strncmp(category.value().data(), "ac", 2) == 0) {
|
||||
// We've located additional content. Find the entitlement id from the content id.
|
||||
auto content_id = dlc_params->GetString("CONTENT_ID");
|
||||
if (!content_id.has_value()) {
|
||||
LOG_WARNING(Lib_AppContent,
|
||||
"Additonal content {} param.sfo is missing CONTENT_ID",
|
||||
entry.path().filename().string());
|
||||
continue;
|
||||
}
|
||||
|
||||
// content id's have consistent formatting, so this will always work.
|
||||
// They follow the format UPXXXX-CUSAXXXXX_XX-entitlement
|
||||
if (content_id.value().length() <= ORBIS_APP_CONTENT_ENTITLEMENT_LABEL_OFFSET) {
|
||||
LOG_WARNING(Lib_AppContent,
|
||||
"Additonal content {} param.sfo has malformed CONTENT_ID",
|
||||
entry.path().filename().string());
|
||||
continue;
|
||||
}
|
||||
auto entitlement_id =
|
||||
content_id.value().substr(ORBIS_APP_CONTENT_ENTITLEMENT_LABEL_OFFSET);
|
||||
LOG_INFO(Lib_AppContent, "Entitlement {} found", entitlement_id);
|
||||
|
||||
// Save the additional content info in addcont_info.
|
||||
auto& info = addcont_info[addcont_count++];
|
||||
entitlement_id.copy(info.entitlement_label, entitlement_id.length());
|
||||
info.status = OrbisAppContentAddcontDownloadStatus::Installed;
|
||||
} else {
|
||||
LOG_WARNING(Lib_AppContent, "Additonal content folder {} is not additional content",
|
||||
entry.path().filename().string());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ constexpr int ORBIS_APP_CONTENT_TEMPORARY_DATA_OPTION_NONE = 0;
|
||||
constexpr int ORBIS_APP_CONTENT_TEMPORARY_DATA_OPTION_FORMAT = (1 << 0);
|
||||
constexpr int ORBIS_NP_UNIFIED_ENTITLEMENT_LABEL_SIZE = 17;
|
||||
constexpr int ORBIS_APP_CONTENT_ENTITLEMENT_KEY_SIZE = 16;
|
||||
constexpr int ORBIS_APP_CONTENT_ENTITLEMENT_LABEL_OFFSET = 20;
|
||||
constexpr int ORBIS_APP_CONTENT_INFO_LIST_MAX_SIZE = 2500;
|
||||
|
||||
enum class OrbisAppContentAddcontDownloadStatus : u32 {
|
||||
|
||||
Reference in New Issue
Block a user