mirror of
https://github.com/Rosalie241/RMG.git
synced 2026-07-11 01:24:01 +02:00
RMG: introduce an updater
This commit is contained in:
+6
-1
@@ -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}")
|
||||
|
||||
@@ -33,6 +33,7 @@ set(RMG_CORE_SOURCES
|
||||
VidExt.cpp
|
||||
Video.cpp
|
||||
Error.cpp
|
||||
Unzip.cpp
|
||||
Core.cpp
|
||||
Key.cpp
|
||||
Rom.cpp
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -13,6 +13,7 @@ enum class SettingsID
|
||||
GUI_PauseEmulationOnFocusLoss,
|
||||
GUI_ResumeEmulationOnFocus,
|
||||
GUI_AutomaticFullscreen,
|
||||
GUI_CheckForUpdates,
|
||||
GUI_Version,
|
||||
|
||||
// Core Plugin Settings
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user