Signed-off-by: crueter <crueter@eden-emu.dev>
This commit is contained in:
crueter 2026-04-28 10:09:44 -04:00
parent 0fdcca036b
commit 02e63b1948
No known key found for this signature in database
GPG key ID: 425ACD2D4830EBC6
3 changed files with 53 additions and 66 deletions

View file

@ -23,20 +23,6 @@
namespace Common::Net {
std::vector<Asset> 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<std::string> suffixes;
// TODO(crueter): Need better handling for this as a whole.
#ifdef NIGHTLY_BUILD
std::vector<std::string> result;
@ -48,44 +34,57 @@ std::vector<Asset> 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<Asset> 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<std::string>& 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) {

View file

@ -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<httplib::Client> client = std::make_unique<httplib::Client>(url);
std::unique_ptr<httplib::Client> client = std::make_unique<httplib::Client>(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) {

View file

@ -24,4 +24,5 @@ private slots:
private:
Ui::UpdateDialog* ui;
QList<QRadioButton *> m_buttons;
Common::Net::Asset m_asset;
};