From b13f6e1b3956b77e30a6f07a2dc766e82ed2bc21 Mon Sep 17 00:00:00 2001 From: Piotr Swat Date: Wed, 10 Jun 2026 23:59:00 +0200 Subject: [PATCH 1/3] Extract Content IDs from PS4/PS5 PKGs --- systems/ps4.ixx | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ systems/ps5.ixx | 10 ++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/systems/ps4.ixx b/systems/ps4.ixx index 78038d40..28e2dff0 100644 --- a/systems/ps4.ixx +++ b/systems/ps4.ixx @@ -54,6 +54,63 @@ public: serial.insert(4, "-"); os << std::format(" serial: {}", serial) << std::endl; } + + std::string content_ids = getContentIds(root_directory, "app.pkg"); + + if(content_ids.empty()) + return; + + os << std::format(" content ID(s): {}", content_ids) << std::endl; + } + +protected: + std:: string getContentIds(std::shared_ptr root_directory, std::string pkg_name) const + { + auto app_directory = root_directory->subEntry("app"); + if(!app_directory) + return ""; + + auto app_directory_entries = app_directory->entries(); + std::string content_ids; + + for(auto &e : app_directory_entries) + { + if(!e->isDirectory()) + continue; + + auto app_pkg_entry = e->subEntry(pkg_name); + if (!app_pkg_entry) + continue; + + auto app_pkg_raw = app_pkg_entry->read(); + + const uint32_t pkg_magic_const = 0x7F434E54; + const uint32_t pkg_magic_offset = 0x00; + const uint32_t pkg_magic_size = 4; + + uint32_t app_pkg_magic = + (static_cast(app_pkg_raw[pkg_magic_offset + 0]) << 24) | + (static_cast(app_pkg_raw[pkg_magic_offset + 1]) << 16) | + (static_cast(app_pkg_raw[pkg_magic_offset + 2]) << 8) | + (static_cast(app_pkg_raw[pkg_magic_offset + 3]) << 0); + + if(pkg_magic_const != app_pkg_magic) + continue; + + const uint32_t pkg_content_id_offset = 0x40; + const uint32_t pkg_content_id_size = 36; + + std::span app_pkg_content_id(app_pkg_raw.data() + pkg_content_id_offset, pkg_content_id_size); + + std::string_view app_pkg_content_id_text(reinterpret_cast(app_pkg_content_id.data()), pkg_content_id_size); + + if (!content_ids.empty()) + content_ids += ", "; + + content_ids += app_pkg_content_id_text; + } + + return content_ids; } }; diff --git a/systems/ps5.ixx b/systems/ps5.ixx index aa3520ea..20aeecc3 100644 --- a/systems/ps5.ixx +++ b/systems/ps5.ixx @@ -11,6 +11,7 @@ export module systems.ps5; import filesystem.iso9660; import readers.data_reader; +import systems.ps4; import utils.misc; import utils.strings; @@ -19,7 +20,7 @@ import utils.strings; namespace gpsxre { -export class SystemPS5 : public System +export class SystemPS5 : public SystemPS4 { public: std::string getName() override @@ -48,6 +49,13 @@ public: if(auto it = param_json.find("masterDataId"); it != param_json.end()) os << std::format(" serial: {}", it->second.insert(4, "-")) << std::endl; + + std::string content_ids = getContentIds(root_directory, "app_sc.pkg"); + + if(content_ids.empty()) + return; + + os << std::format(" content ID(s): {}", content_ids) << std::endl; } private: From 4593348d10a0a023f6f487ed79a0b79e19d6c0be Mon Sep 17 00:00:00 2001 From: Piotr Swat Date: Thu, 11 Jun 2026 18:28:22 +0200 Subject: [PATCH 2/3] Fixes for extracting Content ID from PS4/PS4 PKGs: - added size validation, - removed unused const, - set some of params/variables to const, - adapted code style to match project. --- systems/ps4.ixx | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/systems/ps4.ixx b/systems/ps4.ixx index 28e2dff0..2d0a0955 100644 --- a/systems/ps4.ixx +++ b/systems/ps4.ixx @@ -64,7 +64,7 @@ public: } protected: - std:: string getContentIds(std::shared_ptr root_directory, std::string pkg_name) const + std::string getContentIds(std::shared_ptr root_directory, const std::string pkg_name) const { auto app_directory = root_directory->subEntry("app"); if(!app_directory) @@ -79,32 +79,28 @@ protected: continue; auto app_pkg_entry = e->subEntry(pkg_name); - if (!app_pkg_entry) + if(!app_pkg_entry) continue; auto app_pkg_raw = app_pkg_entry->read(); - const uint32_t pkg_magic_const = 0x7F434E54; - const uint32_t pkg_magic_offset = 0x00; - const uint32_t pkg_magic_size = 4; + if(app_pkg_raw.size() < _PKG_HEADER_SIZE) + continue; uint32_t app_pkg_magic = - (static_cast(app_pkg_raw[pkg_magic_offset + 0]) << 24) | - (static_cast(app_pkg_raw[pkg_magic_offset + 1]) << 16) | - (static_cast(app_pkg_raw[pkg_magic_offset + 2]) << 8) | - (static_cast(app_pkg_raw[pkg_magic_offset + 3]) << 0); + (static_cast(app_pkg_raw[_PKG_MAGIC_OFFSET + 0]) << 24) | + (static_cast(app_pkg_raw[_PKG_MAGIC_OFFSET + 1]) << 16) | + (static_cast(app_pkg_raw[_PKG_MAGIC_OFFSET + 2]) << 8) | + (static_cast(app_pkg_raw[_PKG_MAGIC_OFFSET + 3]) << 0); - if(pkg_magic_const != app_pkg_magic) + if(app_pkg_magic != _PKG_MAGIC) continue; - const uint32_t pkg_content_id_offset = 0x40; - const uint32_t pkg_content_id_size = 36; - - std::span app_pkg_content_id(app_pkg_raw.data() + pkg_content_id_offset, pkg_content_id_size); + std::span app_pkg_content_id(app_pkg_raw.data() + _PKG_CONTENT_ID_OFFSET, _PKG_CONTENT_ID_SIZE); - std::string_view app_pkg_content_id_text(reinterpret_cast(app_pkg_content_id.data()), pkg_content_id_size); + std::string_view app_pkg_content_id_text(reinterpret_cast(app_pkg_content_id.data()), _PKG_CONTENT_ID_SIZE); - if (!content_ids.empty()) + if(!content_ids.empty()) content_ids += ", "; content_ids += app_pkg_content_id_text; @@ -112,6 +108,13 @@ protected: return content_ids; } + +private: + static constexpr uint32_t _PKG_HEADER_SIZE = 0x1000; + static constexpr uint32_t _PKG_MAGIC = 0x7F434E54; + static constexpr uint32_t _PKG_MAGIC_OFFSET = 0x00; + static constexpr uint32_t _PKG_CONTENT_ID_OFFSET = 0x40; + static constexpr uint32_t _PKG_CONTENT_ID_SIZE = 36; }; } From 3c8b10fc513779808ec45ddf838c7096942d1646 Mon Sep 17 00:00:00 2001 From: Piotr Swat Date: Sun, 14 Jun 2026 11:37:51 +0200 Subject: [PATCH 3/3] Added support for reading Content ID from PKGs for PS4 multi-disc games --- systems/ps4.ixx | 46 ++++++++++++++++++++++++++-------------------- systems/ps5.ixx | 4 +++- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/systems/ps4.ixx b/systems/ps4.ixx index 2d0a0955..df63ed26 100644 --- a/systems/ps4.ixx +++ b/systems/ps4.ixx @@ -55,7 +55,7 @@ public: os << std::format(" serial: {}", serial) << std::endl; } - std::string content_ids = getContentIds(root_directory, "app.pkg"); + std::string content_ids = getContentIds(root_directory, _PKG_FILE_NAMES); if(content_ids.empty()) return; @@ -64,7 +64,7 @@ public: } protected: - std::string getContentIds(std::shared_ptr root_directory, const std::string pkg_name) const + std::string getContentIds(std::shared_ptr root_directory, std::span pkg_file_names) const { auto app_directory = root_directory->subEntry("app"); if(!app_directory) @@ -78,32 +78,37 @@ protected: if(!e->isDirectory()) continue; - auto app_pkg_entry = e->subEntry(pkg_name); - if(!app_pkg_entry) - continue; + for(const auto& pkg_file_name : pkg_file_names) + { + auto app_pkg_entry = e->subEntry(pkg_file_name); + if(!app_pkg_entry) + continue; - auto app_pkg_raw = app_pkg_entry->read(); + auto app_pkg_raw = app_pkg_entry->read(); - if(app_pkg_raw.size() < _PKG_HEADER_SIZE) - continue; + if(app_pkg_raw.size() < _PKG_HEADER_SIZE) + continue; - uint32_t app_pkg_magic = - (static_cast(app_pkg_raw[_PKG_MAGIC_OFFSET + 0]) << 24) | - (static_cast(app_pkg_raw[_PKG_MAGIC_OFFSET + 1]) << 16) | - (static_cast(app_pkg_raw[_PKG_MAGIC_OFFSET + 2]) << 8) | - (static_cast(app_pkg_raw[_PKG_MAGIC_OFFSET + 3]) << 0); + uint32_t app_pkg_magic = + (static_cast(app_pkg_raw[_PKG_MAGIC_OFFSET + 0]) << 24) | + (static_cast(app_pkg_raw[_PKG_MAGIC_OFFSET + 1]) << 16) | + (static_cast(app_pkg_raw[_PKG_MAGIC_OFFSET + 2]) << 8) | + (static_cast(app_pkg_raw[_PKG_MAGIC_OFFSET + 3]) << 0); - if(app_pkg_magic != _PKG_MAGIC) - continue; + if(app_pkg_magic != _PKG_MAGIC) + continue; + + std::span app_pkg_content_id(app_pkg_raw.data() + _PKG_CONTENT_ID_OFFSET, _PKG_CONTENT_ID_SIZE); - std::span app_pkg_content_id(app_pkg_raw.data() + _PKG_CONTENT_ID_OFFSET, _PKG_CONTENT_ID_SIZE); + std::string_view app_pkg_content_id_text(reinterpret_cast(app_pkg_content_id.data()), _PKG_CONTENT_ID_SIZE); - std::string_view app_pkg_content_id_text(reinterpret_cast(app_pkg_content_id.data()), _PKG_CONTENT_ID_SIZE); + if(!content_ids.empty()) + content_ids += ", "; - if(!content_ids.empty()) - content_ids += ", "; + content_ids += app_pkg_content_id_text; - content_ids += app_pkg_content_id_text; + break; + } } return content_ids; @@ -115,6 +120,7 @@ private: static constexpr uint32_t _PKG_MAGIC_OFFSET = 0x00; static constexpr uint32_t _PKG_CONTENT_ID_OFFSET = 0x40; static constexpr uint32_t _PKG_CONTENT_ID_SIZE = 36; + static constexpr std::array _PKG_FILE_NAMES = {"app.pkg", "app_h.pkg", "app_0.pkg"}; }; } diff --git a/systems/ps5.ixx b/systems/ps5.ixx index 20aeecc3..e078814a 100644 --- a/systems/ps5.ixx +++ b/systems/ps5.ixx @@ -50,7 +50,7 @@ public: if(auto it = param_json.find("masterDataId"); it != param_json.end()) os << std::format(" serial: {}", it->second.insert(4, "-")) << std::endl; - std::string content_ids = getContentIds(root_directory, "app_sc.pkg"); + std::string content_ids = getContentIds(root_directory, _PKG_FILE_NAMES); if(content_ids.empty()) return; @@ -59,6 +59,8 @@ public: } private: + static constexpr std::array _PKG_FILE_NAMES = {"app_sc.pkg"}; + std::map loadJSON(std::shared_ptr json_entry) const { std::map json;