RMG: introduce an updater

This commit is contained in:
Rosalie Wanders
2022-12-04 20:39:45 +01:00
parent 40f8fa982d
commit cabc56550b
20 changed files with 999 additions and 1 deletions
+6 -1
View File
@@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.15)
option(PORTABLE_INSTALL "Portable Installation" ON)
option(NO_GIT_CLONE "Disables git clone usage for 3rdParty dependencies" OFF)
option(UPDATER "Enables updater" ${PORTABLE_INSTALL})
project(RMG)
find_package(Git REQUIRED)
@@ -22,6 +22,11 @@ if (NOT PORTABLE_INSTALL AND (WIN32 OR MSYS))
set(PORTABLE_INSTALL ON)
endif()
if (NOT PORTABLE_INSTALL AND UPDATER)
message(WARNING "Not-Portable builds don't support the updater, disabling UPDATER")
set(UPDATER OFF)
endif()
if (PORTABLE_INSTALL)
set(CMAKE_INSTALL_PREFIX "")
set(INSTALL_PATH "Bin/${CMAKE_BUILD_TYPE}")
+1
View File
@@ -33,6 +33,7 @@ set(RMG_CORE_SOURCES
VidExt.cpp
Video.cpp
Error.cpp
Unzip.cpp
Core.cpp
Key.cpp
Rom.cpp
+1
View File
@@ -24,6 +24,7 @@
#include "Plugins.hpp"
#include "Cheats.hpp"
#include "Error.hpp"
#include "Unzip.hpp"
#include "Video.hpp"
#include "Key.hpp"
#include "Rom.hpp"
+3
View File
@@ -161,6 +161,9 @@ static l_Setting get_setting(SettingsID settingId)
case SettingsID::GUI_AutomaticFullscreen:
setting = {SETTING_SECTION_GUI, "AutomaticFullscreen", false};
break;
case SettingsID::GUI_CheckForUpdates:
setting = {SETTING_SECTION_GUI, "CheckForUpdates", true};
break;
case SettingsID::GUI_Version:
setting = {SETTING_SECTION_GUI, "Version", CORE_VERSION};
break;
+1
View File
@@ -13,6 +13,7 @@ enum class SettingsID
GUI_PauseEmulationOnFocusLoss,
GUI_ResumeEmulationOnFocus,
GUI_AutomaticFullscreen,
GUI_CheckForUpdates,
GUI_Version,
// Core Plugin Settings
+169
View File
@@ -0,0 +1,169 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "Unzip.hpp"
#include "Error.hpp"
#include <exception>
#include <filesystem>
#include <fstream>
#include <unzip.h>
//
// Local Defines
//
#define UNZIP_READ_SIZE 67108860 /* 64 MiB */
//
// Exported Functions
//
bool CoreUnzip(std::filesystem::path file, std::filesystem::path path)
{
std::string error;
unzFile zipFile;
unz_global_info zipInfo;
std::ofstream outputStream;
char* read_buffer = (char*)malloc(UNZIP_READ_SIZE);
size_t bytes_read = 0;
if (read_buffer == nullptr)
{
error = "CoreUnzip: malloc Failed!";
CoreSetError(error);
return false;
}
zipFile = unzOpen(file.string().c_str());
if (zipFile == nullptr)
{
free(read_buffer);
error = "CoreUnzip: unzOpen Failed!";
CoreSetError(error);
return false;
}
if (unzGetGlobalInfo(zipFile, &zipInfo) != UNZ_OK)
{
free(read_buffer);
error = "CoreUnzip: unzGetGlobalInfo Failed!";
CoreSetError(error);
return false;
}
for (int i = 0; i < zipInfo.number_entry; i++)
{
unz_file_info fileInfo;
char fileName[PATH_MAX];
std::filesystem::path targetPath;
// ensure we can retrieve the current file info
if (unzGetCurrentFileInfo(zipFile, &fileInfo, fileName, PATH_MAX, nullptr, 0, nullptr, 0) != UNZ_OK)
{
free(read_buffer);
unzClose(zipFile);
error = "CoreUnzip: unzGetCurrentFileInfo Failed!";
CoreSetError(error);
return false;
}
targetPath = path;
targetPath += "/";
targetPath += fileName;
if (targetPath.string().ends_with("/"))
{ // directory
try
{
if (!std::filesystem::is_directory(targetPath) &&
!std::filesystem::create_directory(targetPath))
{
throw std::exception();
}
}
catch (...)
{
free(read_buffer);
unzClose(zipFile);
error = "CoreUnzip: std::filesystem::create_directory(";
error += targetPath.string();
error += ") Failed!";
CoreSetError(error);
return false;
}
}
else
{ // file
if (unzOpenCurrentFile(zipFile) != UNZ_OK)
{
free(read_buffer);
unzClose(zipFile);
error = "CoreUnzip: unzOpenCurrentFile Failed!";
CoreSetError(error);
return false;
}
outputStream.open(targetPath, std::ios::trunc | std::ios::binary);
if (!outputStream.is_open())
{
free(read_buffer);
unzCloseCurrentFile(zipFile);
unzClose(zipFile);
error = "CoreUnzip: failed to open file!";
error += targetPath.string();
CoreSetError(error);
return false;
}
do
{
bytes_read = unzReadCurrentFile(zipFile, read_buffer, UNZIP_READ_SIZE);
if (bytes_read < 0)
{
free(read_buffer);
unzCloseCurrentFile(zipFile);
unzClose(zipFile);
error = "CoreUnzip: unzReadCurrentFile Failed!";
CoreSetError(error);
return false;
}
else
{ // write data to file
outputStream.write(read_buffer, bytes_read);
}
} while (bytes_read > 0);
outputStream.close();
unzCloseCurrentFile(zipFile);
}
// break when we've iterated over all entries
if ((i + 1) >= zipInfo.number_entry)
{
break;
}
// move to next file
if (unzGoToNextFile(zipFile) != UNZ_OK)
{
free(read_buffer);
unzClose(zipFile);
error = "CoreUnzip: unzGoToNextFile Failed!";
CoreSetError(error);
return false;
}
}
unzClose(zipFile);
free(read_buffer);
return true;
}
+18
View File
@@ -0,0 +1,18 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef CORE_UNZIP_HPP
#define CORE_UNZIP_HPP
#include <filesystem>
// attempts to unzip the file to path
bool CoreUnzip(std::filesystem::path file, std::filesystem::path path);
#endif // CORE_UNZIP_HPP
+21
View File
@@ -7,6 +7,11 @@ set(CMAKE_AUTOUIC ON)
find_package(Qt5 COMPONENTS Gui Widgets Core REQUIRED)
if (UPDATER)
find_package(Qt5 COMPONENTS Network REQUIRED)
add_definitions(-DUPDATER)
endif(UPDATER)
find_package(PkgConfig REQUIRED)
pkg_check_modules(SDL2 REQUIRED sdl2)
@@ -42,6 +47,17 @@ set(RMG_SOURCES
main.cpp
)
if (UPDATER)
list(APPEND RMG_SOURCES
UserInterface/Dialog/UpdateDialog.cpp
UserInterface/Dialog/UpdateDialog.ui
UserInterface/Dialog/DownloadUpdateDialog.cpp
UserInterface/Dialog/DownloadUpdateDialog.ui
UserInterface/Dialog/InstallUpdateDialog.cpp
UserInterface/Dialog/InstallUpdateDialog.ui
)
endif(UPDATER)
if (CMAKE_BUILD_TYPE MATCHES Release)
add_executable(RMG WIN32 ${RMG_SOURCES})
else()
@@ -61,3 +77,8 @@ target_include_directories(RMG PRIVATE
)
target_link_libraries(RMG Qt5::Gui Qt5::Widgets)
if (UPDATER)
target_link_libraries(RMG Qt5::Network)
endif(UPDATER)
@@ -0,0 +1,102 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "DownloadUpdateDialog.hpp"
#include <QMessageBox>
#include <QFileInfo>
#include <QPushButton>
#include <QDesktopServices>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QTemporaryDir>
#include <QFile>
using namespace UserInterface::Dialog;
DownloadUpdateDialog::DownloadUpdateDialog(QWidget *parent, QUrl url, QString filename) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint)
{
this->setupUi(this);
this->filename = filename;
this->label->setText("Downloading " + filename + "...");
QNetworkAccessManager* networkAccessManager = new QNetworkAccessManager(this);
QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
this->reply = networkAccessManager->get(request);
connect(reply, &QNetworkReply::downloadProgress, this, &DownloadUpdateDialog::on_reply_downloadProgress);
connect(reply, &QNetworkReply::finished, this, &DownloadUpdateDialog::on_reply_finished);
}
DownloadUpdateDialog::~DownloadUpdateDialog(void)
{
}
QString DownloadUpdateDialog::GetTempDirectory(void)
{
return this->temporaryDirectory;
}
QString DownloadUpdateDialog::GetFileName(void)
{
return this->filename;
}
void DownloadUpdateDialog::showErrorMessage(QString error, QString details)
{
QMessageBox msgBox((this->isVisible() ? this : this->parentWidget()));
msgBox.setIcon(QMessageBox::Icon::Critical);
msgBox.setWindowTitle("Error");
msgBox.setText(error);
msgBox.setDetailedText(details);
msgBox.addButton(QMessageBox::Ok);
msgBox.exec();
}
void DownloadUpdateDialog::on_reply_downloadProgress(qint64 size, qint64 total)
{
this->progressBar->setMaximum(total);
this->progressBar->setMinimum(0);
this->progressBar->setValue(size);
}
void DownloadUpdateDialog::on_reply_finished(void)
{
if (this->reply->error())
{
this->showErrorMessage("Failed to download update file!", this->reply->errorString());
this->reply->deleteLater();
this->reject();
return;
}
QTemporaryDir temporaryDir;
temporaryDir.setAutoRemove(false);
if (!temporaryDir.isValid())
{
this->showErrorMessage("Failed to create temporary directory!", "");
this->reply->deleteLater();
this->reject();
return;
}
this->temporaryDirectory = temporaryDir.path();
QFile file(temporaryDir.filePath(this->filename));
file.open(QIODevice::WriteOnly);
file.write(this->reply->readAll());
file.close();
this->reply->deleteLater();
this->accept();
}
@@ -0,0 +1,52 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef DOWNLOADUPDATEDIALOG_HPP
#define DOWNLOADUPDATEDIALOG_HPP
#include <QWidget>
#include <QDialog>
#include <QJsonObject>
#include <QNetworkReply>
#include <RMG-Core/Core.hpp>
#include <qglobal.h>
#include "ui_DownloadUpdateDialog.h"
namespace UserInterface
{
namespace Dialog
{
class DownloadUpdateDialog : public QDialog, private Ui::DownloadUpdateDialog
{
Q_OBJECT
private:
QNetworkReply *reply;
QString temporaryDirectory;
QString filename;
void showErrorMessage(QString error, QString details);
public:
DownloadUpdateDialog(QWidget *parent, QUrl url, QString filename);
~DownloadUpdateDialog(void);
QString GetTempDirectory(void);
QString GetFileName(void);
private slots:
void on_reply_downloadProgress(qint64 size, qint64 total);
void on_reply_finished(void);
};
} // namespace Dialog
} // namespace UserInterface
#endif // DOWNLOADUPDATEDIALOG_HPP
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DownloadUpdateDialog</class>
<widget class="QDialog" name="DownloadUpdateDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>238</width>
<height>62</height>
</rect>
</property>
<property name="windowTitle">
<string>Downloading Update</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Downloading ...</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,157 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "InstallUpdateDialog.hpp"
#include "RMG-Core/Error.hpp"
#include <QMessageBox>
#include <QProcess>
#include <QDir>
#include <QTextStream>
using namespace UserInterface::Dialog;
InstallUpdateDialog::InstallUpdateDialog(QWidget *parent, QString installationDirectory, QString temporaryDirectory, QString filename) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint)
{
this->setupUi(this);
this->installationDirectory = installationDirectory;
this->temporaryDirectory = temporaryDirectory;
this->filename = filename;
this->install();
}
InstallUpdateDialog::~InstallUpdateDialog(void)
{
}
void InstallUpdateDialog::install(void)
{
this->label->setText("Extracting " + this->filename + "...");
this->progressBar->setValue(50);
QString fullFilePath;
fullFilePath = this->temporaryDirectory;
fullFilePath += "/" + this->filename;
QString appPath = QCoreApplication::applicationDirPath();
QString appPid = QString::number(QCoreApplication::applicationPid());
#ifdef _WIN32
if (this->filename.endsWith(".exe"))
{
QStringList scriptLines =
{
"@echo off",
"taskkill /F /PID:" + appPid,
"\"" + fullFilePath + "\" /CLOSEAPPLICATIONS /SILENT /DIR=\"" + appPath + "\"",
"rmdir /S /Q \"" + this->temporaryDirectory + "\"",
};
this->writeAndRunScript(scriptLines);
this->accept();
return;
}
#endif // _WIN32
QDir dir(this->temporaryDirectory);
if (!dir.mkdir("extract"))
{
this->showErrorMessage("QDir::mkdir() Failed!", "");
this->reject();
return;
}
QString extractDirectory;
extractDirectory = this->temporaryDirectory;
extractDirectory += "/extract";
if (!CoreUnzip(fullFilePath.toStdU32String(), extractDirectory.toStdU32String()))
{
this->showErrorMessage("CoreUnzip() Failed!", QString::fromStdString(CoreGetError()));
this->reject();
return;
}
#ifdef _WIN32
QStringList scriptLines =
{
"@echo off",
"del /F /Q \"" + fullFilePath + "\"",
"taskkill /F /PID:" + appPid,
"copy /Y \"" + extractDirectory + "\\*\" \"" + appPath + "/\"",
"start \"\" \"" + appPath + "\\RMG.exe\"",
"rmdir /S /Q \"" + this->temporaryDirectory + "\"",
};
this->writeAndRunScript(scriptLines);
#else // Linux
QStringList scriptLines =
{
"#!/usr/bin/env bash",
"set -e",
"rm -rf \"" + fullFilePath + "\"",
"kill -1 " + appPid,
"cp -R \"" + extractDirectory + "/\"* \"" + appPath + "/\"",
"chmod +x \"" + appPath + "/RMG\"",
"\"" + appPath + "/RMG\"&",
"rm -rf \"" + this->temporaryDirectory + "\"" ,
};
this->writeAndRunScript(scriptLines);
#endif // _WIN32
}
void InstallUpdateDialog::writeAndRunScript(QStringList stringList)
{
QString scriptPath;
scriptPath = this->temporaryDirectory;
#ifdef _WIN32
scriptPath += "/update.bat";
#else
scriptPath += "/update.sh";
#endif // _WIN32
QFile scriptFile(scriptPath);
if (!scriptFile.open(QIODevice::WriteOnly | QIODevice::Text))
{
this->showErrorMessage("QFile::open() Failed!", "");
return;
}
QTextStream textStream(&scriptFile);
// write script to file
for (const QString& str : stringList)
{
textStream << str << "\n";
}
scriptFile.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner);
scriptFile.close();
this->launchProcess(scriptPath, {});
}
void InstallUpdateDialog::launchProcess(QString file, QStringList arguments)
{
QProcess process;
process.setProgram(file);
process.setArguments(arguments);
process.startDetached();
}
void InstallUpdateDialog::showErrorMessage(QString error, QString details)
{
QMessageBox msgBox((this->isVisible() ? this : this->parentWidget()));
msgBox.setIcon(QMessageBox::Icon::Critical);
msgBox.setWindowTitle("Error");
msgBox.setText(error);
msgBox.setDetailedText(details);
msgBox.addButton(QMessageBox::Ok);
msgBox.exec();
}
@@ -0,0 +1,46 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef INSTALLUPDATEDIALOG_HPP
#define INSTALLUPDATEDIALOG_HPP
#include <QWidget>
#include <QDialog>
#include <RMG-Core/Core.hpp>
#include "ui_InstallUpdateDialog.h"
namespace UserInterface
{
namespace Dialog
{
class InstallUpdateDialog : public QDialog, private Ui::InstallUpdateDialog
{
Q_OBJECT
private:
QString installationDirectory;
QString temporaryDirectory;
QString filename;
void install(void);
void writeAndRunScript(QStringList stringList);
void launchProcess(QString file, QStringList arguments);
void showErrorMessage(QString error, QString details);
public:
InstallUpdateDialog(QWidget *parent, QString installationDirectory, QString temporaryDirectory, QString filename);
~InstallUpdateDialog(void);
};
} // namespace Dialog
} // namespace UserInterface
#endif // INSTALLUPDATEDIALOG_HPP
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>InstallUpdateDialog</class>
<widget class="QDialog" name="InstallUpdateDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>223</width>
<height>62</height>
</rect>
</property>
<property name="windowTitle">
<string>Installing Update</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Extracting ...</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
@@ -0,0 +1,112 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "UpdateDialog.hpp"
#include "DownloadUpdateDialog.hpp"
#include <QMessageBox>
#include <QFileInfo>
#include <QPushButton>
#include <QJsonArray>
#include <QDesktopServices>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QTemporaryDir>
#include <QFile>
#include <QProcess>
using namespace UserInterface::Dialog;
UpdateDialog::UpdateDialog(QWidget *parent, QJsonObject jsonObject) : QDialog(parent, Qt::WindowSystemMenuHint | Qt::WindowTitleHint)
{
this->setupUi(this);
this->jsonObject = jsonObject;
this->label->setText(jsonObject.value("tag_name").toString() + " Available");
this->textEdit->setText(jsonObject.value("body").toString());
// change ok button text to 'Update'
QPushButton* button = this->buttonBox->button(QDialogButtonBox::Ok);
button->setText("Update");
}
UpdateDialog::~UpdateDialog(void)
{
}
void UpdateDialog::on_disableUpdateCheckCheckBox_stateChanged(int state)
{
CoreSettingsSetValue(SettingsID::GUI_CheckForUpdates, (state == Qt::Unchecked));
CoreSettingsSave();
}
QString UpdateDialog::GetFileName(void)
{
return this->filename;
}
QUrl UpdateDialog::GetUrl(void)
{
return this->url;
}
void UpdateDialog::accept(void)
{
QJsonArray jsonArray = jsonObject["assets"].toArray();
QString filenameToDownload;
QUrl urlToDownload;
#ifdef _WIN32
this->isWin32Setup = QFile::exists("unins000.exe") && QFile::exists("unins000.dat");
#endif // _WIN32
for (const QJsonValue& value : jsonArray)
{
QJsonObject object = value.toObject();
QString filename = object.value("name").toString();
QString url = object.value("browser_download_url").toString();
#ifdef _WIN32
if (this->isWin32Setup)
{
if (filename.contains("Windows64") &&
filename.contains("Setup"))
{
filenameToDownload = filename;
urlToDownload = QUrl(url);
break;
}
}
else
{
if (filename.contains("Windows64") &&
filename.contains("Portable"))
{
filenameToDownload = filename;
urlToDownload = QUrl(url);
break;
}
}
#else
if (filename.contains("Linux64") &&
filename.contains("Portable"))
{
filenameToDownload = filename;
urlToDownload = QUrl(url);
break;
}
#endif // _WIN32
}
this->url = urlToDownload;
this->filename = filenameToDownload;
QDialog::accept();
}
@@ -0,0 +1,53 @@
/*
* Rosalie's Mupen GUI - https://github.com/Rosalie241/RMG
* Copyright (C) 2020 Rosalie Wanders <rosalie@mailbox.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef UPDATEDIALOG_HPP
#define UPDATEDIALOG_HPP
#include <QWidget>
#include <QDialog>
#include <QJsonObject>
#include <QNetworkReply>
#include <RMG-Core/Core.hpp>
#include "ui_UpdateDialog.h"
namespace UserInterface
{
namespace Dialog
{
class UpdateDialog : public QDialog, private Ui::UpdateDialog
{
Q_OBJECT
private:
QJsonObject jsonObject;
QString filename;
QUrl url;
#ifdef _WIN32
bool isWin32Setup = false;
#endif // _WIN32
public:
UpdateDialog(QWidget *parent, QJsonObject jsonObject);
~UpdateDialog(void);
QString GetFileName(void);
QUrl GetUrl(void);
private slots:
void on_disableUpdateCheckCheckBox_stateChanged(int state);
void accept(void) Q_DECL_OVERRIDE;
};
} // namespace Dialog
} // namespace UserInterface
#endif // UPDATEDIALOG_HPP
@@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>UpdateDialog</class>
<widget class="QDialog" name="UpdateDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>351</width>
<height>419</height>
</rect>
</property>
<property name="windowTitle">
<string>A new version is available</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>v0.2.2 Available</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QTextEdit" name="textEdit">
<property name="lineWrapMode">
<enum>QTextEdit::NoWrap</enum>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="disableUpdateCheckCheckBox">
<property name="text">
<string>Don't check for updates again</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>UpdateDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>UpdateDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
+78
View File
@@ -10,6 +10,11 @@
#include "MainWindow.hpp"
#include "UserInterface/Dialog/AboutDialog.hpp"
#ifdef UPDATER
#include "UserInterface/Dialog/UpdateDialog.hpp"
#include "UserInterface/Dialog/DownloadUpdateDialog.hpp"
#include "UserInterface/Dialog/InstallUpdateDialog.hpp"
#endif // UPDATER
#include "UserInterface/EventFilter.hpp"
#include "Utilities/QtKeyToSdl2Key.hpp"
#include "Callbacks.hpp"
@@ -60,6 +65,10 @@ bool MainWindow::Init(QGuiApplication* app)
this->ui_Actions_Init();
this->ui_Actions_Connect();
#ifdef UPDATER
this->ui_CheckForUpdates();
#endif // UPDATER
this->menuBar_Init();
this->menuBar_Setup(false, false);
@@ -705,6 +714,20 @@ void MainWindow::ui_Actions_Connect(void)
connect(this->action_Help_About, &QAction::triggered, this, &MainWindow::on_Action_Help_About);
}
#ifdef UPDATER
void MainWindow::ui_CheckForUpdates(void)
{
if (!CoreSettingsGetBoolValue(SettingsID::GUI_CheckForUpdates))
{
return;
}
QNetworkAccessManager* networkAccessManager = new QNetworkAccessManager(this);
connect(networkAccessManager, &QNetworkAccessManager::finished, this, &MainWindow::on_networkAccessManager_Finished);
networkAccessManager->get(QNetworkRequest(QUrl("https://api.github.com/repos/Rosalie241/RMG/releases/latest")));
}
#endif // UPDATER
void MainWindow::timerEvent(QTimerEvent *event)
{
int timerId = event->timerId();
@@ -825,6 +848,61 @@ void MainWindow::on_QGuiApplication_applicationStateChanged(Qt::ApplicationState
}
}
#ifdef UPDATER
void MainWindow::on_networkAccessManager_Finished(QNetworkReply* reply)
{
if (reply->error())
{
reply->deleteLater();
return;
}
QJsonDocument jsonDocument = QJsonDocument::fromJson(reply->readAll());
QJsonObject jsonObject = jsonDocument.object();
QString currentVersion = QString(VERSION_STR);
QString latestVersion = jsonObject.value("tag_name").toString();
reply->deleteLater();
// make sure the current version is valid
// and not a dev version
if (currentVersion.size() != 6)
{
return;
}
// do nothing when versions match
if (currentVersion == latestVersion)
{
return;
}
int ret = 0;
Dialog::UpdateDialog updateDialog(this, jsonObject);
ret = updateDialog.exec();
if (ret != QDialog::Accepted)
{
return;
}
Dialog::DownloadUpdateDialog downloadUpdateDialog(this, updateDialog.GetUrl(), updateDialog.GetFileName());
ret = downloadUpdateDialog.exec();
if (ret != QDialog::Accepted)
{
return;
}
Dialog::InstallUpdateDialog installUpdateDialog(this, QCoreApplication::applicationDirPath(), downloadUpdateDialog.GetTempDirectory(), downloadUpdateDialog.GetFileName());
ret = installUpdateDialog.exec();
if (ret != QDialog::Accepted)
{
return;
}
}
#endif // UPDATER
void MainWindow::on_Action_File_OpenRom(void)
{
bool isRunning = CoreIsEmulationRunning();
+14
View File
@@ -27,6 +27,13 @@
#include <QStackedWidget>
#include <QGuiApplication>
#ifdef UPDATER
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#endif // UPDATER
namespace UserInterface
{
class MainWindow : public QMainWindow
@@ -134,6 +141,9 @@ class MainWindow : public QMainWindow
void ui_Actions_Remove(void);
void ui_Actions_Connect(void);
#ifdef UPDATER
void ui_CheckForUpdates(void);
#endif // UPDATER
protected:
void timerEvent(QTimerEvent *) Q_DECL_OVERRIDE;
@@ -143,6 +153,10 @@ class MainWindow : public QMainWindow
void on_EventFilter_FileDropped(QDropEvent *);
void on_QGuiApplication_applicationStateChanged(Qt::ApplicationState);
#ifdef UPDATER
void on_networkAccessManager_Finished(QNetworkReply *);
#endif // UPDATER
void on_Action_File_OpenRom(void);
void on_Action_File_OpenCombo(void);
+4
View File
@@ -40,4 +40,8 @@ done
windeployqt "$exe"
# needed by Qt at runtime
cp "$path/libcrypto-1_1-x64.dll" "$bin_dir/"
cp "$path/libssl-1_1-x64.dll" "$bin_dir/"
exit 0