From 7bd433ffea83d947c938656609922d1bb288d727 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Tue, 15 Nov 2022 17:45:21 +0200 Subject: [PATCH] we can now extract icon0 from pkg (WIP progress in the rest extraction) --- shadPS4/emulator/fileFormat/PKG.cpp | 35 +++++++++ shadPS4/emulator/fileFormat/PKG.h | 116 +++++++++++++++++++++++++++- shadPS4/gui/shadps4gui.cpp | 17 ++++ shadPS4/shadPS4.vcxproj | 4 +- 4 files changed, 168 insertions(+), 4 deletions(-) diff --git a/shadPS4/emulator/fileFormat/PKG.cpp b/shadPS4/emulator/fileFormat/PKG.cpp index 571bee4a..f7088e5f 100644 --- a/shadPS4/emulator/fileFormat/PKG.cpp +++ b/shadPS4/emulator/fileFormat/PKG.cpp @@ -28,4 +28,39 @@ bool PKG::open(const std::string& filepath) { file.Close(); return true; +} +bool PKG::extract(const std::string& filepath, const std::string& extractPath, std::string& failreason) +{ + this->extractPath = extractPath; + FsFile file; + if (!file.Open(filepath, fsRead)) + { + return false; + } + pkgSize = file.getFileSize(); + PKGHeader pkgheader; + file.ReadBE(pkgheader); + + file.Seek(0, fsSeekSet); + pkg = (U08*)mmap(pkgSize, file.fileDescr()); + + file.Read(pkg, pkgSize); + + U32 offset = pkgheader.pkg_table_entry_offset; + U32 n_files = pkgheader.pkg_table_entry_count; + + + for (int i = 0; i < n_files; i++) { + PKGEntry entry = (PKGEntry&)pkg[offset + i * 0x20]; + ReadBE(entry); + if (entry.id == 0x1200)//test code for extracting icon0 + { + FsFile out; + out.Open(extractPath + "icon0.png", fsWrite); + out.Write(pkg + entry.offset, entry.size); + out.Close(); + } + } + munmap(pkg); + return true; } \ No newline at end of file diff --git a/shadPS4/emulator/fileFormat/PKG.h b/shadPS4/emulator/fileFormat/PKG.h index d1a512c6..9d2ab0ec 100644 --- a/shadPS4/emulator/fileFormat/PKG.h +++ b/shadPS4/emulator/fileFormat/PKG.h @@ -1,17 +1,105 @@ #pragma once #include #include "../../Types.h" +#include +#include struct PKGHeader { - /*BE*/U32 magic; // Magic + /*BE*/U32 magic;// Magic + /*BE*/U32 pkg_type; + /*BE*/U32 pkg_0x8; //unknown field + /*BE*/U32 pkg_file_count; + /*BE*/U32 pkg_table_entry_count; + /*BE*/U16 pkg_sc_entry_count; + /*BE*/U16 pkg_table_entry_count_2;// same as pkg_entry_count + /*BE*/U32 pkg_table_entry_offset;//file table offset + /*BE*/U32 pkg_sc_entry_data_size; + /*BE*/U64 pkg_body_offset;//offset of PKG entries + /*BE*/U64 pkg_body_size;//length of all PKG entries + /*BE*/U64 pkg_content_offset; + /*BE*/U64 pkg_content_size; + U08 pkg_content_id[0x24];//packages' content ID as a 36-byte string + U08 pkg_padding[0xC];//padding + /*BE*/U32 pkg_drm_type;//DRM type + /*BE*/U32 pkg_content_type;//Content type + /*BE*/U32 pkg_content_flags;//Content flags + /*BE*/U32 pkg_promote_size; + /*BE*/U32 pkg_version_date; + /*BE*/U32 pkg_version_hash; + /*BE*/U32 pkg_0x088; + /*BE*/U32 pkg_0x08C; + /*BE*/U32 pkg_0x090; + /*BE*/U32 pkg_0x094; + /*BE*/U32 pkg_iro_tag; + /*BE*/U32 pkg_drm_type_version; + + U08 pkg_zeroes_1[0x60]; + + /* Digest table */ + U08 digest_entries1[0x20]; // sha256 digest for main entry 1 + U08 digest_entries2[0x20]; // sha256 digest for main entry 2 + U08 digest_table_digest[0x20]; // sha256 digest for digest table + U08 digest_body_digest[0x20]; // sha256 digest for main table + + U08 pkg_zeroes_2[0x280]; + + U32 pkg_0x400; + + U32 pfs_image_count; // count of PFS images + U64 pfs_image_flags; // PFS flags + U64 pfs_image_offset; // offset to start of external PFS image + U64 pfs_image_size; // size of external PFS image + U64 mount_image_offset; + U64 mount_image_size; + U64 pkg_size; + U32 pfs_signed_size; + U32 pfs_cache_size; + U08 pfs_image_digest[0x20]; + U08 pfs_signed_digest[0x20]; + U64 pfs_split_size_nth_0; + U64 pfs_split_size_nth_1; + + U08 pkg_zeroes_3[0xB50]; + + U08 pkg_digest[0x20]; + }; +inline void ReadBE(PKGHeader& s) +{ + ReadBE(s.magic); + ReadBE(s.pkg_table_entry_offset); + ReadBE(s.pkg_table_entry_count); +} + +struct PKGEntry { + U32 id; // File ID, useful for files without a filename entry + U32 filename_offset; // Offset into the filenames table (ID 0x200) where this file's name is located + U32 flags1; // Flags including encrypted flag, etc + U32 flags2; // Flags including encryption key index, etc + U32 offset; // Offset into PKG to find the file + U32 size; // Size of the file + U64 padding; // blank padding +}; + +inline void ReadBE(PKGEntry& s) +{ + ReadBE(s.id); + ReadBE(s.filename_offset); + ReadBE(s.flags1); + ReadBE(s.flags2); + ReadBE(s.offset); + ReadBE(s.size); + ReadBE(s.padding); +} + class PKG { private: U08* pkg; U64 pkgSize = 0; S08 pkgTitleID[9]; + std::string extractPath; public: PKG(); @@ -23,7 +111,31 @@ public: } std::string getTitleID() { - return std::string(pkgTitleID); + return std::string(pkgTitleID,9); + } + bool extract(const std::string& filepath, const std::string& extractPath, std::string& failreason); + + void* mmap(size_t sLength, std::FILE* nFd) { + HANDLE hHandle; + void* pStart; + hHandle = CreateFileMapping( + (HANDLE)_get_osfhandle(_fileno((nFd))), + NULL, // default security + PAGE_WRITECOPY, // read/write access + 0, // maximum object size (high-order DWORD) + 0, // maximum object size (low-order DWORD) + NULL); // name of mapping object + + if (hHandle != NULL) { + pStart = MapViewOfFile(hHandle, FILE_MAP_COPY, 0, 0, sLength); + } + return pStart; + } + int munmap(void* pStart) { + if (UnmapViewOfFile(pStart) != 0) + return FALSE; + + return TRUE; } }; diff --git a/shadPS4/gui/shadps4gui.cpp b/shadPS4/gui/shadps4gui.cpp index ebfe1e81..546080c0 100644 --- a/shadPS4/gui/shadps4gui.cpp +++ b/shadPS4/gui/shadps4gui.cpp @@ -28,6 +28,23 @@ void shadps4gui::installPKG() { PKG pkg; pkg.open(file); + //if pkg is ok we procced with extraction + std::string failreason; + QString gamedir = QDir::currentPath() + "/game/" + QString::fromStdString(pkg.getTitleID()); + QDir dir(gamedir); + if (!dir.exists()) { + dir.mkpath("."); + } + std::string extractpath = QDir::currentPath().toStdString() + "/game/" + pkg.getTitleID() + "/"; + if (!pkg.extract(file, extractpath, failreason)) + { + QMessageBox::critical(this, "PKG ERROR", QString::fromStdString(failreason), QMessageBox::Ok, 0); + } + else + { + QMessageBox::information(this, "Extraction Finished", "Game successfully installed at " + gamedir, QMessageBox::Ok, 0); + game_list->RefreshGameDirectory();//force refreshing since filelistwatcher doesn't work properly + } } else diff --git a/shadPS4/shadPS4.vcxproj b/shadPS4/shadPS4.vcxproj index 78e86485..d0b92e67 100644 --- a/shadPS4/shadPS4.vcxproj +++ b/shadPS4/shadPS4.vcxproj @@ -56,12 +56,12 @@ - qt6.4.0 + 6.4.0 core;gui;widgets debug - qt6.4.0 + 6.4.0 core;gui;widgets release