This commit is contained in:
Elad 2025-12-15 14:34:45 -03:00 committed by GitHub
commit e9474178c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 164 additions and 30 deletions

View File

@ -9,6 +9,7 @@
#include "Emu/system_utils.hpp" #include "Emu/system_utils.hpp"
#include "Emu/VFS.h" #include "Emu/VFS.h"
#include "unpkg.h" #include "unpkg.h"
#include "unself.h"
#include "util/sysinfo.hpp" #include "util/sysinfo.hpp"
#include "Loader/PSF.h" #include "Loader/PSF.h"
@ -47,12 +48,23 @@ package_reader::package_reader(const std::string& path, fs::file file)
return; return;
} }
const bool param_sfo_found = read_param_sfo(); m_psf = psf::load_object(read_file("PARAM.SFO"), path + ":PARAM.SFO");
if (!param_sfo_found) if (m_psf.empty())
{ {
pkg_log.notice("PKG does not contain a PARAM.SFO"); pkg_log.notice("PKG does not contain a PARAM.SFO");
} }
m_rap_file_path = get_rap_file_path_of_self(read_file("USRDIR/EBOOT.BIN"));
if (m_rap_file_path.empty())
{
pkg_log.notice("PKG needs a RAP file: %s", m_rap_file_path);
}
else
{
pkg_log.notice("PKG does not need a license file.");
}
} }
package_reader::~package_reader() package_reader::~package_reader()
@ -577,13 +589,14 @@ bool package_reader::read_entries(std::vector<PKGEntry>& entries)
return true; return true;
} }
bool package_reader::read_param_sfo() fs::file package_reader::read_file(std::string_view relative_path)
{ {
std::vector<PKGEntry> entries; std::vector<PKGEntry> entries;
fs::file tmp;
if (!read_entries(entries)) if (!read_entries(entries))
{ {
return false; return tmp;
} }
std::vector<u8> data_buf; std::vector<u8> data_buf;
@ -609,13 +622,13 @@ bool package_reader::read_param_sfo()
std::string_view name = fmt::trim_back_sv(name_buf, "\0"sv); std::string_view name = fmt::trim_back_sv(name_buf, "\0"sv);
// We're looking for the PARAM.SFO file, if there is any // We're looking for the PARAM.SFO file, if there is any
if (usz ndelim = name.find_first_not_of('/'); ndelim == umax || name.substr(ndelim) != "PARAM.SFO") if (usz ndelim = name.find_first_not_of('/'); ndelim == umax || name.substr(ndelim) != relative_path)
{ {
continue; continue;
} }
// Read the package's PARAM.SFO // Read the package's PARAM.SFO
fs::file tmp = fs::make_stream<std::vector<uchar>>(); tmp = fs::make_stream<std::vector<uchar>>();
{ {
for (u64 pos = 0; pos < entry.file_size; pos += BUF_SIZE) for (u64 pos = 0; pos < entry.file_size; pos += BUF_SIZE)
{ {
@ -625,32 +638,21 @@ bool package_reader::read_param_sfo()
if (decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), data_buf.data()) != block_size) if (decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data(), data_buf.data()) != block_size)
{ {
pkg_log.error("Failed to decrypt PARAM.SFO file"); pkg_log.error("Failed to decrypt %s file", relative_path);
return false; tmp.close();
return tmp;
} }
if (tmp.write(data_buf.data(), block_size) != block_size) ensure(tmp.write(data_buf.data(), block_size) == block_size);
{
pkg_log.error("Failed to write to temporary PARAM.SFO file");
return false;
}
} }
tmp.seek(0); tmp.seek(0);
return tmp;
m_psf = psf::load_object(tmp, name);
if (m_psf.empty())
{
// Invalid
continue;
}
return true;
} }
} }
return false; tmp.close();
return tmp;
} }
// TODO: maybe also check if VERSION matches // TODO: maybe also check if VERSION matches

View File

@ -362,10 +362,15 @@ public:
return m_file; return m_file;
} }
const std::string& gep_needed_rap_file_path() const
{
return m_rap_file_path;
}
private: private:
bool read_header(); bool read_header();
bool read_metadata(); bool read_metadata();
bool read_param_sfo(); fs::file read_file(std::string_view relative_path);
bool set_decryption_key(); bool set_decryption_key();
bool read_entries(std::vector<PKGEntry>& entries); bool read_entries(std::vector<PKGEntry>& entries);
void archive_seek(s64 new_offset, const fs::seek_mode damode = fs::seek_set); void archive_seek(s64 new_offset, const fs::seek_mode damode = fs::seek_set);
@ -401,4 +406,5 @@ private:
// Expose bootable file installed (if installed such) // Expose bootable file installed (if installed such)
std::string m_bootable_file_path; std::string m_bootable_file_path;
std::string m_rap_file_path;
}; };

View File

@ -1068,6 +1068,37 @@ bool SELFDecrypter::DecryptNPDRM(u8 *metadata, u32 metadata_size)
return true; return true;
} }
std::string SELFDecrypter::GetRapFilePath()
{
// Check if we have a valid NPDRM control info structure.
// If not, the data has no NPDRM layer.
const NPD_HEADER* npd = GetNPDHeader();
if (!npd)
{
self_log.trace("No NPDRM control info found!");
return {};
}
if (npd->license == 1) // Network license.
{
return rpcs3::utils::get_rap_file_path(npd->content_id);
}
else if (npd->license == 2) // Local license.
{
return rpcs3::utils::get_rap_file_path(npd->content_id);
}
else if (npd->license == 3) // Free license.
{
//
}
else
{
self_log.error("Invalid NPDRM license type!");
}
return {};
}
const NPD_HEADER* SELFDecrypter::GetNPDHeader() const const NPD_HEADER* SELFDecrypter::GetNPDHeader() const
{ {
// Parse the control info structures to find the NPDRM control info. // Parse the control info structures to find the NPDRM control info.
@ -1435,6 +1466,46 @@ fs::file decrypt_self(const fs::file& elf_or_self, const u8* klic_key, SelfAddit
return {}; return {};
} }
std::string get_rap_file_path_of_self(const fs::file& elf_or_self)
{
if (!elf_or_self)
{
return {};
}
elf_or_self.seek(0);
// Check SELF header first. Check for a debug SELF.
u32 file_type = umax;
elf_or_self.read_at(0, &file_type, sizeof(file_type));
if (file_type == "SCE\0"_u32)
{
if (fs::file res = CheckDebugSelf(elf_or_self))
{
return {};
}
// Check the ELF file class (32 or 64 bit).
const bool isElf32 = IsSelfElf32(elf_or_self);
// Start the decrypter on this SELF file.
SELFDecrypter self_dec(elf_or_self);
// Load the SELF file headers.
if (!self_dec.LoadHeaders(isElf32, nullptr))
{
self_log.error("Failed to load SELF file headers!");
return {};
}
// Load and decrypt the SELF file metadata.
return self_dec.GetRapFilePath();
}
return {};
}
bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key, NPD_HEADER* npd_out) bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key, NPD_HEADER* npd_out)
{ {
if (!self) if (!self)

View File

@ -481,6 +481,7 @@ public:
bool DecryptNPDRM(u8 *metadata, u32 metadata_size); bool DecryptNPDRM(u8 *metadata, u32 metadata_size);
const NPD_HEADER* GetNPDHeader() const; const NPD_HEADER* GetNPDHeader() const;
static bool GetKeyFromRap(const char *content_id, u8 *npdrm_key); static bool GetKeyFromRap(const char *content_id, u8 *npdrm_key);
std::string GetRapFilePath();
private: private:
template<typename EHdr, typename SHdr, typename PHdr> template<typename EHdr, typename SHdr, typename PHdr>
@ -562,5 +563,6 @@ private:
fs::file decrypt_self(const fs::file& elf_or_self, const u8* klic_key = nullptr, SelfAdditionalInfo* additional_info = nullptr); fs::file decrypt_self(const fs::file& elf_or_self, const u8* klic_key = nullptr, SelfAdditionalInfo* additional_info = nullptr);
bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key = nullptr, NPD_HEADER* npd_out = nullptr); bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key = nullptr, NPD_HEADER* npd_out = nullptr);
bool get_npdrm_self_header(const fs::file& self, NPD_HEADER& npd); bool get_npdrm_self_header(const fs::file& self, NPD_HEADER& npd);
std::string get_rap_file_path_of_self(const fs::file& elf_or_self);
u128 get_default_self_klic(); u128 get_default_self_klic();

View File

@ -909,7 +909,7 @@ bool main_window::InstallPackages(QStringList file_paths, bool from_boot)
// Install rap files if available // Install rap files if available
int installed_rap_and_edat_count = 0; int installed_rap_and_edat_count = 0;
const auto install_filetype = [&installed_rap_and_edat_count, &file_paths](const std::string extension) const auto install_filetype = [this, &installed_rap_and_edat_count, &file_paths](const std::string extension)
{ {
const QString pattern = QString(".*\\.%1").arg(QString::fromStdString(extension)); const QString pattern = QString(".*\\.%1").arg(QString::fromStdString(extension));
for (const QString& file : file_paths.filter(QRegularExpression(pattern, QRegularExpression::PatternOption::CaseInsensitiveOption))) for (const QString& file : file_paths.filter(QRegularExpression(pattern, QRegularExpression::PatternOption::CaseInsensitiveOption)))
@ -919,6 +919,13 @@ bool main_window::InstallPackages(QStringList file_paths, bool from_boot)
if (InstallFileInExData(extension, file, filename)) if (InstallFileInExData(extension, file, filename))
{ {
if (filename == last_rap_file_needed_for_pkg)
{
file_paths.append(last_pkg_path_postponed_due_to_missing_rap);
last_rap_file_needed_for_pkg = {};
last_pkg_path_postponed_due_to_missing_rap = {};
}
gui_log.success("Successfully copied %s file: %s", extension, filename); gui_log.success("Successfully copied %s file: %s", extension, filename);
installed_rap_and_edat_count++; installed_rap_and_edat_count++;
} }
@ -1022,10 +1029,6 @@ bool main_window::HandlePackageInstallation(QStringList file_paths, bool from_bo
} }
gui_log.notice("About to install packages:\n%s", fmt::merge(path_vec, "\n")); gui_log.notice("About to install packages:\n%s", fmt::merge(path_vec, "\n"));
progress_dialog pdlg(tr("RPCS3 Package Installer"), tr("Installing package, please wait..."), tr("Cancel"), 0, 1000, false, this);
pdlg.setAutoClose(false);
pdlg.show();
package_install_result result = {}; package_install_result result = {};
auto get_app_info = [](compat::package_info& package) auto get_app_info = [](compat::package_info& package)
@ -1064,6 +1067,53 @@ bool main_window::HandlePackageInstallation(QStringList file_paths, bool from_bo
readers.emplace_back(info.path.toStdString()); readers.emplace_back(info.path.toStdString());
} }
std::string missing_licenses;
std::string missing_licenses_short;
for (const auto& reader : readers)
{
if (std::string filepath = reader.gep_needed_rap_file_path(); !filepath.empty() && fs::is_file(filepath))
{
missing_licenses += filepath;
missing_licenses += "\n";
if (std::count(missing_licenses_short.begin(), missing_licenses_short.end(), '\n') < 5)
{
missing_licenses_short += std::string_view(filepath).substr(filepath.find_last_of(fs::delim) + 1);
missing_licenses_short += "\n";
}
}
}
if (!missing_licenses.empty())
{
gui_log.fatal("Failed to locate the game license file(s):\n%s"
"\nEnsure the .rap license file is placed in the dev_hdd0/home/%s/exdata folder with a lowercase file extension."
"\nIf you need assistance on dumping the license file from your PS3, read our quickstart guide: https://rpcs3.net/quickstart", missing_licenses, Emu.GetUsr());
QString error = tr("Failed to locate the game license file(s):\n\n%1\nEnsure the .rap license file(s) are placed in the dev_hdd0 folder with a lowercase file extension.").arg(QString::fromStdString(missing_licenses_short));
QMessageBox* mb = new QMessageBox(QMessageBox::Warning, tr("PKG Installer: Missing License(s) For PKG(s)"), error, QMessageBox::Ok, this, Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::WindowStaysOnTopHint);
mb->setAttribute(Qt::WA_DeleteOnClose);
mb->open();
if (readers.size() == 1)
{
last_pkg_path_postponed_due_to_missing_rap = packages[0].path;
missing_licenses_short.pop_back(); // Remove newline
last_rap_file_needed_for_pkg = QString::fromStdString(missing_licenses_short);
}
return false;
}
last_pkg_path_postponed_due_to_missing_rap = {};
last_rap_file_needed_for_pkg = {};
progress_dialog pdlg(tr("RPCS3 Package Installer"), tr("Installing package, please wait..."), tr("Cancel"), 0, 1000, false, this);
pdlg.setAutoClose(false);
pdlg.show();
std::deque<std::string> bootable_paths; std::deque<std::string> bootable_paths;
// Run PKG unpacking asynchronously // Run PKG unpacking asynchronously

View File

@ -51,6 +51,9 @@ class main_window : public QMainWindow
int m_other_slider_pos = 0; int m_other_slider_pos = 0;
std::function<void()> m_notify_batch_game_action_cb; std::function<void()> m_notify_batch_game_action_cb;
QString last_pkg_path_postponed_due_to_missing_rap;
QString last_rap_file_needed_for_pkg;
QIcon m_app_icon; QIcon m_app_icon;
QIcon m_icon_play; QIcon m_icon_play;
QIcon m_icon_pause; QIcon m_icon_pause;