From 02e63b19480fa066df2ddcd1d4019d6225c0b371 Mon Sep 17 00:00:00 2001 From: crueter Date: Tue, 28 Apr 2026 10:09:44 -0400 Subject: [PATCH] Rewrite Signed-off-by: crueter --- src/common/net/net.cpp | 69 +++++++++++++++--------------- src/yuzu/updater/update_dialog.cpp | 49 ++++++++------------- src/yuzu/updater/update_dialog.h | 1 + 3 files changed, 53 insertions(+), 66 deletions(-) diff --git a/src/common/net/net.cpp b/src/common/net/net.cpp index 5c24f7bc5b..ef1fcf0c3e 100644 --- a/src/common/net/net.cpp +++ b/src/common/net/net.cpp @@ -23,20 +23,6 @@ namespace Common::Net { std::vector Release::GetPlatformAssets() const { - // FIXME(crueter): use search strings based on platform's assets, or a fallback name if not -#ifdef _WIN32 - static constexpr const std::string prefix = "Eden-Windows"; -#elif defined(__APPLE__) - static constexpr const std::string prefix = "Eden-macOS"; -#elif defined(__ANDROID__) - static constexpr const std::string prefix = "Eden-Android"; -#else - LOG_DEBUG(Common, "Unsupported platform for auto-update"); - static constexpr const std::string prefix = "Eden"; -#endif - - std::vector suffixes; - // TODO(crueter): Need better handling for this as a whole. #ifdef NIGHTLY_BUILD std::vector result; @@ -48,44 +34,57 @@ std::vector Release::GetPlatformAssets() const { const auto ref = tag; #endif - const auto make_asset = [this, ref](const std::string& name, - const std::string& suffix) -> Asset { - const auto filename = fmt::format("{}-{}{}", prefix, ref, suffix); - return Asset{.name = name, - .url = host, - .path = fmt::format("{}/{}", base_download_url, filename), - .filename = filename}; + std::vector found_assets; + + // FIXME: This is mildly inefficient. + // Finds assets based on a hierarchy of regex search strings. + const auto find_asset = [&found_assets, ref, this](const std::string& name, + const std::vector& suffixes) { + for (const std::string& asset : assets) { + for (const auto& suffix : suffixes) { + if (asset.ends_with(suffix)) { + const std::string_view asset_sv = asset; + const size_t pos = asset_sv.find_last_of('/'); + const std::string_view filename = + (pos != std::string_view::npos) ? asset_sv.substr(pos + 1) : asset_sv; + + found_assets.emplace_back(Asset{ + .name = name, + .url = host, + .path = asset, + .filename = std::string{filename}, + }); + return; + } + } + } }; - // TODO(crueter): Handle setup when that becomes a thing - // TODO(crueter): Descriptions? Android? - return { #ifdef _WIN32 #ifdef ARCHITECTURE_x86_64 - make_asset(QT_TR_NOOP("Standard"), "-amd64-msvc-standard.zip"), - // make_asset(QT_TR_NOOP("MinGW"), "-mingw-amd64-gcc-standard.zip"), - make_asset(QT_TR_NOOP("PGO"), "-mingw-amd64-clang-pgo.zip") + find_asset("Standard", {"amd64-msvc-standard.exe", "amd64-msvc-standard.zip", "mingw-amd64-gcc-standard.exe", "mingw-amd64-gcc-standard.zip"}); + find_asset("PGO", {"mingw-amd64-clang-pgo.exe", "mingw-amd64-clang-pgo.zip"}); #elif defined(ARCHITECTURE_arm64) - make_asset(QT_TR_NOOP("Standard"), "-mingw-arm64-clang-standard.zip"), - make_asset(QT_TR_NOOP("PGO"), "-mingw-arm64-clang-pgo.zip") + find_asset("Standard", {"mingw-arm64-clang-standard.exe", "mingw-arm64-clang-standard.zip"}); + find_asset("PGO", {"mingw-arm64-clang-pgo.exe", "mingw-arm64-clang-pgo.zip"}); #endif #elif defined(__APPLE__) #ifdef ARCHITECTURE_arm64 - make_asset(QT_TR_NOOP("Standard"), ".dmg"), + find_asset("Standard", {".dmg", ".tar.gz"}); #endif #elif defined(__ANDROID__) #ifdef ARCHITECTURE_x86_64 - make_asset("Standard", "-chromeos.apk"), + find_asset("Standard", {"chromeos.apk"}); #elif defined(ARCHITECTURE_arm64) #ifdef YUZU_LEGACY - make_asset("Standard", "-legacy.apk"), + find_asset("Standard", {"legacy.apk"}); #else - make_asset("Standard", "-standard.apk"), - make_asset("Genshin Spoof", "-optimized.apk"), + find_asset("Standard", {"standard.apk"}); + find_asset("Genshin Spoof", {"optimized.apk"}); #endif #endif #endif - }; + return found_assets; } static inline u64 ParseIsoTimestamp(const std::string& iso) { diff --git a/src/yuzu/updater/update_dialog.cpp b/src/yuzu/updater/update_dialog.cpp index addbff6101..a35e39e5d0 100644 --- a/src/yuzu/updater/update_dialog.cpp +++ b/src/yuzu/updater/update_dialog.cpp @@ -43,19 +43,22 @@ UpdateDialog::UpdateDialog(const Common::Net::Release& release, QWidget* parent) connect(this, &QDialog::accepted, this, [release]() { QDesktopServices::openUrl(QUrl{QString::fromStdString(release.html_url)}); }); + } else if (assets.size() == 1) { + m_asset = assets[0]; + + connect(this, &QDialog::accepted, this, &UpdateDialog::Download); } else { u32 i = 0; - for (const Common::Net::Asset& a : release.GetPlatformAssets()) { + for (const Common::Net::Asset& a : assets) { QRadioButton* r = new QRadioButton(tr(a.name.c_str()), this); if (i == 0) r->setChecked(true); ++i; - r->setProperty("url", QString::fromStdString(a.url)); - r->setProperty("path", QString::fromStdString(a.path)); - r->setProperty("filename", QString::fromStdString(a.filename)); - ui->radioButtons->addWidget(r); - m_buttons.append(r); + + connect(r, &QRadioButton::clicked, this, [a, this](bool checked) { + m_asset = a; + }); } connect(this, &QDialog::accepted, this, &UpdateDialog::Download); @@ -67,22 +70,9 @@ UpdateDialog::~UpdateDialog() { } void UpdateDialog::Download() { - std::string url, path, asset_filename; - for (QRadioButton* r : std::as_const(m_buttons)) { - if (r->isChecked()) { - url = r->property("url").toString().toStdString(); - path = r->property("path").toString().toStdString(); - asset_filename = r->property("filename").toString().toStdString(); - break; - } - } - - if (url.empty()) - return; - const auto filename = QtCommon::Frontend::GetSaveFileName( tr("New Version Location"), - qApp->applicationDirPath() % QStringLiteral("/") % QString::fromStdString(asset_filename), + qApp->applicationDirPath() % QStringLiteral("/") % QString::fromStdString(m_asset.filename), tr("All Files (*.*)")); if (filename.isEmpty()) @@ -99,7 +89,7 @@ void UpdateDialog::Download() { // TODO(crueter): Move to net.cpp constexpr std::size_t timeout_seconds = 15; - std::unique_ptr client = std::make_unique(url); + std::unique_ptr client = std::make_unique(m_asset.url); client->set_connection_timeout(timeout_seconds); client->set_read_timeout(timeout_seconds); client->set_write_timeout(timeout_seconds); @@ -109,7 +99,7 @@ void UpdateDialog::Download() { #endif if (client == nullptr) { - LOG_ERROR(Frontend, "Invalid URL {}{}", url, path); + LOG_ERROR(Frontend, "Invalid URL {}{}", m_asset.url, m_asset.path); return; } @@ -140,7 +130,7 @@ void UpdateDialog::Download() { }; // Now send off request - auto result = client->Get(path, content_receiver, progress_callback); + auto result = client->Get(m_asset.path, content_receiver, progress_callback); progress->close(); // commit to file @@ -151,33 +141,30 @@ void UpdateDialog::Download() { } if (!result) { - LOG_ERROR(Frontend, "GET to {}{} returned null", url, path); + LOG_ERROR(Frontend, "GET to {}{} returned null", m_asset.url, m_asset.path); return; } const auto& response = result.value(); if (response.status >= 400) { - LOG_ERROR(Frontend, "GET to {}{} returned error status code: {}", url, path, + LOG_ERROR(Frontend, "GET to {}{} returned error status code: {}", m_asset.url, m_asset.path, response.status); QtCommon::Frontend::Critical( tr("Failed to download file"), tr("Could not download from %1%2\nError code: %3") - .arg(QString::fromStdString(url), QString::fromStdString(path), QString::number(response.status))); + .arg(QString::fromStdString(m_asset.url), QString::fromStdString(m_asset.path), QString::number(response.status))); return; } if (!response.headers.contains("content-type")) { - LOG_ERROR(Frontend, "GET to {}{} returned no content", url, path); + LOG_ERROR(Frontend, "GET to {}{} returned no content", m_asset.url, m_asset.path); return; } // Download is complete. User may choose to open in the file manager. - // TODO(crueter): Auto-extract for zip, auto-open for DMG - // e.g. download to tmp directory? - auto button = QtCommon::Frontend::Question(tr("Download Complete"), tr("Successfully downloaded %1. Would you like to open it?") - .arg(QString::fromStdString(asset_filename)), + .arg(QString::fromStdString(m_asset.filename)), QtCommon::Frontend::Yes | QtCommon::Frontend::No); if (button == QtCommon::Frontend::Yes) { diff --git a/src/yuzu/updater/update_dialog.h b/src/yuzu/updater/update_dialog.h index cd8924f4f5..692481b710 100644 --- a/src/yuzu/updater/update_dialog.h +++ b/src/yuzu/updater/update_dialog.h @@ -24,4 +24,5 @@ private slots: private: Ui::UpdateDialog* ui; QList m_buttons; + Common::Net::Asset m_asset; };