mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-21 03:55:32 +00:00
Read patchsets from compat db
This commit is contained in:
parent
f14d47bfe6
commit
68d411918d
6 changed files with 343 additions and 134 deletions
|
@ -30,6 +30,18 @@ package_reader::package_reader(const std::string& path)
|
|||
}
|
||||
|
||||
m_is_valid = read_metadata();
|
||||
|
||||
if (!m_is_valid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool param_sfo_found = read_param_sfo();
|
||||
|
||||
if (!param_sfo_found)
|
||||
{
|
||||
pkg_log.notice("PKG does not contain a PARAM.SFO");
|
||||
}
|
||||
}
|
||||
|
||||
package_reader::~package_reader()
|
||||
|
@ -476,12 +488,11 @@ bool package_reader::decrypt_data()
|
|||
return true;
|
||||
}
|
||||
|
||||
// TODO: maybe also check if VERSION matches
|
||||
package_error package_reader::check_target_app_version()
|
||||
bool package_reader::read_param_sfo()
|
||||
{
|
||||
if (!decrypt_data())
|
||||
{
|
||||
return package_error::other;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<PKGEntry> entries(header.file_count);
|
||||
|
@ -500,7 +511,7 @@ package_error package_reader::check_target_app_version()
|
|||
|
||||
decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : dec_key.data());
|
||||
|
||||
const std::string name{ reinterpret_cast<char*>(buf.get()), entry.name_size };
|
||||
const std::string name{reinterpret_cast<char*>(buf.get()), entry.name_size};
|
||||
|
||||
// We're looking for the PARAM.SFO file, if there is any
|
||||
if (name != "PARAM.SFO")
|
||||
|
@ -518,130 +529,143 @@ package_error package_reader::check_target_app_version()
|
|||
if (decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : dec_key.data()) != block_size)
|
||||
{
|
||||
pkg_log.error("Failed to decrypt PARAM.SFO file");
|
||||
return package_error::other;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tmp.write(buf.get(), block_size) != block_size)
|
||||
{
|
||||
pkg_log.error("Failed to write to temporary PARAM.SFO file");
|
||||
return package_error::other;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
tmp.seek(0);
|
||||
|
||||
const auto psf = psf::load_object(tmp);
|
||||
m_psf = psf::load_object(tmp);
|
||||
|
||||
const auto category = psf::get_string(psf, "CATEGORY", "");
|
||||
const auto title_id = psf::get_string(psf, "TITLE_ID", "");
|
||||
const auto app_ver = psf::get_string(psf, "APP_VER", "");
|
||||
const auto target_app_ver = psf::get_string(psf, "TARGET_APP_VER", "");
|
||||
|
||||
if (category != "GD")
|
||||
{
|
||||
// We allow anything that isn't an update for now
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
if (title_id.empty())
|
||||
{
|
||||
// Let's allow packages without ID for now
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
if (app_ver.empty())
|
||||
{
|
||||
if (!target_app_ver.empty())
|
||||
{
|
||||
// Let's see if this case exists
|
||||
pkg_log.fatal("Trying to install an unversioned patch with a target app version (%s). Please contact a developer!", target_app_ver);
|
||||
}
|
||||
|
||||
// This is probably not a version dependant patch, so we may install the package
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
const fs::file installed_sfo_file(Emu.GetHddDir() + "game/" + std::string(title_id) + "/PARAM.SFO");
|
||||
if (!installed_sfo_file)
|
||||
{
|
||||
if (!target_app_ver.empty())
|
||||
{
|
||||
// We are unable to compare anything with the target app version
|
||||
pkg_log.error("A target app version is required (%s), but no PARAM.SFO was found for %s", target_app_ver, title_id);
|
||||
return package_error::app_version;
|
||||
}
|
||||
|
||||
// There is nothing we need to compare, so we may install the package
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
const auto installed_psf = psf::load_object(installed_sfo_file);
|
||||
|
||||
const auto installed_title_id = psf::get_string(installed_psf, "TITLE_ID", "");
|
||||
const auto installed_app_ver = psf::get_string(installed_psf, "APP_VER", "");
|
||||
|
||||
if (title_id != installed_title_id || installed_app_ver.empty())
|
||||
{
|
||||
// Let's allow this package for now
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
std::add_pointer_t<char> ev0, ev1;
|
||||
const double old_version = std::strtod(installed_app_ver.data(), &ev0);
|
||||
|
||||
if (installed_app_ver.data() + installed_app_ver.size() != ev0)
|
||||
{
|
||||
pkg_log.error("Failed to convert the installed app version to double (%s)", installed_app_ver);
|
||||
return package_error::other;
|
||||
}
|
||||
|
||||
if (target_app_ver.empty())
|
||||
{
|
||||
// This is most likely the first patch. Let's make sure its version is high enough for the installed game.
|
||||
|
||||
const double new_version = std::strtod(app_ver.data(), &ev1);
|
||||
|
||||
if (app_ver.data() + app_ver.size() != ev1)
|
||||
{
|
||||
pkg_log.error("Failed to convert the package's app version to double (%s)", app_ver);
|
||||
return package_error::other;
|
||||
}
|
||||
|
||||
if (new_version >= old_version)
|
||||
{
|
||||
// Yay! The patch has a higher or equal version than the installed game.
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
pkg_log.error("The new app version (%s) is smaller than the installed app version (%s)", app_ver, installed_app_ver);
|
||||
return package_error::app_version;
|
||||
}
|
||||
|
||||
// Check if the installed app version matches the target app version
|
||||
|
||||
const double target_version = std::strtod(target_app_ver.data(), &ev1);
|
||||
|
||||
if (target_app_ver.data() + target_app_ver.size() != ev1)
|
||||
{
|
||||
pkg_log.error("Failed to convert the package's target app version to double (%s)", target_app_ver);
|
||||
return package_error::other;
|
||||
}
|
||||
|
||||
if (target_version == old_version)
|
||||
{
|
||||
// Yay! This patch is for the installed game version.
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
pkg_log.error("The installed app version (%s) does not match the target app version (%s)", installed_app_ver, target_app_ver);
|
||||
return package_error::app_version;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pkg_log.error("Failed to create temporary PARAM.SFO file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pkg_log.error("Failed to create temporary PARAM.SFO file");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: maybe also check if VERSION matches
|
||||
package_error package_reader::check_target_app_version()
|
||||
{
|
||||
if (!m_is_valid)
|
||||
{
|
||||
return package_error::other;
|
||||
}
|
||||
|
||||
return package_error::no_error;
|
||||
const auto category = psf::get_string(m_psf, "CATEGORY", "");
|
||||
const auto title_id = psf::get_string(m_psf, "TITLE_ID", "");
|
||||
const auto app_ver = psf::get_string(m_psf, "APP_VER", "");
|
||||
const auto target_app_ver = psf::get_string(m_psf, "TARGET_APP_VER", "");
|
||||
|
||||
if (category != "GD")
|
||||
{
|
||||
// We allow anything that isn't an update for now
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
if (title_id.empty())
|
||||
{
|
||||
// Let's allow packages without ID for now
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
if (app_ver.empty())
|
||||
{
|
||||
if (!target_app_ver.empty())
|
||||
{
|
||||
// Let's see if this case exists
|
||||
pkg_log.fatal("Trying to install an unversioned patch with a target app version (%s). Please contact a developer!", target_app_ver);
|
||||
}
|
||||
|
||||
// This is probably not a version dependant patch, so we may install the package
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
const fs::file installed_sfo_file(Emu.GetHddDir() + "game/" + std::string(title_id) + "/PARAM.SFO");
|
||||
if (!installed_sfo_file)
|
||||
{
|
||||
if (!target_app_ver.empty())
|
||||
{
|
||||
// We are unable to compare anything with the target app version
|
||||
pkg_log.error("A target app version is required (%s), but no PARAM.SFO was found for %s", target_app_ver, title_id);
|
||||
return package_error::app_version;
|
||||
}
|
||||
|
||||
// There is nothing we need to compare, so we may install the package
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
const auto installed_psf = psf::load_object(installed_sfo_file);
|
||||
|
||||
const auto installed_title_id = psf::get_string(installed_psf, "TITLE_ID", "");
|
||||
const auto installed_app_ver = psf::get_string(installed_psf, "APP_VER", "");
|
||||
|
||||
if (title_id != installed_title_id || installed_app_ver.empty())
|
||||
{
|
||||
// Let's allow this package for now
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
std::add_pointer_t<char> ev0, ev1;
|
||||
const double old_version = std::strtod(installed_app_ver.data(), &ev0);
|
||||
|
||||
if (installed_app_ver.data() + installed_app_ver.size() != ev0)
|
||||
{
|
||||
pkg_log.error("Failed to convert the installed app version to double (%s)", installed_app_ver);
|
||||
return package_error::other;
|
||||
}
|
||||
|
||||
if (target_app_ver.empty())
|
||||
{
|
||||
// This is most likely the first patch. Let's make sure its version is high enough for the installed game.
|
||||
|
||||
const double new_version = std::strtod(app_ver.data(), &ev1);
|
||||
|
||||
if (app_ver.data() + app_ver.size() != ev1)
|
||||
{
|
||||
pkg_log.error("Failed to convert the package's app version to double (%s)", app_ver);
|
||||
return package_error::other;
|
||||
}
|
||||
|
||||
if (new_version >= old_version)
|
||||
{
|
||||
// Yay! The patch has a higher or equal version than the installed game.
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
pkg_log.error("The new app version (%s) is smaller than the installed app version (%s)", app_ver, installed_app_ver);
|
||||
return package_error::app_version;
|
||||
}
|
||||
|
||||
// Check if the installed app version matches the target app version
|
||||
|
||||
const double target_version = std::strtod(target_app_ver.data(), &ev1);
|
||||
|
||||
if (target_app_ver.data() + target_app_ver.size() != ev1)
|
||||
{
|
||||
pkg_log.error("Failed to convert the package's target app version to double (%s)", target_app_ver);
|
||||
return package_error::other;
|
||||
}
|
||||
|
||||
if (target_version == old_version)
|
||||
{
|
||||
// Yay! This patch is for the installed game version.
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
pkg_log.error("The installed app version (%s) does not match the target app version (%s)", installed_app_ver, target_app_ver);
|
||||
return package_error::app_version;
|
||||
}
|
||||
|
||||
bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "Loader/PSF.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
// Constants
|
||||
enum
|
||||
{
|
||||
PKG_HEADER_SIZE = 0xC0, //sizeof(pkg_header) + sizeof(pkg_unk_checksum)
|
||||
PKG_HEADER_SIZE = 0xC0, // sizeof(pkg_header) + sizeof(pkg_unk_checksum)
|
||||
PKG_HEADER_SIZE2 = 0x280,
|
||||
};
|
||||
|
||||
|
@ -303,10 +304,12 @@ public:
|
|||
|
||||
package_error check_target_app_version();
|
||||
bool extract_data(atomic_t<double>& sync);
|
||||
psf::registry get_psf() const { return m_psf; }
|
||||
|
||||
private:
|
||||
bool read_header();
|
||||
bool read_metadata();
|
||||
bool read_param_sfo();
|
||||
bool decrypt_data();
|
||||
void archive_seek(const s64 new_offset, const fs::seek_mode damode = fs::seek_set);
|
||||
u64 archive_read(void* data_ptr, const u64 num_bytes);
|
||||
|
@ -327,4 +330,5 @@ private:
|
|||
|
||||
PKGHeader header{};
|
||||
PKGMetaData metadata{};
|
||||
psf::registry m_psf;
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <QApplication>
|
||||
#include <QMessageBox>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
|
||||
LOG_CHANNEL(compat_log, "Compat");
|
||||
|
@ -112,7 +113,7 @@ bool game_compatibility::ReadJSON(const QJsonObject& json_data, bool after_downl
|
|||
QJsonObject json_result = json_results[key].toObject();
|
||||
|
||||
// Retrieve compatibility information from json
|
||||
compat_status status = Status_Data.at(json_result.value("status").toString("NoResult"));
|
||||
compat::status status = Status_Data.at(json_result.value("status").toString("NoResult"));
|
||||
|
||||
// Add date if possible
|
||||
status.date = json_result.value("date").toString();
|
||||
|
@ -120,8 +121,63 @@ bool game_compatibility::ReadJSON(const QJsonObject& json_data, bool after_downl
|
|||
// Add latest version if possible
|
||||
status.latest_version = json_result.value("update").toString();
|
||||
|
||||
// Add patchsets if possible
|
||||
if (const QJsonValue patchsets_value = json_result.value("patchsets"); patchsets_value.isArray())
|
||||
{
|
||||
for (const QJsonValue& patch_set : patchsets_value.toArray())
|
||||
{
|
||||
compat::pkg_patchset set;
|
||||
set.tag_id = patch_set["tag_id"].toString().toStdString();
|
||||
set.popup = patch_set["popup"].toBool();
|
||||
set.signoff = patch_set["signoff"].toBool();
|
||||
set.popup_delay = patch_set["popup_delay"].toInt();
|
||||
set.min_system_ver = patch_set["min_system_ver"].toString().toStdString();
|
||||
|
||||
if (const QJsonValue packages_value = patch_set["packages"]; packages_value.isArray())
|
||||
{
|
||||
for (const QJsonValue& package : packages_value.toArray())
|
||||
{
|
||||
compat::pkg_package pkg;
|
||||
pkg.version = package["version"].toString().toStdString();
|
||||
pkg.size = package["size"].toInt();
|
||||
pkg.sha1sum = package["sha1sum"].toString().toStdString();
|
||||
pkg.ps3_system_ver = package["ps3_system_ver"].toString().toStdString();
|
||||
pkg.drm_type = package["drm_type"].toString().toStdString();
|
||||
|
||||
if (const QJsonValue changelogs_value = package["changelogs"]; changelogs_value.isArray())
|
||||
{
|
||||
for (const QJsonValue& changelog : changelogs_value.toArray())
|
||||
{
|
||||
compat::pkg_changelog chl;
|
||||
chl.type = changelog["type"].toString().toStdString();
|
||||
chl.content = changelog["content"].toString().toStdString();
|
||||
|
||||
pkg.changelogs.push_back(std::move(chl));
|
||||
}
|
||||
}
|
||||
|
||||
if (const QJsonValue titles_value = package["titles"]; titles_value.isArray())
|
||||
{
|
||||
for (const QJsonValue& title : titles_value.toArray())
|
||||
{
|
||||
compat::pkg_title ttl;
|
||||
ttl.type = title["type"].toString().toStdString();
|
||||
ttl.title = title["title"].toString().toStdString();
|
||||
|
||||
pkg.titles.push_back(std::move(ttl));
|
||||
}
|
||||
}
|
||||
|
||||
set.packages.push_back(std::move(pkg));
|
||||
}
|
||||
}
|
||||
|
||||
status.patch_sets.push_back(std::move(set));
|
||||
}
|
||||
}
|
||||
|
||||
// Add status to map
|
||||
m_compat_database.emplace(std::pair<std::string, compat_status>(sstr(key), status));
|
||||
m_compat_database.emplace(std::pair<std::string, compat::status>(sstr(key), status));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -146,7 +202,7 @@ void game_compatibility::RequestCompatibility(bool online)
|
|||
return;
|
||||
}
|
||||
|
||||
QByteArray data = file.readAll();
|
||||
const QByteArray data = file.readAll();
|
||||
file.close();
|
||||
|
||||
compat_log.notice("Finished reading database from file: %s", sstr(m_filepath));
|
||||
|
@ -166,7 +222,7 @@ void game_compatibility::RequestCompatibility(bool online)
|
|||
Q_EMIT DownloadStarted();
|
||||
}
|
||||
|
||||
compat_status game_compatibility::GetCompatibility(const std::string& title_id)
|
||||
compat::status game_compatibility::GetCompatibility(const std::string& title_id)
|
||||
{
|
||||
if (m_compat_database.empty())
|
||||
{
|
||||
|
@ -180,7 +236,7 @@ compat_status game_compatibility::GetCompatibility(const std::string& title_id)
|
|||
return Status_Data.at("NoResult");
|
||||
}
|
||||
|
||||
compat_status game_compatibility::GetStatusData(const QString& status)
|
||||
compat::status game_compatibility::GetStatusData(const QString& status)
|
||||
{
|
||||
return Status_Data.at(status);
|
||||
}
|
||||
|
|
|
@ -8,22 +8,89 @@
|
|||
class downloader;
|
||||
class gui_settings;
|
||||
|
||||
struct compat_status
|
||||
namespace compat
|
||||
{
|
||||
int index;
|
||||
QString date;
|
||||
QString color;
|
||||
QString text;
|
||||
QString tooltip;
|
||||
QString latest_version;
|
||||
};
|
||||
struct pkg_title
|
||||
{
|
||||
std::string type; // TITLE or TITLE_08 etc. (system languages)
|
||||
std::string title; // The Last of Arse
|
||||
};
|
||||
|
||||
struct pkg_changelog
|
||||
{
|
||||
std::string type; // paramhip or paramhip_08 etc. (system languages)
|
||||
std::string content; // "This system software update improves system performance."
|
||||
};
|
||||
|
||||
struct pkg_package
|
||||
{
|
||||
std::string version; // 01.04
|
||||
int size = 0;
|
||||
std::string sha1sum; // a5c83b88394ea3ae99974caedd38690981a80f3e
|
||||
std::string ps3_system_ver; // 04.4000
|
||||
std::string drm_type; // local or mbind etc.
|
||||
std::vector<pkg_changelog> changelogs;
|
||||
std::vector<pkg_title> titles;
|
||||
|
||||
std::string get_changelog(const std::string& type) const
|
||||
{
|
||||
if (auto it = std::find_if(changelogs.begin(), changelogs.end(), [type](const pkg_changelog& cl) { return cl.type == type; });
|
||||
it != changelogs.end())
|
||||
{
|
||||
return it->content;
|
||||
}
|
||||
if (auto it = std::find_if(changelogs.begin(), changelogs.end(), [](const pkg_changelog& cl) { return cl.type == "paramhip"; });
|
||||
it != changelogs.end())
|
||||
{
|
||||
return it->content;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string get_title(const std::string& type) const
|
||||
{
|
||||
if (auto it = std::find_if(titles.begin(), titles.end(), [type](const pkg_title& t) { return t.type == type; });
|
||||
it != titles.end())
|
||||
{
|
||||
return it->title;
|
||||
}
|
||||
if (auto it = std::find_if(titles.begin(), titles.end(), [](const pkg_title& t) { return t.type == "TITLE"; });
|
||||
it != titles.end())
|
||||
{
|
||||
return it->title;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
struct pkg_patchset
|
||||
{
|
||||
std::string tag_id; // BLES01269_T7
|
||||
bool popup = false;
|
||||
bool signoff = false;
|
||||
int popup_delay = 0;
|
||||
std::string min_system_ver; // 03.60
|
||||
std::vector<pkg_package> packages;
|
||||
};
|
||||
|
||||
struct status
|
||||
{
|
||||
int index;
|
||||
QString date;
|
||||
QString color;
|
||||
QString text;
|
||||
QString tooltip;
|
||||
QString latest_version;
|
||||
std::vector<pkg_patchset> patch_sets;
|
||||
};
|
||||
}
|
||||
|
||||
class game_compatibility : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
const std::map<QString, compat_status> Status_Data =
|
||||
const std::map<QString, compat::status> Status_Data =
|
||||
{
|
||||
{ "Playable", { 0, "", "#1ebc61", tr("Playable"), tr("Games that can be properly played from start to finish") } },
|
||||
{ "Ingame", { 1, "", "#f9b32f", tr("Ingame"), tr("Games that either can't be finished, have serious glitches or have insufficient performance") } },
|
||||
|
@ -37,7 +104,7 @@ private:
|
|||
std::shared_ptr<gui_settings> m_gui_settings;
|
||||
QString m_filepath;
|
||||
downloader* m_downloader = nullptr;
|
||||
std::map<std::string, compat_status> m_compat_database;
|
||||
std::map<std::string, compat::status> m_compat_database;
|
||||
|
||||
/** Creates new map from the database */
|
||||
bool ReadJSON(const QJsonObject& json_data, bool after_download);
|
||||
|
@ -50,10 +117,10 @@ public:
|
|||
void RequestCompatibility(bool online = false);
|
||||
|
||||
/** Returns the compatibility status for the requested title */
|
||||
compat_status GetCompatibility(const std::string& title_id);
|
||||
compat::status GetCompatibility(const std::string& title_id);
|
||||
|
||||
/** Returns the data for the requested status */
|
||||
compat_status GetStatusData(const QString& status);
|
||||
compat::status GetStatusData(const QString& status);
|
||||
|
||||
Q_SIGNALS:
|
||||
void DownloadStarted();
|
||||
|
|
|
@ -25,7 +25,7 @@ struct gui_game_info
|
|||
{
|
||||
GameInfo info;
|
||||
QString localized_category;
|
||||
compat_status compat;
|
||||
compat::status compat;
|
||||
QPixmap icon;
|
||||
QPixmap pxmap;
|
||||
bool hasCustomConfig;
|
||||
|
@ -40,7 +40,7 @@ class game_list_frame : public custom_dock_widget
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit game_list_frame(std::shared_ptr<gui_settings> gui_settings, std::shared_ptr<emu_settings> emu_settings, std::shared_ptr<persistent_settings> persistent_settings, QWidget *parent = nullptr);
|
||||
explicit game_list_frame(std::shared_ptr<gui_settings> gui_settings, std::shared_ptr<emu_settings> emu_settings, std::shared_ptr<persistent_settings> persistent_settings, QWidget* parent = nullptr);
|
||||
~game_list_frame();
|
||||
|
||||
/** Fix columns with width smaller than the minimal section size */
|
||||
|
@ -69,6 +69,8 @@ public:
|
|||
|
||||
void SetShowHidden(bool show);
|
||||
|
||||
game_compatibility* GetGameCompatibility() const { return m_game_compat; };
|
||||
|
||||
QList<game_info> GetGameInfo() const;
|
||||
|
||||
// Returns the visible version string in the game list
|
||||
|
|
|
@ -511,7 +511,63 @@ void main_window::InstallPackages(QStringList file_paths)
|
|||
else if (file_paths.count() == 1)
|
||||
{
|
||||
// This can currently only happen by drag and drop.
|
||||
if (QMessageBox::question(this, tr("PKG Decrypter / Installer"), tr("Install package: %1?").arg(file_paths.front()),
|
||||
const QString file_path = file_paths.front();
|
||||
package_reader reader(file_path.toStdString());
|
||||
psf::registry psf = reader.get_psf();
|
||||
const std::string title_id(psf::get_string(psf, "TITLE_ID"));
|
||||
|
||||
// TODO: localization of title and changelog
|
||||
std::string title_key = "TITLE";
|
||||
std::string changelog_key = "paramhip";
|
||||
//std::string cat(psf::get_string(psf, "CATEGORY"));
|
||||
QString version = qstr(std::string(psf::get_string(psf, "APP_VER")));
|
||||
QString title = qstr(std::string(psf::get_string(psf, title_key))); // Let's read this from the psf first
|
||||
QString changelog;
|
||||
|
||||
if (game_compatibility* compat = m_game_list_frame ? m_game_list_frame->GetGameCompatibility() : nullptr)
|
||||
{
|
||||
compat::status info = compat->GetCompatibility(title_id);
|
||||
if (!info.patch_sets.empty())
|
||||
{
|
||||
// We currently only handle the first patch set
|
||||
for (const auto& package : info.patch_sets.front().packages)
|
||||
{
|
||||
if (sstr(version) == package.version)
|
||||
{
|
||||
if (const std::string localized_title = package.get_title(title_key); !localized_title.empty())
|
||||
{
|
||||
title = qstr(localized_title);
|
||||
}
|
||||
|
||||
if (const std::string localized_changelog = package.get_changelog(changelog_key); !localized_changelog.empty())
|
||||
{
|
||||
changelog = qstr(localized_changelog);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!changelog.isEmpty())
|
||||
{
|
||||
changelog = tr("\n\nChangelog:\n%0").arg(changelog);
|
||||
}
|
||||
|
||||
if (!version.isEmpty())
|
||||
{
|
||||
version = tr("\nVersion %0").arg(version);
|
||||
}
|
||||
|
||||
if (title.isEmpty())
|
||||
{
|
||||
QFileInfo file_info(file_path);
|
||||
title = file_info.fileName();
|
||||
}
|
||||
|
||||
if (QMessageBox::question(this, tr("PKG Decrypter / Installer"), tr("Do you want to install this package?\n\n%0%1%2")
|
||||
.arg(title).arg(version).arg(changelog),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) != QMessageBox::Yes)
|
||||
{
|
||||
gui_log.notice("PKG: Cancelled installation from drop. File: %s", sstr(file_paths.front()));
|
||||
|
|
Loading…
Add table
Reference in a new issue