-
Notifications
You must be signed in to change notification settings - Fork 37
Extract Content IDs from PS4/PS5 PKGs #390
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,7 +54,73 @@ public: | |
| serial.insert(4, "-"); | ||
| os << std::format(" serial: {}", serial) << std::endl; | ||
| } | ||
|
|
||
| std::string content_ids = getContentIds(root_directory, _PKG_FILE_NAMES); | ||
|
|
||
| if(content_ids.empty()) | ||
| return; | ||
|
|
||
| os << std::format(" content ID(s): {}", content_ids) << std::endl; | ||
| } | ||
|
|
||
| protected: | ||
| std::string getContentIds(std::shared_ptr<iso9660::Entry> root_directory, std::span<const std::string> pkg_file_names) 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; | ||
|
|
||
| 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(); | ||
|
|
||
| if(app_pkg_raw.size() < _PKG_HEADER_SIZE) | ||
| continue; | ||
|
|
||
| uint32_t app_pkg_magic = | ||
| (static_cast<uint32_t>(app_pkg_raw[_PKG_MAGIC_OFFSET + 0]) << 24) | | ||
| (static_cast<uint32_t>(app_pkg_raw[_PKG_MAGIC_OFFSET + 1]) << 16) | | ||
| (static_cast<uint32_t>(app_pkg_raw[_PKG_MAGIC_OFFSET + 2]) << 8) | | ||
| (static_cast<uint32_t>(app_pkg_raw[_PKG_MAGIC_OFFSET + 3]) << 0); | ||
|
Comment on lines
+92
to
+96
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://github.com/superg/redumper/blob/main/systems/ps3.ixx#L161 Can you do something like this here? |
||
|
|
||
| if(app_pkg_magic != _PKG_MAGIC) | ||
| continue; | ||
|
|
||
| std::span<const uint8_t> 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<const char*>(app_pkg_content_id.data()), _PKG_CONTENT_ID_SIZE); | ||
|
|
||
| if(!content_ids.empty()) | ||
| content_ids += ", "; | ||
|
|
||
| content_ids += app_pkg_content_id_text; | ||
|
|
||
| break; | ||
| } | ||
| } | ||
|
|
||
| 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; | ||
|
Comment on lines
+118
to
+122
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd ask you to define a FileHeader structure, like here: https://www.psdevwiki.com/ps4/PKG_files and just use file_header.pkg_content_id in the above code. In general I prefer the code to be clear to understand for somebody who wants to learn what is read and why is read. |
||
| static constexpr std::array<std::string, 3> _PKG_FILE_NAMES = {"app.pkg", "app_h.pkg", "app_0.pkg"}; | ||
| }; | ||
|
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid full-file reads for PKG header parsing.
At Line 87,
app_pkg_entry->read()loads the entire PKG even though this logic only needs header bytes up to_PKG_HEADER_SIZE(0x1000). For large PS4/PS5 PKGs, this can significantly increaseredumper infolatency and memory pressure. Prefer a bounded header read (or a header-only helper) before magic/content-ID checks.🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought the
read()opens the pointer to the data that then can be read on demand? Unsure if I am in the wrong or AI.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Length of output: 203090
@RobbedColek— good instinct to question it, but having looked at the actual implementation infilesystem/iso9660/iso9660_entry.ixx,read()is an eager, full-file load, not a lazy reader:It pre-allocates
sectorsSize() × sectorSizebytes (the whole file), calls through to_dataReader->read()(which hits the underlying file stream), and returns a fully populatedstd::vector<uint8_t>. There is no lazy/streaming interface here.So the concern is valid: for a PS4/PS5 app PKG that can easily be several gigabytes,
app_pkg_entry->read()will attempt to load the entire file into RAM just to check the first0x40 + 36 = 76bytes. A bounded read (if the underlying reader supports seeking + partial reads) or a dedicated header-reading helper would be significantly more efficient.🧠 Learnings used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RobbedColek looks like this is legit callout, those pkg files are huge.
It's a little bit inconvenient but I would ask instead of using directory read(), use data_reader directly and directory entry sectorsLBA() method to get LBA and only read what you need.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.