diff --git a/CMakeLists.txt b/CMakeLists.txt index e2fbb851..0dde6e20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -341,6 +341,7 @@ TARGET_LINK_LIBRARIES (noggit Qt5::OpenGL Qt5::OpenGLExtensions Qt5::Xml + Qt5::Network ColorWidgets-qt5 FramelessHelper qt_imgui_widgets diff --git a/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.cpp b/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.cpp index d4f43477..a4b0f53d 100755 --- a/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.cpp +++ b/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.cpp @@ -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) diff --git a/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.hpp b/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.hpp index 14e56da2..3dd41dd0 100755 --- a/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.hpp +++ b/src/noggit/ui/windows/projectSelection/NoggitProjectSelectionWindow.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include 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 _project_selection_page; std::unique_ptr _load_project_component; diff --git a/src/noggit/ui/windows/updater/Updater.cpp b/src/noggit/ui/windows/updater/Updater.cpp new file mode 100644 index 00000000..7ba4d89e --- /dev/null +++ b/src/noggit/ui/windows/updater/Updater.cpp @@ -0,0 +1,274 @@ +#include "Updater.h" +#include "ui_Updater.h" +#include + +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 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(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(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(); + } + } + } +} diff --git a/src/noggit/ui/windows/updater/Updater.h b/src/noggit/ui/windows/updater/Updater.h new file mode 100644 index 00000000..e16af297 --- /dev/null +++ b/src/noggit/ui/windows/updater/Updater.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include "qprocess.h" +#include +#include +#include +#include +#include +#include +#include + +#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 LocalMD5; + QMap OnlineMD5; + + QVector FileNeeded; + }; + } + +} + diff --git a/src/noggit/ui/windows/updater/Updater.ui b/src/noggit/ui/windows/updater/Updater.ui new file mode 100644 index 00000000..f7453481 --- /dev/null +++ b/src/noggit/ui/windows/updater/Updater.ui @@ -0,0 +1,104 @@ + + + Updater + + + + 0 + 0 + 589 + 376 + + + + NoggitRed Updater + + + + + + Qt::Vertical + + + + 20 + 12 + + + + + + + + An update was found, do you want to update ? + + + Qt::AlignCenter + + + + + + + + + Update + + + + + + + Close + + + + + + + + + 10 + + + 0 + + + File %v/10 + + + + + + + 10 + + + 0 + + + Bytes %v / %m + + + + + + + + + + Qt::Vertical + + + + 20 + 12 + + + + + + + + +