Auto-Updater
This commit is contained in:
@@ -341,6 +341,7 @@ TARGET_LINK_LIBRARIES (noggit
|
||||
Qt5::OpenGL
|
||||
Qt5::OpenGLExtensions
|
||||
Qt5::Xml
|
||||
Qt5::Network
|
||||
ColorWidgets-qt5
|
||||
FramelessHelper
|
||||
qt_imgui_widgets
|
||||
|
||||
@@ -128,6 +128,14 @@ NoggitProjectSelectionWindow::NoggitProjectSelectionWindow(Noggit::Application::
|
||||
close();
|
||||
}
|
||||
);
|
||||
|
||||
_updater = new Noggit::Ui::CUpdater(this);
|
||||
|
||||
QObject::connect(_updater, &CUpdater::OpenUpdater, [=]()
|
||||
{
|
||||
_updater->setModal(true);
|
||||
_updater->show();
|
||||
});
|
||||
}
|
||||
|
||||
void NoggitProjectSelectionWindow::handleContextMenuProjectListItemDelete(std::string const& project_path)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <noggit/ui/windows/noggitWindow/NoggitWindow.hpp>
|
||||
#include <noggit/ui/windows/projectCreation/NoggitProjectCreationDialog.h>
|
||||
#include <noggit/ui/windows/settingsPanel/SettingsPanel.h>
|
||||
#include <noggit/ui/windows/updater/Updater.h>
|
||||
#include <ui_NoggitProjectSelectionWindow.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@@ -45,6 +46,7 @@ namespace Noggit::Ui::Windows
|
||||
::Ui::NoggitProjectSelectionWindow* _ui;
|
||||
Noggit::Application::NoggitApplication* _noggit_application;
|
||||
Noggit::Ui::settings* _settings;
|
||||
Noggit::Ui::CUpdater* _updater;
|
||||
|
||||
std::unique_ptr<Noggit::Ui::Windows::NoggitWindow> _project_selection_page;
|
||||
std::unique_ptr<Component::LoadProjectComponent> _load_project_component;
|
||||
|
||||
274
src/noggit/ui/windows/updater/Updater.cpp
Normal file
274
src/noggit/ui/windows/updater/Updater.cpp
Normal file
@@ -0,0 +1,274 @@
|
||||
#include "Updater.h"
|
||||
#include "ui_Updater.h"
|
||||
#include <noggit/Log.h>
|
||||
|
||||
namespace Noggit
|
||||
{
|
||||
namespace Ui
|
||||
{
|
||||
CUpdater::CUpdater(QWidget* parent) :
|
||||
QDialog(parent)
|
||||
{
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
ui = new ::Ui::Updater;
|
||||
ui->setupUi(this);
|
||||
|
||||
// hide progress
|
||||
ui->ProgressFile->hide();
|
||||
ui->ProgressDownload->hide();
|
||||
hide();
|
||||
|
||||
FileNeeded.clear();
|
||||
LocalMD5.clear();
|
||||
OnlineMD5.clear();
|
||||
|
||||
FileNeededCount = 0;
|
||||
FileMissingCount = 0;
|
||||
|
||||
LocalCheck = false;
|
||||
OnlineCheck = false;
|
||||
NeedUpdate = false;
|
||||
|
||||
// connect ui button to update or close
|
||||
connect(ui->Close, &QPushButton::clicked, this, [=]() { close(); });
|
||||
connect(ui->Update, &QPushButton::clicked, this, [=]() { DownloadUpdate(); });
|
||||
|
||||
QNetworkRequest request((QUrl(StorageURL.arg("MD5"))));
|
||||
QNetworkReply* reply = (new QNetworkAccessManager)->get(request);
|
||||
connect(reply, SIGNAL(finished()), this, SLOT(GenerateOnlineMD5()));
|
||||
|
||||
GenerateLocalMD5();
|
||||
}
|
||||
|
||||
QByteArray CUpdater::FileMD5(const QString& filename, QCryptographicHash::Algorithm algo)
|
||||
{
|
||||
QFile file(filename);
|
||||
if (file.open(QFile::ReadOnly))
|
||||
{
|
||||
QCryptographicHash hash(algo);
|
||||
|
||||
if (hash.addData(&file))
|
||||
return hash.result();
|
||||
}
|
||||
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QString CUpdater::ToHashFile(const QString& name, const QString& hash)
|
||||
{
|
||||
return QString("%1 %2").arg(name, hash);
|
||||
}
|
||||
|
||||
QUrl CUpdater::GenerateLink(const QString& name)
|
||||
{
|
||||
return QUrl(StorageURL.arg(name));
|
||||
}
|
||||
|
||||
void CUpdater::GenerateLocalMD5()
|
||||
{
|
||||
QVector<QString> ignore = {
|
||||
"md5",
|
||||
"listfile.csv",
|
||||
"log.txt"
|
||||
};
|
||||
|
||||
QDirIterator it(QDir::currentPath(), QStringList() << "*", QDir::Files, QDirIterator::Subdirectories);
|
||||
QFile MD5 = QDir::currentPath() + "/MD5";
|
||||
if (MD5.open(QIODevice::WriteOnly))
|
||||
{
|
||||
QTextStream stream(&MD5);
|
||||
|
||||
while (it.hasNext())
|
||||
{
|
||||
const QString file = it.next();
|
||||
|
||||
if (ignore.contains(QFileInfo(file).fileName().toLower()))
|
||||
continue;
|
||||
|
||||
QString name = file.mid(QDir::currentPath().size() + 1);
|
||||
QString hash = FileMD5(file, QCryptographicHash::Md5).toHex();
|
||||
|
||||
LocalMD5[name] = hash;
|
||||
stream << ToHashFile(name, hash) << Qt::endl;
|
||||
}
|
||||
|
||||
MD5.flush();
|
||||
MD5.close();
|
||||
}
|
||||
|
||||
LocalCheck = true;
|
||||
CompareMD5();
|
||||
}
|
||||
|
||||
void CUpdater::GenerateOnlineMD5()
|
||||
{
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
if (!reply || reply->size() == 0xE)
|
||||
return;
|
||||
|
||||
QFile temp_md5(QDir::currentPath() + "online_MD5.txt");
|
||||
if (temp_md5.open(QIODevice::WriteOnly))
|
||||
{
|
||||
QTextStream stream(&temp_md5);
|
||||
stream << reply->readAll();
|
||||
temp_md5.flush();
|
||||
temp_md5.close();
|
||||
}
|
||||
|
||||
if (temp_md5.open(QIODevice::ReadOnly))
|
||||
{
|
||||
QTextStream stream(&temp_md5);
|
||||
|
||||
while (!stream.atEnd())
|
||||
{
|
||||
QString line = stream.readLine();
|
||||
QStringList split = line.split(R"( )");
|
||||
OnlineMD5[split[0]] = split[1];
|
||||
}
|
||||
|
||||
temp_md5.close();
|
||||
}
|
||||
|
||||
QDir dir;
|
||||
dir.remove(temp_md5.fileName());
|
||||
|
||||
OnlineCheck = true;
|
||||
CompareMD5();
|
||||
}
|
||||
|
||||
void CUpdater::CompareMD5()
|
||||
{
|
||||
if (!LocalCheck || !OnlineCheck)
|
||||
return;
|
||||
|
||||
for (const auto& e : OnlineMD5.toStdMap())
|
||||
{
|
||||
if (LocalMD5[e.first] != e.second)
|
||||
{
|
||||
FileNeeded.push_back(e.first);
|
||||
|
||||
if (ui->FileList->toPlainText().isEmpty())
|
||||
{
|
||||
ui->FileList->append(QString(tr("This is all files needed to update.")));
|
||||
}
|
||||
|
||||
ui->FileList->append(QString(" - %1 [%2 => %3]").arg(FileNeeded.last()).arg(LocalMD5[e.first]).arg(e.second));
|
||||
}
|
||||
}
|
||||
|
||||
if (FileNeeded.size() == 0)
|
||||
{
|
||||
NeedUpdate = false;
|
||||
return;
|
||||
}
|
||||
|
||||
FileNeededCount = FileNeeded.size();
|
||||
NeedUpdate = true;
|
||||
|
||||
if (NeedUpdate)
|
||||
{
|
||||
ui->ProgressFile->setMaximum(FileNeededCount);
|
||||
ui->ProgressFile->setFormat(QString("File %v/%1").arg(FileNeededCount));
|
||||
ui->ProgressFile->show();
|
||||
|
||||
emit OpenUpdater();
|
||||
}
|
||||
}
|
||||
|
||||
void CUpdater::DownloadUpdate()
|
||||
{
|
||||
if (FileNeeded.size() == 0)
|
||||
return;
|
||||
|
||||
ui->ProgressDownload->show();
|
||||
|
||||
QNetworkRequest request(GenerateLink(FileNeeded[0]));
|
||||
QNetworkReply* reply = (new QNetworkAccessManager)->get(request);
|
||||
connect(reply, &QNetworkReply::downloadProgress, reply, [this](qint64 received, qint64 total)
|
||||
{
|
||||
ui->ProgressDownload->setMaximum(total);
|
||||
ui->ProgressDownload->setValue(received);
|
||||
});
|
||||
|
||||
connect(reply, SIGNAL(finished()), this, SLOT(GetOnlineFile()));
|
||||
}
|
||||
|
||||
void CUpdater::GetOnlineFile()
|
||||
{
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
|
||||
ui->ProgressFile->setValue(ui->ProgressFile->value() + 1);
|
||||
ui->FileList->append(QString("Downloading : %1 (%2 mb)").arg(FileNeeded[0]).arg(reply->size() / pow(10,6)));
|
||||
|
||||
if (!reply || reply->size() == 0xE)
|
||||
{
|
||||
FileMissingCount += 1;
|
||||
FileNeeded.removeAt(0);
|
||||
if (FileNeeded.size() > 0)
|
||||
{
|
||||
DownloadUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
StartExternalUpdater();
|
||||
return;
|
||||
}
|
||||
|
||||
QDir dir;
|
||||
if (!dir.exists(QDir::currentPath() + TemporaryFolder))
|
||||
dir.mkpath(QDir::currentPath() + TemporaryFolder);
|
||||
|
||||
auto index = FileNeeded[0].lastIndexOf(R"(/)");
|
||||
if (index >= 0)
|
||||
{
|
||||
QDir dir;
|
||||
if (!dir.exists(FileURL.arg(QDir::currentPath(), TemporaryFolder, FileNeeded[0].mid(0, index))))
|
||||
dir.mkpath(FileURL.arg(QDir::currentPath(), TemporaryFolder, FileNeeded[0].mid(0, index)));
|
||||
}
|
||||
|
||||
QFile file = FileURL.arg(QDir::currentPath(), TemporaryFolder, FileNeeded[0]);
|
||||
if (file.open(QIODevice::WriteOnly))
|
||||
{
|
||||
file.write(reply->readAll());
|
||||
file.flush();
|
||||
file.close();
|
||||
}
|
||||
|
||||
FileNeeded.removeAt(0);
|
||||
if (FileNeeded.size() > 0)
|
||||
{
|
||||
DownloadUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
StartExternalUpdater();
|
||||
}
|
||||
|
||||
void CUpdater::StartExternalUpdater()
|
||||
{
|
||||
|
||||
Log << "Start External 0" << std::endl;
|
||||
|
||||
if (!NeedUpdate)
|
||||
return;
|
||||
|
||||
Log << "Start External 1" << std::endl;
|
||||
|
||||
if (FileNeededCount == FileMissingCount)
|
||||
return;
|
||||
|
||||
Log << "Start External 2" << std::endl;
|
||||
|
||||
{
|
||||
QString exec(QDir::currentPath() + ExternalProcess);
|
||||
|
||||
Log << "Start External - " << exec.toStdString() << std::endl;
|
||||
|
||||
QProcess process;
|
||||
process.setProgram(exec);
|
||||
process.startDetached();
|
||||
QCoreApplication::quit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
64
src/noggit/ui/windows/updater/Updater.h
Normal file
64
src/noggit/ui/windows/updater/Updater.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <qpushbutton.h>
|
||||
#include "qprocess.h"
|
||||
#include <qfile.h>
|
||||
#include <qdir.h>
|
||||
#include <qdiriterator.h>
|
||||
#include <QtWidgets/QDialog>
|
||||
#include <QtNetwork/qnetworkreply.h>
|
||||
#include <QtNetwork/qnetworkrequest.h>
|
||||
#include <QtNetwork/qnetworkaccessmanager.h>
|
||||
|
||||
#include "ui_Updater.h"
|
||||
|
||||
namespace Noggit
|
||||
{
|
||||
namespace Ui
|
||||
{
|
||||
class CUpdater : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
::Ui::Updater* ui;
|
||||
|
||||
public:
|
||||
CUpdater(QWidget* parent = nullptr);
|
||||
|
||||
private:
|
||||
QByteArray FileMD5(const QString& filename, QCryptographicHash::Algorithm algo);
|
||||
QString ToHashFile(const QString& name, const QString& hash);
|
||||
QUrl GenerateLink(const QString& name);
|
||||
|
||||
void GenerateLocalMD5();
|
||||
void CompareMD5();
|
||||
void DownloadUpdate();
|
||||
void StartExternalUpdater();
|
||||
|
||||
private slots:
|
||||
void GenerateOnlineMD5();
|
||||
void GetOnlineFile();
|
||||
|
||||
signals:
|
||||
void OpenUpdater();
|
||||
|
||||
private:
|
||||
const QString TemporaryFolder = "/temp";
|
||||
const QString StorageURL = "https://raw.githubusercontent.com/Intemporel/NoggitRedBinaries/main/%1";
|
||||
const QString FileURL = "%1%2/%3";
|
||||
const QString ExternalProcess = "/noggit-updater.exe";
|
||||
|
||||
int FileNeededCount;
|
||||
int FileMissingCount;
|
||||
|
||||
bool NeedUpdate;
|
||||
bool LocalCheck, OnlineCheck;
|
||||
|
||||
QMap<QString, QString> LocalMD5;
|
||||
QMap<QString, QString> OnlineMD5;
|
||||
|
||||
QVector<QString> FileNeeded;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
104
src/noggit/ui/windows/updater/Updater.ui
Normal file
104
src/noggit/ui/windows/updater/Updater.ui
Normal file
@@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Updater</class>
|
||||
<widget class="QDialog" name="Updater">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>589</width>
|
||||
<height>376</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>NoggitRed Updater</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>12</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>An update was found, do you want to update ?</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="Update">
|
||||
<property name="text">
|
||||
<string>Update</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="Close">
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="ProgressFile">
|
||||
<property name="maximum">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="format">
|
||||
<string>File %v/10</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QProgressBar" name="ProgressDownload">
|
||||
<property name="maximum">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="format">
|
||||
<string>Bytes %v / %m</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTextEdit" name="FileList"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>12</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
Reference in New Issue
Block a user