Qt: Use HTTPDownloader instead of QtNetwork for updates
This commit is contained in:
parent
365e3fb965
commit
af86e5d058
|
@ -4,10 +4,13 @@
|
||||||
#include "autoupdaterdialog.h"
|
#include "autoupdaterdialog.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "qthost.h"
|
#include "qthost.h"
|
||||||
|
#include "qtprogresscallback.h"
|
||||||
#include "qtutils.h"
|
#include "qtutils.h"
|
||||||
#include "scmversion/scmversion.h"
|
#include "scmversion/scmversion.h"
|
||||||
#include "unzip.h"
|
#include "unzip.h"
|
||||||
|
|
||||||
|
#include "util/http_downloader.h"
|
||||||
|
|
||||||
#include "common/file_system.h"
|
#include "common/file_system.h"
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/minizip_helpers.h"
|
#include "common/minizip_helpers.h"
|
||||||
|
@ -22,13 +25,13 @@
|
||||||
#include <QtCore/QJsonValue>
|
#include <QtCore/QJsonValue>
|
||||||
#include <QtCore/QProcess>
|
#include <QtCore/QProcess>
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
#include <QtNetwork/QNetworkAccessManager>
|
|
||||||
#include <QtNetwork/QNetworkReply>
|
|
||||||
#include <QtNetwork/QNetworkRequest>
|
|
||||||
#include <QtWidgets/QDialog>
|
#include <QtWidgets/QDialog>
|
||||||
#include <QtWidgets/QMessageBox>
|
#include <QtWidgets/QMessageBox>
|
||||||
#include <QtWidgets/QProgressDialog>
|
#include <QtWidgets/QProgressDialog>
|
||||||
|
|
||||||
|
// Interval at which HTTP requests are polled.
|
||||||
|
static constexpr u32 HTTP_POLL_INTERVAL = 10;
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
#include "common/cocoa_tools.h"
|
#include "common/cocoa_tools.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -45,8 +48,8 @@
|
||||||
#ifdef AUTO_UPDATER_SUPPORTED
|
#ifdef AUTO_UPDATER_SUPPORTED
|
||||||
|
|
||||||
static const char* LATEST_TAG_URL = "https://api.github.com/repos/stenzek/duckstation/tags";
|
static const char* LATEST_TAG_URL = "https://api.github.com/repos/stenzek/duckstation/tags";
|
||||||
static const char* LATEST_RELEASE_URL = "https://api.github.com/repos/stenzek/duckstation/releases/tags/%s";
|
static const char* LATEST_RELEASE_URL = "https://api.github.com/repos/stenzek/duckstation/releases/tags/{}";
|
||||||
static const char* CHANGES_URL = "https://api.github.com/repos/stenzek/duckstation/compare/%s...%s";
|
static const char* CHANGES_URL = "https://api.github.com/repos/stenzek/duckstation/compare/{}...{}";
|
||||||
static const char* UPDATE_ASSET_FILENAME = SCM_RELEASE_ASSET;
|
static const char* UPDATE_ASSET_FILENAME = SCM_RELEASE_ASSET;
|
||||||
static const char* UPDATE_TAGS[] = SCM_RELEASE_TAGS;
|
static const char* UPDATE_TAGS[] = SCM_RELEASE_TAGS;
|
||||||
static const char* THIS_RELEASE_TAG = SCM_RELEASE_TAG;
|
static const char* THIS_RELEASE_TAG = SCM_RELEASE_TAG;
|
||||||
|
@ -55,11 +58,8 @@ static const char* THIS_RELEASE_TAG = SCM_RELEASE_TAG;
|
||||||
|
|
||||||
Log_SetChannel(AutoUpdaterDialog);
|
Log_SetChannel(AutoUpdaterDialog);
|
||||||
|
|
||||||
AutoUpdaterDialog::AutoUpdaterDialog(EmuThread* host_interface, QWidget* parent /* = nullptr */)
|
AutoUpdaterDialog::AutoUpdaterDialog(QWidget* parent /* = nullptr */) : QDialog(parent)
|
||||||
: QDialog(parent), m_host_interface(host_interface)
|
|
||||||
{
|
{
|
||||||
m_network_access_mgr = new QNetworkAccessManager(this);
|
|
||||||
|
|
||||||
m_ui.setupUi(this);
|
m_ui.setupUi(this);
|
||||||
|
|
||||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
@ -67,6 +67,10 @@ AutoUpdaterDialog::AutoUpdaterDialog(EmuThread* host_interface, QWidget* parent
|
||||||
connect(m_ui.downloadAndInstall, &QPushButton::clicked, this, &AutoUpdaterDialog::downloadUpdateClicked);
|
connect(m_ui.downloadAndInstall, &QPushButton::clicked, this, &AutoUpdaterDialog::downloadUpdateClicked);
|
||||||
connect(m_ui.skipThisUpdate, &QPushButton::clicked, this, &AutoUpdaterDialog::skipThisUpdateClicked);
|
connect(m_ui.skipThisUpdate, &QPushButton::clicked, this, &AutoUpdaterDialog::skipThisUpdateClicked);
|
||||||
connect(m_ui.remindMeLater, &QPushButton::clicked, this, &AutoUpdaterDialog::remindMeLaterClicked);
|
connect(m_ui.remindMeLater, &QPushButton::clicked, this, &AutoUpdaterDialog::remindMeLaterClicked);
|
||||||
|
|
||||||
|
m_http = HTTPDownloader::Create(Host::GetHTTPUserAgent());
|
||||||
|
if (!m_http)
|
||||||
|
Log_ErrorPrint("Failed to create HTTP downloader, auto updater will not be available.");
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoUpdaterDialog::~AutoUpdaterDialog() = default;
|
AutoUpdaterDialog::~AutoUpdaterDialog() = default;
|
||||||
|
@ -129,16 +133,52 @@ void AutoUpdaterDialog::reportError(const char* msg, ...)
|
||||||
QMessageBox::critical(this, tr("Updater Error"), QString::fromStdString(full_msg));
|
QMessageBox::critical(this, tr("Updater Error"), QString::fromStdString(full_msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AutoUpdaterDialog::ensureHttpReady()
|
||||||
|
{
|
||||||
|
if (!m_http)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!m_http_poll_timer)
|
||||||
|
{
|
||||||
|
m_http_poll_timer = new QTimer(this);
|
||||||
|
m_http_poll_timer->connect(m_http_poll_timer, &QTimer::timeout, this, &AutoUpdaterDialog::httpPollTimerPoll);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m_http_poll_timer->isActive())
|
||||||
|
{
|
||||||
|
m_http_poll_timer->setSingleShot(false);
|
||||||
|
m_http_poll_timer->setInterval(HTTP_POLL_INTERVAL);
|
||||||
|
m_http_poll_timer->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoUpdaterDialog::httpPollTimerPoll()
|
||||||
|
{
|
||||||
|
Assert(m_http);
|
||||||
|
m_http->PollRequests();
|
||||||
|
|
||||||
|
if (!m_http->HasAnyRequests())
|
||||||
|
{
|
||||||
|
Log_VerbosePrint("All HTTP requests done.");
|
||||||
|
m_http_poll_timer->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void AutoUpdaterDialog::queueUpdateCheck(bool display_message)
|
void AutoUpdaterDialog::queueUpdateCheck(bool display_message)
|
||||||
{
|
{
|
||||||
m_display_messages = display_message;
|
m_display_messages = display_message;
|
||||||
|
|
||||||
#ifdef AUTO_UPDATER_SUPPORTED
|
#ifdef AUTO_UPDATER_SUPPORTED
|
||||||
connect(m_network_access_mgr, &QNetworkAccessManager::finished, this, &AutoUpdaterDialog::getLatestTagComplete);
|
if (!ensureHttpReady())
|
||||||
|
{
|
||||||
|
emit updateCheckCompleted();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QUrl url(QUrl::fromEncoded(QByteArray(LATEST_TAG_URL)));
|
m_http->CreateRequest(LATEST_TAG_URL, std::bind(&AutoUpdaterDialog::getLatestTagComplete, this, std::placeholders::_1,
|
||||||
QNetworkRequest request(url);
|
std::placeholders::_3));
|
||||||
m_network_access_mgr->get(request);
|
|
||||||
#else
|
#else
|
||||||
emit updateCheckCompleted();
|
emit updateCheckCompleted();
|
||||||
#endif
|
#endif
|
||||||
|
@ -147,32 +187,29 @@ void AutoUpdaterDialog::queueUpdateCheck(bool display_message)
|
||||||
void AutoUpdaterDialog::queueGetLatestRelease()
|
void AutoUpdaterDialog::queueGetLatestRelease()
|
||||||
{
|
{
|
||||||
#ifdef AUTO_UPDATER_SUPPORTED
|
#ifdef AUTO_UPDATER_SUPPORTED
|
||||||
connect(m_network_access_mgr, &QNetworkAccessManager::finished, this, &AutoUpdaterDialog::getLatestReleaseComplete);
|
if (!ensureHttpReady())
|
||||||
|
{
|
||||||
|
emit updateCheckCompleted();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
SmallString url_string;
|
std::string url = fmt::format(fmt::runtime(LATEST_RELEASE_URL), getCurrentUpdateTag());
|
||||||
url_string.format(LATEST_RELEASE_URL, getCurrentUpdateTag().c_str());
|
m_http->CreateRequest(std::move(url), std::bind(&AutoUpdaterDialog::getLatestReleaseComplete, this,
|
||||||
|
std::placeholders::_1, std::placeholders::_3));
|
||||||
QUrl url(QUrl::fromEncoded(QByteArray(url_string)));
|
|
||||||
QNetworkRequest request(url);
|
|
||||||
m_network_access_mgr->get(request);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoUpdaterDialog::getLatestTagComplete(QNetworkReply* reply)
|
void AutoUpdaterDialog::getLatestTagComplete(s32 status_code, std::vector<u8> response)
|
||||||
{
|
{
|
||||||
#ifdef AUTO_UPDATER_SUPPORTED
|
#ifdef AUTO_UPDATER_SUPPORTED
|
||||||
const std::string selected_tag(getCurrentUpdateTag());
|
const std::string selected_tag(getCurrentUpdateTag());
|
||||||
const QString selected_tag_qstr = QString::fromStdString(selected_tag);
|
const QString selected_tag_qstr = QString::fromStdString(selected_tag);
|
||||||
|
|
||||||
// this might fail due to a lack of internet connection - in which case, don't spam the user with messages every time.
|
if (status_code == HTTPDownloader::HTTP_STATUS_OK)
|
||||||
m_network_access_mgr->disconnect(this);
|
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NoError)
|
|
||||||
{
|
{
|
||||||
const QByteArray reply_json(reply->readAll());
|
|
||||||
QJsonParseError parse_error;
|
QJsonParseError parse_error;
|
||||||
QJsonDocument doc(QJsonDocument::fromJson(reply_json, &parse_error));
|
const QJsonDocument doc = QJsonDocument::fromJson(
|
||||||
|
QByteArray(reinterpret_cast<const char*>(response.data()), response.size()), &parse_error);
|
||||||
if (doc.isArray())
|
if (doc.isArray())
|
||||||
{
|
{
|
||||||
const QJsonArray doc_array(doc.array());
|
const QJsonArray doc_array(doc.array());
|
||||||
|
@ -215,24 +252,21 @@ void AutoUpdaterDialog::getLatestTagComplete(QNetworkReply* reply)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (m_display_messages)
|
if (m_display_messages)
|
||||||
reportError("Failed to download latest tag info: %d", static_cast<int>(reply->error()));
|
reportError("Failed to download latest tag info: HTTP %d", status_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit updateCheckCompleted();
|
emit updateCheckCompleted();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoUpdaterDialog::getLatestReleaseComplete(QNetworkReply* reply)
|
void AutoUpdaterDialog::getLatestReleaseComplete(s32 status_code, std::vector<u8> response)
|
||||||
{
|
{
|
||||||
#ifdef AUTO_UPDATER_SUPPORTED
|
#ifdef AUTO_UPDATER_SUPPORTED
|
||||||
m_network_access_mgr->disconnect(this);
|
if (status_code == HTTPDownloader::HTTP_STATUS_OK)
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NoError)
|
|
||||||
{
|
{
|
||||||
const QByteArray reply_json(reply->readAll());
|
|
||||||
QJsonParseError parse_error;
|
QJsonParseError parse_error;
|
||||||
QJsonDocument doc(QJsonDocument::fromJson(reply_json, &parse_error));
|
const QJsonDocument doc = QJsonDocument::fromJson(
|
||||||
|
QByteArray(reinterpret_cast<const char*>(response.data()), response.size()), &parse_error);
|
||||||
if (doc.isObject())
|
if (doc.isObject())
|
||||||
{
|
{
|
||||||
const QJsonObject doc_object(doc.object());
|
const QJsonObject doc_object(doc.object());
|
||||||
|
@ -253,8 +287,12 @@ void AutoUpdaterDialog::getLatestReleaseComplete(QNetworkReply* reply)
|
||||||
m_ui.newVersion->setText(
|
m_ui.newVersion->setText(
|
||||||
tr("New Version: %1 (%2)").arg(m_latest_sha).arg(doc_object["published_at"].toString()));
|
tr("New Version: %1 (%2)").arg(m_latest_sha).arg(doc_object["published_at"].toString()));
|
||||||
m_ui.updateNotes->setText(tr("Loading..."));
|
m_ui.updateNotes->setText(tr("Loading..."));
|
||||||
|
m_ui.downloadAndInstall->setEnabled(true);
|
||||||
queueGetChanges();
|
queueGetChanges();
|
||||||
exec();
|
|
||||||
|
// We have to defer this, because it comes back through the timer/HTTP callback...
|
||||||
|
QMetaObject::invokeMethod(this, "exec", Qt::QueuedConnection);
|
||||||
|
|
||||||
emit updateCheckCompleted();
|
emit updateCheckCompleted();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -272,7 +310,7 @@ void AutoUpdaterDialog::getLatestReleaseComplete(QNetworkReply* reply)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reportError("Failed to download latest release info: %d", static_cast<int>(reply->error()));
|
reportError("Failed to download latest release info: HTTP %d", status_code);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -280,27 +318,23 @@ void AutoUpdaterDialog::getLatestReleaseComplete(QNetworkReply* reply)
|
||||||
void AutoUpdaterDialog::queueGetChanges()
|
void AutoUpdaterDialog::queueGetChanges()
|
||||||
{
|
{
|
||||||
#ifdef AUTO_UPDATER_SUPPORTED
|
#ifdef AUTO_UPDATER_SUPPORTED
|
||||||
connect(m_network_access_mgr, &QNetworkAccessManager::finished, this, &AutoUpdaterDialog::getChangesComplete);
|
if (!ensureHttpReady())
|
||||||
|
return;
|
||||||
|
|
||||||
const std::string url_string(
|
std::string url = fmt::format(fmt::runtime(CHANGES_URL), g_scm_hash_str, getCurrentUpdateTag());
|
||||||
StringUtil::StdStringFromFormat(CHANGES_URL, g_scm_hash_str, getCurrentUpdateTag().c_str()));
|
m_http->CreateRequest(std::move(url), std::bind(&AutoUpdaterDialog::getChangesComplete, this, std::placeholders::_1,
|
||||||
QUrl url(QUrl::fromEncoded(QByteArray(url_string.c_str(), static_cast<int>(url_string.size()))));
|
std::placeholders::_3));
|
||||||
QNetworkRequest request(url);
|
|
||||||
m_network_access_mgr->get(request);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoUpdaterDialog::getChangesComplete(QNetworkReply* reply)
|
void AutoUpdaterDialog::getChangesComplete(s32 status_code, std::vector<u8> response)
|
||||||
{
|
{
|
||||||
#ifdef AUTO_UPDATER_SUPPORTED
|
#ifdef AUTO_UPDATER_SUPPORTED
|
||||||
m_network_access_mgr->disconnect(this);
|
if (status_code == HTTPDownloader::HTTP_STATUS_OK)
|
||||||
reply->deleteLater();
|
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NoError)
|
|
||||||
{
|
{
|
||||||
const QByteArray reply_json(reply->readAll());
|
|
||||||
QJsonParseError parse_error;
|
QJsonParseError parse_error;
|
||||||
QJsonDocument doc(QJsonDocument::fromJson(reply_json, &parse_error));
|
const QJsonDocument doc = QJsonDocument::fromJson(
|
||||||
|
QByteArray(reinterpret_cast<const char*>(response.data()), response.size()), &parse_error);
|
||||||
if (doc.isObject())
|
if (doc.isObject())
|
||||||
{
|
{
|
||||||
const QJsonObject doc_object(doc.object());
|
const QJsonObject doc_object(doc.object());
|
||||||
|
@ -362,67 +396,59 @@ void AutoUpdaterDialog::getChangesComplete(QNetworkReply* reply)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reportError("Failed to download change list: %d", static_cast<int>(reply->error()));
|
reportError("Failed to download change list: HTTP %d", status_code);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_ui.downloadAndInstall->setEnabled(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoUpdaterDialog::downloadUpdateClicked()
|
void AutoUpdaterDialog::downloadUpdateClicked()
|
||||||
{
|
{
|
||||||
QUrl url(m_download_url);
|
m_display_messages = true;
|
||||||
QNetworkRequest request(url);
|
|
||||||
QNetworkReply* reply = m_network_access_mgr->get(request);
|
|
||||||
|
|
||||||
QProgressDialog progress(tr("Downloading %1...").arg(m_download_url), tr("Cancel"), 0, 1);
|
std::optional<bool> download_result;
|
||||||
progress.setWindowTitle(tr("Automatic Updater"));
|
QtModalProgressCallback progress(this);
|
||||||
progress.setWindowIcon(windowIcon());
|
progress.SetTitle(tr("Automatic Updater").toUtf8().constData());
|
||||||
progress.setAutoClose(false);
|
progress.SetStatusText(tr("Downloading %1...").arg(m_latest_sha).toUtf8().constData());
|
||||||
|
progress.GetDialog().setWindowIcon(windowIcon());
|
||||||
|
progress.SetCancellable(true);
|
||||||
|
|
||||||
connect(reply, &QNetworkReply::downloadProgress, [&progress](quint64 received, quint64 total) {
|
m_http->CreateRequest(
|
||||||
progress.setRange(0, static_cast<int>(total));
|
m_download_url.toStdString(),
|
||||||
progress.setValue(static_cast<int>(received));
|
[this, &download_result](s32 status_code, const std::string&, std::vector<u8> response) {
|
||||||
});
|
if (status_code == HTTPDownloader::HTTP_STATUS_CANCELLED)
|
||||||
|
return;
|
||||||
|
|
||||||
connect(m_network_access_mgr, &QNetworkAccessManager::finished, this, [this, &progress](QNetworkReply* reply) {
|
if (status_code != HTTPDownloader::HTTP_STATUS_OK)
|
||||||
m_network_access_mgr->disconnect();
|
{
|
||||||
|
reportError("Download failed: %d", status_code);
|
||||||
|
download_result = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (reply->error() != QNetworkReply::NoError)
|
if (response.empty())
|
||||||
{
|
{
|
||||||
reportError("Download failed: %s", reply->errorString().toUtf8().constData());
|
reportError("Download failed: Update is empty");
|
||||||
progress.done(-1);
|
download_result = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QByteArray data = reply->readAll();
|
download_result = processUpdate(response);
|
||||||
if (data.isEmpty())
|
},
|
||||||
{
|
&progress);
|
||||||
reportError("Download failed: Update is empty");
|
|
||||||
progress.done(-1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (processUpdate(data))
|
// Block until completion.
|
||||||
progress.done(1);
|
while (m_http->HasAnyRequests())
|
||||||
else
|
|
||||||
progress.done(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
const int result = progress.exec();
|
|
||||||
if (result == 0)
|
|
||||||
{
|
{
|
||||||
// cancelled
|
QApplication::processEvents(QEventLoop::AllEvents, HTTP_POLL_INTERVAL);
|
||||||
reply->abort();
|
m_http->PollRequests();
|
||||||
}
|
}
|
||||||
else if (result == 1)
|
|
||||||
|
if (download_result.value_or(false))
|
||||||
{
|
{
|
||||||
// updater started
|
// updater started. since we're a modal on the main window, we have to queue this.
|
||||||
g_main_window->requestExit();
|
QMetaObject::invokeMethod(g_main_window, "requestExit", Qt::QueuedConnection, Q_ARG(bool, true));
|
||||||
done(0);
|
done(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
reply->deleteLater();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AutoUpdaterDialog::updateNeeded() const
|
bool AutoUpdaterDialog::updateNeeded() const
|
||||||
|
@ -456,7 +482,7 @@ void AutoUpdaterDialog::remindMeLaterClicked()
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
bool AutoUpdaterDialog::processUpdate(const QByteArray& update_data)
|
bool AutoUpdaterDialog::processUpdate(const std::vector<u8>& update_data)
|
||||||
{
|
{
|
||||||
const QString update_directory = QCoreApplication::applicationDirPath();
|
const QString update_directory = QCoreApplication::applicationDirPath();
|
||||||
const QString update_zip_path = update_directory + QStringLiteral("\\update.zip");
|
const QString update_zip_path = update_directory + QStringLiteral("\\update.zip");
|
||||||
|
@ -472,7 +498,9 @@ bool AutoUpdaterDialog::processUpdate(const QByteArray& update_data)
|
||||||
|
|
||||||
{
|
{
|
||||||
QFile update_zip_file(update_zip_path);
|
QFile update_zip_file(update_zip_path);
|
||||||
if (!update_zip_file.open(QIODevice::WriteOnly) || update_zip_file.write(update_data) != update_data.size())
|
if (!update_zip_file.open(QIODevice::WriteOnly) ||
|
||||||
|
update_zip_file.write(reinterpret_cast<const char*>(update_data.data()),
|
||||||
|
static_cast<qint64>(update_data.size())) != static_cast<qint64>(update_data.size()))
|
||||||
{
|
{
|
||||||
reportError("Writing update zip to '%s' failed", update_zip_path.toUtf8().constData());
|
reportError("Writing update zip to '%s' failed", update_zip_path.toUtf8().constData());
|
||||||
return false;
|
return false;
|
||||||
|
@ -587,7 +615,7 @@ void AutoUpdaterDialog::cleanupAfterUpdate()
|
||||||
|
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
|
|
||||||
bool AutoUpdaterDialog::processUpdate(const QByteArray& update_data)
|
bool AutoUpdaterDialog::processUpdate(const std::vector<u8>& update_data)
|
||||||
{
|
{
|
||||||
std::optional<std::string> bundle_path = CocoaTools::GetNonTranslocatedBundlePath();
|
std::optional<std::string> bundle_path = CocoaTools::GetNonTranslocatedBundlePath();
|
||||||
if (!bundle_path.has_value())
|
if (!bundle_path.has_value())
|
||||||
|
@ -628,7 +656,9 @@ bool AutoUpdaterDialog::processUpdate(const QByteArray& update_data)
|
||||||
// Save update.
|
// Save update.
|
||||||
{
|
{
|
||||||
QFile zip_file(QString::fromStdString(zip_path));
|
QFile zip_file(QString::fromStdString(zip_path));
|
||||||
if (!zip_file.open(QIODevice::WriteOnly) || zip_file.write(update_data) != update_data.size())
|
if (!zip_file.open(QIODevice::WriteOnly) ||
|
||||||
|
zip_file.write(reinterpret_cast<const char*>(update_data.data()), static_cast<qint64>(update_data.size())) !=
|
||||||
|
static_cast<qint64>(update_data.size()))
|
||||||
{
|
{
|
||||||
reportError("Writing update zip to '%s' failed", zip_path.c_str());
|
reportError("Writing update zip to '%s' failed", zip_path.c_str());
|
||||||
return false;
|
return false;
|
||||||
|
@ -659,7 +689,7 @@ void AutoUpdaterDialog::cleanupAfterUpdate()
|
||||||
|
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
|
|
||||||
bool AutoUpdaterDialog::processUpdate(const QByteArray& update_data)
|
bool AutoUpdaterDialog::processUpdate(const std::vector<u8>& update_data)
|
||||||
{
|
{
|
||||||
const char* appimage_path = std::getenv("APPIMAGE");
|
const char* appimage_path = std::getenv("APPIMAGE");
|
||||||
if (!appimage_path || !FileSystem::FileExists(appimage_path))
|
if (!appimage_path || !FileSystem::FileExists(appimage_path))
|
||||||
|
@ -699,7 +729,9 @@ bool AutoUpdaterDialog::processUpdate(const QByteArray& update_data)
|
||||||
QFile old_file(qappimage_path);
|
QFile old_file(qappimage_path);
|
||||||
const QFileDevice::Permissions old_permissions = old_file.permissions();
|
const QFileDevice::Permissions old_permissions = old_file.permissions();
|
||||||
QFile new_file(new_appimage_path);
|
QFile new_file(new_appimage_path);
|
||||||
if (!new_file.open(QIODevice::WriteOnly) || new_file.write(update_data) != update_data.size() ||
|
if (!new_file.open(QIODevice::WriteOnly) ||
|
||||||
|
new_file.write(reinterpret_cast<const char*>(update_data.data()), static_cast<qint64>(update_data.size())) !=
|
||||||
|
static_cast<qint64>(update_data.size()) ||
|
||||||
!new_file.setPermissions(old_permissions))
|
!new_file.setPermissions(old_permissions))
|
||||||
{
|
{
|
||||||
QFile::remove(new_appimage_path);
|
QFile::remove(new_appimage_path);
|
||||||
|
@ -759,7 +791,7 @@ void AutoUpdaterDialog::cleanupAfterUpdate()
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
bool AutoUpdaterDialog::processUpdate(const QByteArray& update_data)
|
bool AutoUpdaterDialog::processUpdate(const std::vector<u8>& update_data)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,19 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/types.h"
|
||||||
|
|
||||||
#include "ui_autoupdaterdialog.h"
|
#include "ui_autoupdaterdialog.h"
|
||||||
|
|
||||||
#include <QtCore/QStringList>
|
#include <memory>
|
||||||
#include <QtWidgets/QDialog>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class QNetworkAccessManager;
|
#include <QtCore/QDateTime>
|
||||||
class QNetworkReply;
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QTimer>
|
||||||
|
#include <QtWidgets/QDialog>
|
||||||
|
|
||||||
|
class HTTPDownloader;
|
||||||
|
|
||||||
class EmuThread;
|
class EmuThread;
|
||||||
|
|
||||||
|
@ -19,7 +24,7 @@ class AutoUpdaterDialog final : public QDialog
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit AutoUpdaterDialog(EmuThread* host_interface, QWidget* parent = nullptr);
|
explicit AutoUpdaterDialog(QWidget* parent = nullptr);
|
||||||
~AutoUpdaterDialog();
|
~AutoUpdaterDialog();
|
||||||
|
|
||||||
static bool isSupported();
|
static bool isSupported();
|
||||||
|
@ -35,11 +40,7 @@ public Q_SLOTS:
|
||||||
void queueGetLatestRelease();
|
void queueGetLatestRelease();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void getLatestTagComplete(QNetworkReply* reply);
|
void httpPollTimerPoll();
|
||||||
void getLatestReleaseComplete(QNetworkReply* reply);
|
|
||||||
|
|
||||||
void queueGetChanges();
|
|
||||||
void getChangesComplete(QNetworkReply* reply);
|
|
||||||
|
|
||||||
void downloadUpdateClicked();
|
void downloadUpdateClicked();
|
||||||
void skipThisUpdateClicked();
|
void skipThisUpdateClicked();
|
||||||
|
@ -47,21 +48,30 @@ private Q_SLOTS:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void reportError(const char* msg, ...);
|
void reportError(const char* msg, ...);
|
||||||
|
|
||||||
|
bool ensureHttpReady();
|
||||||
|
|
||||||
bool updateNeeded() const;
|
bool updateNeeded() const;
|
||||||
std::string getCurrentUpdateTag() const;
|
std::string getCurrentUpdateTag() const;
|
||||||
|
|
||||||
|
void getLatestTagComplete(s32 status_code, std::vector<u8> response);
|
||||||
|
void getLatestReleaseComplete(s32 status_code, std::vector<u8> response);
|
||||||
|
|
||||||
|
void queueGetChanges();
|
||||||
|
void getChangesComplete(s32 status_code, std::vector<u8> response);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
bool processUpdate(const QByteArray& update_data);
|
bool processUpdate(const std::vector<u8>& update_data);
|
||||||
bool extractUpdater(const QString& zip_path, const QString& destination_path);
|
bool extractUpdater(const QString& zip_path, const QString& destination_path);
|
||||||
bool doUpdate(const QString& zip_path, const QString& updater_path, const QString& destination_path);
|
bool doUpdate(const QString& zip_path, const QString& updater_path, const QString& destination_path);
|
||||||
#else
|
#else
|
||||||
bool processUpdate(const QByteArray& update_data);
|
bool processUpdate(const std::vector<u8>& update_data);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Ui::AutoUpdaterDialog m_ui;
|
Ui::AutoUpdaterDialog m_ui;
|
||||||
|
|
||||||
EmuThread* m_host_interface;
|
std::unique_ptr<HTTPDownloader> m_http;
|
||||||
QNetworkAccessManager* m_network_access_mgr = nullptr;
|
QTimer* m_http_poll_timer = nullptr;
|
||||||
QString m_latest_sha;
|
QString m_latest_sha;
|
||||||
QString m_download_url;
|
QString m_download_url;
|
||||||
int m_download_size = 0;
|
int m_download_size = 0;
|
||||||
|
|
|
@ -2871,7 +2871,7 @@ void MainWindow::checkForUpdates(bool display_message)
|
||||||
if (m_auto_updater_dialog)
|
if (m_auto_updater_dialog)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_auto_updater_dialog = new AutoUpdaterDialog(g_emu_thread, this);
|
m_auto_updater_dialog = new AutoUpdaterDialog(this);
|
||||||
connect(m_auto_updater_dialog, &AutoUpdaterDialog::updateCheckCompleted, this, &MainWindow::onUpdateCheckComplete);
|
connect(m_auto_updater_dialog, &AutoUpdaterDialog::updateCheckCompleted, this, &MainWindow::onUpdateCheckComplete);
|
||||||
m_auto_updater_dialog->queueUpdateCheck(display_message);
|
m_auto_updater_dialog->queueUpdateCheck(display_message);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue