From 46e8b4f56173569eebff2d178422f4cf28fc723b Mon Sep 17 00:00:00 2001 From: Megamouse Date: Sun, 6 Sep 2020 11:47:45 +0200 Subject: [PATCH] patch_manager: add download button --- Utilities/bin_patch.cpp | 23 +++-- Utilities/bin_patch.h | 2 +- rpcs3/rpcs3qt/downloader.cpp | 3 +- rpcs3/rpcs3qt/downloader.h | 3 +- rpcs3/rpcs3qt/game_compatibility.cpp | 2 +- rpcs3/rpcs3qt/patch_manager_dialog.cpp | 121 ++++++++++++++++++++++++- rpcs3/rpcs3qt/patch_manager_dialog.h | 5 + rpcs3/rpcs3qt/patch_manager_dialog.ui | 2 +- rpcs3/rpcs3qt/update_manager.cpp | 2 +- rpcs3/util/yaml.cpp | 6 +- 10 files changed, 147 insertions(+), 22 deletions(-) diff --git a/Utilities/bin_patch.cpp b/Utilities/bin_patch.cpp index dc6c18cbc9..0887a097ae 100644 --- a/Utilities/bin_patch.cpp +++ b/Utilities/bin_patch.cpp @@ -98,19 +98,24 @@ static void append_log_message(std::stringstream* log_messages, const std::strin *log_messages << message << std::endl; }; -bool patch_engine::load(patch_map& patches_map, const std::string& path, bool importing, std::stringstream* log_messages) +bool patch_engine::load(patch_map& patches_map, const std::string& path, std::string content, bool importing, std::stringstream* log_messages) { - // Load patch file - fs::file file{ path }; - - if (!file) + if (content.empty()) { - // Do nothing - return true; + // Load patch file + fs::file file{path}; + + if (!file) + { + // Do nothing + return true; + } + + content = file.to_string(); } // Interpret yaml nodes - auto [root, error] = yaml_load(file.to_string()); + auto [root, error] = yaml_load(content); if (!error.empty() || !root) { @@ -1059,7 +1064,7 @@ bool patch_engine::import_patches(const patch_engine::patch_map& patches, const { patch_engine::patch_map existing_patches; - if (load(existing_patches, path, true, log_messages)) + if (load(existing_patches, path, "", true, log_messages)) { append_patches(existing_patches, patches, count, total, log_messages); return count == 0 || save_patches(existing_patches, path, log_messages); diff --git a/Utilities/bin_patch.h b/Utilities/bin_patch.h index b59c197678..db1784157e 100644 --- a/Utilities/bin_patch.h +++ b/Utilities/bin_patch.h @@ -99,7 +99,7 @@ public: static std::string get_imported_patch_path(); // Load from file and append to specified patches map - static bool load(patch_map& patches, const std::string& path, bool importing = false, std::stringstream* log_messages = nullptr); + static bool load(patch_map& patches, const std::string& path, std::string content = "", bool importing = false, std::stringstream* log_messages = nullptr); // Read and add a patch node to the patch info static bool read_patch_node(patch_info& info, YAML::Node node, const YAML::Node& root, std::stringstream* log_messages = nullptr); diff --git a/rpcs3/rpcs3qt/downloader.cpp b/rpcs3/rpcs3qt/downloader.cpp index b21f2d4204..4886e21924 100644 --- a/rpcs3/rpcs3qt/downloader.cpp +++ b/rpcs3/rpcs3qt/downloader.cpp @@ -15,10 +15,9 @@ size_t curl_write_cb_compat(char* ptr, size_t /*size*/, size_t nmemb, void* user return download->update_buffer(ptr, nmemb); } -downloader::downloader(const std::string& thread_name, QWidget* parent) +downloader::downloader(QWidget* parent) : QObject(parent) , m_parent(parent) - , m_thread_name(thread_name) { m_curl = new curl_handle(this); } diff --git a/rpcs3/rpcs3qt/downloader.h b/rpcs3/rpcs3qt/downloader.h index a0c9a80999..8bd253929a 100644 --- a/rpcs3/rpcs3qt/downloader.h +++ b/rpcs3/rpcs3qt/downloader.h @@ -12,7 +12,7 @@ class downloader : public QObject Q_OBJECT public: - downloader(const std::string& thread_name, QWidget* parent = nullptr); + downloader(QWidget* parent = nullptr); void start(const std::string& url, bool follow_location, bool show_progress_dialog, const QString& progress_dialog_title = "", bool keep_progress_dialog_open = false, int expected_size = -1); size_t update_buffer(char* data, size_t size); @@ -32,7 +32,6 @@ Q_SIGNALS: private: QWidget* m_parent = nullptr; - std::string m_thread_name; curl_handle* m_curl = nullptr; QByteArray m_curl_buf; diff --git a/rpcs3/rpcs3qt/game_compatibility.cpp b/rpcs3/rpcs3qt/game_compatibility.cpp index 288e481d1c..a8386b18b3 100644 --- a/rpcs3/rpcs3qt/game_compatibility.cpp +++ b/rpcs3/rpcs3qt/game_compatibility.cpp @@ -16,7 +16,7 @@ game_compatibility::game_compatibility(std::shared_ptr settings, Q , m_gui_settings(settings) { m_filepath = m_gui_settings->GetSettingsDir() + "/compat_database.dat"; - m_downloader = new downloader("Compat Update", parent); + m_downloader = new downloader(parent); RequestCompatibility(); connect(m_downloader, &downloader::signal_download_error, this, &game_compatibility::handle_download_error); diff --git a/rpcs3/rpcs3qt/patch_manager_dialog.cpp b/rpcs3/rpcs3qt/patch_manager_dialog.cpp index 66c24b9866..d326ae41af 100644 --- a/rpcs3/rpcs3qt/patch_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/patch_manager_dialog.cpp @@ -8,16 +8,19 @@ #include #include #include +#include +#include #include "ui_patch_manager_dialog.h" #include "patch_manager_dialog.h" #include "table_item_delegate.h" #include "gui_settings.h" +#include "downloader.h" #include "qt_utils.h" #include "Utilities/File.h" #include "util/logs.hpp" -LOG_CHANNEL(patch_log); +LOG_CHANNEL(patch_log, "PAT"); enum patch_column : int { @@ -68,6 +71,10 @@ patch_manager_dialog::patch_manager_dialog(std::shared_ptr gui_set ui->cb_enable_legacy_patches->setChecked(m_legacy_patches_enabled); ui->cb_owned_games_only->setChecked(m_show_owned_games_only); + ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)->setText(tr("Download latest patches")); + + m_downloader = new downloader(parent); + // Create connects connect(ui->patch_filter, &QLineEdit::textChanged, this, &patch_manager_dialog::filter_patches); connect(ui->patch_tree, &QTreeWidget::currentItemChanged, this, &patch_manager_dialog::handle_item_selected); @@ -87,6 +94,23 @@ patch_manager_dialog::patch_manager_dialog(std::shared_ptr gui_set { save_config(); } + else if (button == ui->buttonBox->button(QDialogButtonBox::RestoreDefaults)) + { + download_update(); + } + }); + connect(m_downloader, &downloader::signal_download_error, this, [this](const QString& /*error*/) + { + QMessageBox::warning(this, tr("Patch downloader"), tr("An error occurred during the download process.\nCheck the log for more information.")); + }); + connect(m_downloader, &downloader::signal_download_finished, this, [this](const QByteArray& data) + { + const bool result_json = handle_json(data); + + if (!result_json) + { + QMessageBox::warning(this, tr("Patch downloader"), tr("An error occurred during the download process.\nCheck the log for more information.")); + } }); } @@ -724,7 +748,7 @@ void patch_manager_dialog::dropEvent(QDropEvent* event) patch_engine::patch_map patches; std::stringstream log_message; - if (patch_engine::load(patches, path, true, &log_message)) + if (patch_engine::load(patches, path, "", true, &log_message)) { patch_log.success("Successfully validated patch file %s", path); @@ -805,3 +829,96 @@ void patch_manager_dialog::dragLeaveEvent(QDragLeaveEvent* event) { event->accept(); } + +void patch_manager_dialog::download_update() +{ + m_downloader->start("https://rpcs3.net/compatibility?patch&api=v1", true, true, tr("Downloading latest patches")); +} + +bool patch_manager_dialog::handle_json(const QByteArray& data) +{ + const QJsonObject json_data = QJsonDocument::fromJson(data).object(); + const int return_code = json_data["return_code"].toInt(-255); + + if (return_code < 0) + { + std::string error_message; + switch (return_code) + { + case -1: error_message = "Hash not found"; break; + case -2: error_message = "Server Error - Maintenance Mode"; break; + case -255: error_message = "Server Error - Return code not found"; break; + default: error_message = "Server Error - Unknown Error"; break; + } + + if (return_code != -1) + patch_log.error("Patch download error: %s return code: %d", error_message, return_code); + else + patch_log.warning("Patch download error: %s return code: %d", error_message, return_code); + + return false; + } + + const QJsonValue& version_obj = json_data["version"]; + + if (!version_obj.isString()) + { + patch_log.error("JSON doesn't contain version"); + return false; + } + + if (const std::string version = version_obj.toString().toStdString(); + version != patch_engine_version) + { + patch_log.error("JSON contains wrong version: %s (needed: %s)", version, patch_engine_version); + return false; + } + + const QJsonValue& patch = json_data["patch"]; + + if (!patch.isString() || patch.toString().isEmpty()) + { + patch_log.error("JSON doesn't contain patch"); + return false; + } + + patch_engine::patch_map patches; + std::stringstream log_message; + + const std::string content = patch.toString().toStdString(); + + if (patch_engine::load(patches, "From Download", content, true, &log_message)) + { + patch_log.success("Successfully validated downloaded patch file"); + + const std::string path = patch_engine::get_patches_path() + "patch.yml"; + const std::string path_old = path + ".old"; + + // Back up current patch file + if (!fs::copy_file(path, path_old, true)) + { + patch_log.error("Could not back up current patches to %s", path_old); + return true; + } + + // Overwrite current patch file + if (fs::file patch_file = fs::file(path, fs::rewrite)) + { + patch_file.write(content); + } + else + { + patch_log.error("Could not save new patches to %s", path); + return true; + } + + refresh(); + } + else + { + patch_log.error("Errors found in downloaded patch file"); + QMessageBox::critical(this, tr("Validation failed"), tr("Errors were found in the downloaded patch file.\n\nLog:\n%0").arg(QString::fromStdString(log_message.str()))); + } + + return true; +} diff --git a/rpcs3/rpcs3qt/patch_manager_dialog.h b/rpcs3/rpcs3qt/patch_manager_dialog.h index e1e7f920e3..a8ed7574cf 100644 --- a/rpcs3/rpcs3qt/patch_manager_dialog.h +++ b/rpcs3/rpcs3qt/patch_manager_dialog.h @@ -12,6 +12,7 @@ namespace Ui class patch_manager_dialog; } +class downloader; class gui_settings; class patch_manager_dialog : public QDialog @@ -55,6 +56,8 @@ private: void save_config(); void update_patch_info(const gui_patch_info& info); bool is_valid_file(const QMimeData& md, QStringList* drop_paths = nullptr); + void download_update(); + bool handle_json(const QByteArray& data); std::shared_ptr m_gui_settings; @@ -64,6 +67,8 @@ private: patch_engine::patch_map m_map; bool m_legacy_patches_enabled = false; + downloader* m_downloader = nullptr; + Ui::patch_manager_dialog *ui; protected: diff --git a/rpcs3/rpcs3qt/patch_manager_dialog.ui b/rpcs3/rpcs3qt/patch_manager_dialog.ui index 8a56c1bf4c..3966d4b685 100644 --- a/rpcs3/rpcs3qt/patch_manager_dialog.ui +++ b/rpcs3/rpcs3qt/patch_manager_dialog.ui @@ -255,7 +255,7 @@ - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Save + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::RestoreDefaults|QDialogButtonBox::Save diff --git a/rpcs3/rpcs3qt/update_manager.cpp b/rpcs3/rpcs3qt/update_manager.cpp index c5ae01f6c6..b5fdf43a06 100644 --- a/rpcs3/rpcs3qt/update_manager.cpp +++ b/rpcs3/rpcs3qt/update_manager.cpp @@ -52,7 +52,7 @@ void update_manager::check_for_updates(bool automatic, bool check_only, QWidget* #endif m_parent = parent; - m_downloader = new downloader("RPCS3 Updater", parent); + m_downloader = new downloader(parent); connect(m_downloader, &downloader::signal_download_error, this, [this, automatic](const QString& /*error*/) { diff --git a/rpcs3/util/yaml.cpp b/rpcs3/util/yaml.cpp index 40e940f2fa..66808dd722 100644 --- a/rpcs3/util/yaml.cpp +++ b/rpcs3/util/yaml.cpp @@ -36,12 +36,12 @@ std::pair yaml_load(const std::string& from) { result = YAML::Load(from); } - catch(const std::exception& e) + catch (const std::exception& e) { - return{YAML::Node(), std::string("YAML exception: ") + e.what()}; + return {YAML::Node(), std::string("YAML exception: ") + e.what()}; } - return{result, ""}; + return {result, ""}; } template