diff --git a/rpcs3/rpcs3qt/downloader.cpp b/rpcs3/rpcs3qt/downloader.cpp index 4886e21924..345491d5de 100644 --- a/rpcs3/rpcs3qt/downloader.cpp +++ b/rpcs3/rpcs3qt/downloader.cpp @@ -7,6 +7,8 @@ #include "curl_handle.h" #include "progress_dialog.h" +#include "Crypto/sha256.h" + LOG_CHANNEL(network_log, "NETWORK"); size_t curl_write_cb_compat(char* ptr, size_t /*size*/, size_t nmemb, void* userdata) @@ -120,6 +122,27 @@ progress_dialog* downloader::get_progress_dialog() const return m_progress_dialog; } +std::string downloader::get_hash(const char* data, size_t size, bool lower_case) +{ + u8 res_hash[32]; + mbedtls_sha256_context ctx; + mbedtls_sha256_init(&ctx); + mbedtls_sha256_starts_ret(&ctx, 0); + mbedtls_sha256_update_ret(&ctx, reinterpret_cast(data), size); + mbedtls_sha256_finish_ret(&ctx, res_hash); + + std::string res_hash_string("0000000000000000000000000000000000000000000000000000000000000000"); + + for (size_t index = 0; index < 32; index++) + { + const auto pal = lower_case ? "0123456789abcdef" : "0123456789ABCDEF"; + res_hash_string[index * 2] = pal[res_hash[index] >> 4]; + res_hash_string[(index * 2) + 1] = pal[res_hash[index] & 15]; + } + + return res_hash_string; +} + size_t downloader::update_buffer(char* data, size_t size) { if (m_curl_abort) diff --git a/rpcs3/rpcs3qt/downloader.h b/rpcs3/rpcs3qt/downloader.h index 8bd253929a..f6110084de 100644 --- a/rpcs3/rpcs3qt/downloader.h +++ b/rpcs3/rpcs3qt/downloader.h @@ -22,6 +22,8 @@ public: progress_dialog* get_progress_dialog() const; + static std::string get_hash(const char* data, size_t size, bool lower_case); + private Q_SLOTS: void handle_buffer_update(int size, int max); diff --git a/rpcs3/rpcs3qt/patch_manager_dialog.cpp b/rpcs3/rpcs3qt/patch_manager_dialog.cpp index d326ae41af..2a88a1ac60 100644 --- a/rpcs3/rpcs3qt/patch_manager_dialog.cpp +++ b/rpcs3/rpcs3qt/patch_manager_dialog.cpp @@ -832,7 +832,26 @@ void patch_manager_dialog::dragLeaveEvent(QDragLeaveEvent* event) void patch_manager_dialog::download_update() { - m_downloader->start("https://rpcs3.net/compatibility?patch&api=v1", true, true, tr("Downloading latest patches")); + patch_log.notice("Patch download triggered"); + + const std::string path = patch_engine::get_patches_path() + "patch.yml"; + std::string url = "https://rpcs3.net/compatibility?patch&api=v1&v=" + patch_engine_version; + + if (fs::is_file(path)) + { + if (fs::file patch_file{path}) + { + const std::string hash = downloader::get_hash(patch_file.to_string().c_str(), patch_file.size(), true); + url += "&sha256=" + hash; + } + else + { + patch_log.error("Could not open patch file: %s", path); + return; + } + } + + m_downloader->start(url, true, true, tr("Downloading latest patches")); } bool patch_manager_dialog::handle_json(const QByteArray& data) @@ -845,8 +864,9 @@ bool patch_manager_dialog::handle_json(const QByteArray& data) std::string error_message; switch (return_code) { - case -1: error_message = "Hash not found"; break; + case -1: error_message = "No patches found for the specified version"; break; case -2: error_message = "Server Error - Maintenance Mode"; break; + case -3: error_message = "Server Error - Illegal Search"; break; case -255: error_message = "Server Error - Return code not found"; break; default: error_message = "Server Error - Unknown Error"; break; } @@ -859,6 +879,19 @@ bool patch_manager_dialog::handle_json(const QByteArray& data) return false; } + if (return_code == 1) + { + patch_log.notice("Patch download: No newer patches found"); + QMessageBox::information(this, tr("Download successful"), tr("Your patch file is already up to date.")); + return true; + } + + if (return_code != 0) + { + patch_log.error("Patch download error: unknown return code: %d", return_code); + return false; + } + const QJsonValue& version_obj = json_data["version"]; if (!version_obj.isString()) @@ -874,6 +907,14 @@ bool patch_manager_dialog::handle_json(const QByteArray& data) return false; } + const QJsonValue& hash_obj = json_data["sha256"]; + + if (!hash_obj.isString()) + { + patch_log.error("JSON doesn't contain sha256"); + return false; + } + const QJsonValue& patch = json_data["patch"]; if (!patch.isString() || patch.toString().isEmpty()) @@ -887,18 +928,26 @@ bool patch_manager_dialog::handle_json(const QByteArray& data) const std::string content = patch.toString().toStdString(); + if (hash_obj.toString().toStdString() != downloader::get_hash(content.c_str(), content.size(), true)) + { + patch_log.error("JSON content does not match the provided checksum"); + return false; + } + if (patch_engine::load(patches, "From Download", content, true, &log_message)) { - patch_log.success("Successfully validated downloaded patch file"); + patch_log.notice("Successfully validated downloaded patch file"); + const std::string path = patch_engine::get_patches_path() + "patch.yml"; - 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)) + // Back up current patch file if possible + if (fs::is_file(path)) { - patch_log.error("Could not back up current patches to %s", path_old); - return true; + if (const std::string path_old = path + ".old"; + !fs::copy_file(path, path_old, true)) + { + patch_log.error("Could not back up current patches to %s", path_old); + return false; + } } // Overwrite current patch file @@ -909,7 +958,7 @@ bool patch_manager_dialog::handle_json(const QByteArray& data) else { patch_log.error("Could not save new patches to %s", path); - return true; + return false; } refresh(); @@ -920,5 +969,7 @@ bool patch_manager_dialog::handle_json(const QByteArray& data) 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()))); } + patch_log.success("Successfully downloaded latest patch file"); + QMessageBox::information(this, tr("Download successful"), tr("Your patch file is now up to date")); return true; } diff --git a/rpcs3/rpcs3qt/update_manager.cpp b/rpcs3/rpcs3qt/update_manager.cpp index b5fdf43a06..4c3c7a625f 100644 --- a/rpcs3/rpcs3qt/update_manager.cpp +++ b/rpcs3/rpcs3qt/update_manager.cpp @@ -5,7 +5,6 @@ #include "rpcs3_version.h" #include "downloader.h" #include "Utilities/StrUtil.h" -#include "Crypto/sha256.h" #include "Emu/System.h" #include @@ -249,22 +248,8 @@ bool update_manager::handle_rpcs3(const QByteArray& data) return false; } - u8 res_hash[32]; - mbedtls_sha256_context ctx; - mbedtls_sha256_init(&ctx); - mbedtls_sha256_starts_ret(&ctx, 0); - mbedtls_sha256_update_ret(&ctx, reinterpret_cast(data.data()), data.size()); - mbedtls_sha256_finish_ret(&ctx, res_hash); - - std::string res_hash_string("0000000000000000000000000000000000000000000000000000000000000000"); - for (size_t index = 0; index < 32; index++) - { - constexpr auto pal = "0123456789ABCDEF"; - res_hash_string[index * 2] = pal[res_hash[index] >> 4]; - res_hash_string[(index * 2) + 1] = pal[res_hash[index] & 15]; - } - - if (m_expected_hash != res_hash_string) + if (const std::string res_hash_string = downloader::get_hash(data.data(), data.size(), false); + m_expected_hash != res_hash_string) { update_log.error("Hash mismatch: %s expected: %s", res_hash_string, m_expected_hash); return false;