mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-02 22:29:21 +00:00
updater works
This commit is contained in:
parent
e37df05d75
commit
5259ec5fc9
13 changed files with 288 additions and 205 deletions
|
@ -112,9 +112,13 @@ HttpRequest::Response HttpRequest::Post(const std::string& url, const std::strin
|
||||||
int HttpRequest::Impl::CurlProgressCallback(Impl* impl, curl_off_t dltotal, curl_off_t dlnow,
|
int HttpRequest::Impl::CurlProgressCallback(Impl* impl, curl_off_t dltotal, curl_off_t dlnow,
|
||||||
curl_off_t ultotal, curl_off_t ulnow)
|
curl_off_t ultotal, curl_off_t ulnow)
|
||||||
{
|
{
|
||||||
// Abort if callback isn't true
|
// Call the progress callback with the current download progress
|
||||||
return !impl->m_callback(static_cast<s64>(dltotal), static_cast<s64>(dlnow),
|
if (impl->m_callback)
|
||||||
static_cast<s64>(ultotal), static_cast<s64>(ulnow));
|
{
|
||||||
|
return !impl->m_callback(static_cast<s64>(dltotal), static_cast<s64>(dlnow),
|
||||||
|
static_cast<s64>(ultotal), static_cast<s64>(ulnow));
|
||||||
|
}
|
||||||
|
return 0; // If no callback, continue
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpRequest::Impl::Impl(std::chrono::milliseconds timeout_ms, ProgressCallback callback)
|
HttpRequest::Impl::Impl(std::chrono::milliseconds timeout_ms, ProgressCallback callback)
|
||||||
|
|
|
@ -28,6 +28,11 @@ public:
|
||||||
// Return false to abort the request
|
// Return false to abort the request
|
||||||
using ProgressCallback = std::function<bool(s64 dltotal, s64 dlnow, s64 ultotal, s64 ulnow)>;
|
using ProgressCallback = std::function<bool(s64 dltotal, s64 dlnow, s64 ultotal, s64 ulnow)>;
|
||||||
|
|
||||||
|
void SetProgressCallback(ProgressCallback callback) {
|
||||||
|
m_callback = std::move(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
explicit HttpRequest(std::chrono::milliseconds timeout_ms = std::chrono::milliseconds{3000},
|
explicit HttpRequest(std::chrono::milliseconds timeout_ms = std::chrono::milliseconds{3000},
|
||||||
ProgressCallback callback = nullptr);
|
ProgressCallback callback = nullptr);
|
||||||
~HttpRequest();
|
~HttpRequest();
|
||||||
|
@ -62,5 +67,6 @@ public:
|
||||||
private:
|
private:
|
||||||
class Impl;
|
class Impl;
|
||||||
std::unique_ptr<Impl> m_impl;
|
std::unique_ptr<Impl> m_impl;
|
||||||
|
ProgressCallback m_callback;
|
||||||
};
|
};
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -276,6 +276,8 @@ add_executable(dolphin-mpn
|
||||||
MainWindow.h
|
MainWindow.h
|
||||||
MarioPartyNetplay/DownloadUpdateDialog.cpp
|
MarioPartyNetplay/DownloadUpdateDialog.cpp
|
||||||
MarioPartyNetplay/DownloadUpdateDialog.h
|
MarioPartyNetplay/DownloadUpdateDialog.h
|
||||||
|
MarioPartyNetplay/DownloadWorker.cpp
|
||||||
|
MarioPartyNetplay/Downloadworker.h
|
||||||
MarioPartyNetplay/InstallUpdateDialog.cpp
|
MarioPartyNetplay/InstallUpdateDialog.cpp
|
||||||
MarioPartyNetplay/InstallUpdateDialog.h
|
MarioPartyNetplay/InstallUpdateDialog.h
|
||||||
MarioPartyNetplay/UpdateDialog.cpp
|
MarioPartyNetplay/UpdateDialog.cpp
|
||||||
|
|
|
@ -187,6 +187,7 @@
|
||||||
<ClCompile Include="Main.cpp" />
|
<ClCompile Include="Main.cpp" />
|
||||||
<ClCompile Include="MainWindow.cpp" />
|
<ClCompile Include="MainWindow.cpp" />
|
||||||
<ClCompile Include="MarioPartyNetplay/DownloadUpdateDialog.cpp" />
|
<ClCompile Include="MarioPartyNetplay/DownloadUpdateDialog.cpp" />
|
||||||
|
<ClCompile Include="MarioPartyNetplay/DownloadWorker.cpp" />
|
||||||
<ClCompile Include="MarioPartyNetplay/InstallUpdateDialog.cpp" />
|
<ClCompile Include="MarioPartyNetplay/InstallUpdateDialog.cpp" />
|
||||||
<ClCompile Include="MarioPartyNetplay/UpdateDialog.cpp" />
|
<ClCompile Include="MarioPartyNetplay/UpdateDialog.cpp" />
|
||||||
<ClCompile Include="MenuBar.cpp" />
|
<ClCompile Include="MenuBar.cpp" />
|
||||||
|
@ -405,6 +406,7 @@
|
||||||
<QtMoc Include="InfinityBase/InfinityBaseWindow.h" />
|
<QtMoc Include="InfinityBase/InfinityBaseWindow.h" />
|
||||||
<QtMoc Include="MainWindow.h" />
|
<QtMoc Include="MainWindow.h" />
|
||||||
<QtMoc Include="MarioPartyNetplay/DownloadUpdateDialog.h" />
|
<QtMoc Include="MarioPartyNetplay/DownloadUpdateDialog.h" />
|
||||||
|
<QtMoc Include="MarioPartyNetplay/DownloadWorker.h" />
|
||||||
<QtMoc Include="MarioPartyNetplay/InstallUpdateDialog.h" />
|
<QtMoc Include="MarioPartyNetplay/InstallUpdateDialog.h" />
|
||||||
<QtMoc Include="MarioPartyNetplay/UpdateDialog.h" />
|
<QtMoc Include="MarioPartyNetplay/UpdateDialog.h" />
|
||||||
<QtMoc Include="MenuBar.h" />
|
<QtMoc Include="MenuBar.h" />
|
||||||
|
|
|
@ -4,38 +4,60 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "DownloadUpdateDialog.h"
|
#include "DownloadUpdateDialog.h"
|
||||||
|
#include "InstallUpdateDialog.h"
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QString>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <curl/curl.h>
|
#include <QThread>
|
||||||
#include <iostream>
|
#include "DownloadWorker.h"
|
||||||
|
|
||||||
// Callback function for libcurl to write data
|
|
||||||
size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp)
|
|
||||||
{
|
|
||||||
((std::string*)userp)->append((char*)contents, size * nmemb);
|
|
||||||
return size * nmemb;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Constructor implementation
|
// Constructor implementation
|
||||||
DownloadUpdateDialog::DownloadUpdateDialog(QWidget* parent, const QUrl& url, const QString& filename)
|
DownloadUpdateDialog::DownloadUpdateDialog(QWidget* parent, const QString& url, const QString& filename)
|
||||||
: QDialog(parent), filename(filename)
|
: QDialog(parent), filename(filename)
|
||||||
{
|
{
|
||||||
// Create UI components
|
// Create UI components
|
||||||
|
setWindowTitle(QStringLiteral("Downloading %1").arg(this->filename));
|
||||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
// Create and add label
|
||||||
QLabel* label = new QLabel(QStringLiteral("Downloading %1...").arg(this->filename), this);
|
QLabel* label = new QLabel(QStringLiteral("Downloading %1...").arg(this->filename), this);
|
||||||
progressBar = new QProgressBar(this);
|
|
||||||
|
|
||||||
// Set up the layout
|
|
||||||
layout->addWidget(label);
|
layout->addWidget(label);
|
||||||
|
|
||||||
|
// Create and add progress bar
|
||||||
|
progressBar = new QProgressBar(this);
|
||||||
layout->addWidget(progressBar);
|
layout->addWidget(progressBar);
|
||||||
|
|
||||||
// Set the layout for the dialog
|
// Set the layout for the dialog
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
|
// Set a minimum size for the dialog
|
||||||
|
setMinimumSize(300, 100); // Adjust size as needed
|
||||||
|
|
||||||
// Start the download
|
// Create the worker and thread
|
||||||
startDownload(url);
|
DownloadWorker* worker = new DownloadWorker(url, filename);
|
||||||
|
QThread* thread = new QThread;
|
||||||
|
|
||||||
|
// Move the worker to the thread
|
||||||
|
worker->moveToThread(thread);
|
||||||
|
|
||||||
|
// Connect signals and slots
|
||||||
|
connect(thread, &QThread::started, worker, &DownloadWorker::startDownload, Qt::UniqueConnection);
|
||||||
|
connect(worker, &DownloadWorker::progressUpdated, this, &DownloadUpdateDialog::updateProgress, Qt::UniqueConnection);
|
||||||
|
connect(worker, &DownloadWorker::finished, thread, &QThread::quit, Qt::UniqueConnection);
|
||||||
|
connect(worker, &DownloadWorker::finished, worker, &DownloadWorker::deleteLater, Qt::UniqueConnection);
|
||||||
|
connect(worker, &DownloadWorker::finished, this, &DownloadUpdateDialog::onDownloadFinished, Qt::UniqueConnection);
|
||||||
|
connect(worker, &DownloadWorker::finished, this, &DownloadUpdateDialog::accept, Qt::UniqueConnection);
|
||||||
|
connect(worker, &DownloadWorker::errorOccurred, this, &DownloadUpdateDialog::handleError, Qt::UniqueConnection);
|
||||||
|
connect(thread, &QThread::finished, thread, &QThread::deleteLater, Qt::UniqueConnection);
|
||||||
|
|
||||||
|
// Start the thread
|
||||||
|
thread->start();
|
||||||
|
|
||||||
|
this->exec(); // Show the dialog
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destructor implementation
|
// Destructor implementation
|
||||||
|
@ -43,74 +65,36 @@ DownloadUpdateDialog::~DownloadUpdateDialog()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the temporary directory
|
// Handle errors
|
||||||
QString DownloadUpdateDialog::GetTempDirectory() const
|
void DownloadUpdateDialog::handleError(const QString& errorMsg)
|
||||||
{
|
{
|
||||||
return temporaryDirectory;
|
QMessageBox::critical(this, tr("Error"), errorMsg);
|
||||||
|
reject();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the filename
|
void DownloadUpdateDialog::onDownloadFinished()
|
||||||
QString DownloadUpdateDialog::GetFileName() const
|
|
||||||
{
|
{
|
||||||
return filename;
|
// Use QCoreApplication to get the application's directory path
|
||||||
|
#ifdef _WIN32
|
||||||
|
installationDirectory = QCoreApplication::applicationDirPath(); // Set the installation directory
|
||||||
|
#endif
|
||||||
|
#ifdef __APPLE__
|
||||||
|
installationDirectory = QCoreApplication::applicationDirPath() + "/../../"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Use QStandardPaths to get the system's temporary directory
|
||||||
|
temporaryDirectory = QDir::tempPath();
|
||||||
|
|
||||||
|
InstallUpdateDialog* installDialog =
|
||||||
|
new InstallUpdateDialog(this, installationDirectory, temporaryDirectory, this->filename);
|
||||||
|
installDialog->exec(); // Show the installation dialog modally
|
||||||
|
|
||||||
|
accept();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the progress bar
|
|
||||||
void DownloadUpdateDialog::updateProgress(qint64 size, qint64 total)
|
void DownloadUpdateDialog::updateProgress(qint64 size, qint64 total)
|
||||||
{
|
{
|
||||||
progressBar->setMaximum(total);
|
progressBar->setMaximum(total);
|
||||||
progressBar->setMinimum(0);
|
progressBar->setMinimum(0);
|
||||||
progressBar->setValue(size);
|
progressBar->setValue(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Starts the download process
|
|
||||||
void DownloadUpdateDialog::startDownload(const QUrl& url)
|
|
||||||
{
|
|
||||||
CURL* curl;
|
|
||||||
CURLcode res;
|
|
||||||
std::string readBuffer;
|
|
||||||
|
|
||||||
curl = curl_easy_init();
|
|
||||||
if (curl)
|
|
||||||
{
|
|
||||||
// Convert QUrl to std::string properly
|
|
||||||
std::string urlStr = url.toString().toStdString();
|
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, urlStr.c_str());
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
|
|
||||||
|
|
||||||
// Set the progress function
|
|
||||||
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, [](void* clientp, double totalToDownload, double nowDownloaded, double totalToUpload, double nowUploaded) {
|
|
||||||
DownloadUpdateDialog* dialog = static_cast<DownloadUpdateDialog*>(clientp);
|
|
||||||
dialog->updateProgress(static_cast<qint64>(nowDownloaded), static_cast<qint64>(totalToDownload));
|
|
||||||
return 0; // Return 0 to continue the download
|
|
||||||
});
|
|
||||||
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, this);
|
|
||||||
|
|
||||||
res = curl_easy_perform(curl);
|
|
||||||
if (res != CURLE_OK)
|
|
||||||
{
|
|
||||||
// Use QString::fromStdString() for proper string conversion
|
|
||||||
QString errorMsg = QString::fromStdString(std::string(curl_easy_strerror(res)));
|
|
||||||
QMessageBox::critical(this, tr("Error"), tr("Failed to download update file: %1").arg(errorMsg));
|
|
||||||
reject();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
QFile file(filename);
|
|
||||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
|
|
||||||
{
|
|
||||||
QMessageBox::critical(this, tr("Error"), tr("Failed to open file for writing."));
|
|
||||||
reject();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
file.write(readBuffer.c_str(), readBuffer.size());
|
|
||||||
file.close();
|
|
||||||
accept();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,7 +6,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QUrl>
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QProgressBar>
|
#include <QProgressBar>
|
||||||
|
|
||||||
|
@ -15,17 +14,17 @@ class DownloadUpdateDialog : public QDialog
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DownloadUpdateDialog(QWidget* parent, const QUrl& url, const QString& filename);
|
explicit DownloadUpdateDialog(QWidget* parent, const QString& url, const QString& filename);
|
||||||
~DownloadUpdateDialog();
|
~DownloadUpdateDialog();
|
||||||
|
|
||||||
QString GetTempDirectory() const;
|
public slots:
|
||||||
QString GetFileName() const;
|
void updateProgress(qint64 size, qint64 total);
|
||||||
|
void handleError(const QString& errorMsg); // Ensure this is declared
|
||||||
|
void onDownloadFinished();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString filename; // Member variable for filename
|
QString filename; // Ensure this is declared as a private member
|
||||||
QString temporaryDirectory; // Member variable for temporary directory
|
QProgressBar* progressBar;
|
||||||
QProgressBar* progressBar; // Declare progressBar as a member variable
|
QString installationDirectory;
|
||||||
|
QString temporaryDirectory;
|
||||||
void startDownload(const QUrl& url);
|
|
||||||
void updateProgress(qint64 size, qint64 total); // Method to update progress
|
|
||||||
};
|
};
|
||||||
|
|
50
Source/Core/DolphinQt/MarioPartyNetplay/DownloadWorker.cpp
Normal file
50
Source/Core/DolphinQt/MarioPartyNetplay/DownloadWorker.cpp
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Dolphin for Mario Party Netplay
|
||||||
|
* Copyright (C) 2025 Tabitha Hanegan <tabithahanegan.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DownloadWorker.h"
|
||||||
|
#include "Common/HttpRequest.h"
|
||||||
|
#include <QFile>
|
||||||
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
DownloadWorker::DownloadWorker(const QString& url, const QString& filename)
|
||||||
|
: url(url), filename(filename) {}
|
||||||
|
|
||||||
|
void DownloadWorker::startDownload()
|
||||||
|
{
|
||||||
|
Common::HttpRequest httpRequest;
|
||||||
|
|
||||||
|
// Set the progress callback
|
||||||
|
httpRequest.SetProgressCallback([this](s64 dltotal, s64 dlnow, s64 ultotal, s64 ulnow) {
|
||||||
|
emit progressUpdated(dlnow, dltotal); // Emit progress signal
|
||||||
|
return true; // Continue the download
|
||||||
|
});
|
||||||
|
|
||||||
|
httpRequest.FollowRedirects();
|
||||||
|
|
||||||
|
Common::HttpRequest::Headers headers;
|
||||||
|
headers["User-Agent"] = "Dolphin-MPN/1.0";
|
||||||
|
|
||||||
|
// Perform the GET request
|
||||||
|
auto response = httpRequest.Get(url.toStdString(), headers);
|
||||||
|
|
||||||
|
// Check the response
|
||||||
|
if (response)
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate))
|
||||||
|
{
|
||||||
|
emit errorOccurred(QStringLiteral("Failed to open file for writing."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QByteArray byteArray(reinterpret_cast<const char*>(response->data()), response->size());
|
||||||
|
file.write(byteArray);
|
||||||
|
file.close();
|
||||||
|
emit finished(); // Emit finished signal
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emit errorOccurred(QStringLiteral("Failed to download update file.")); // This should also work
|
||||||
|
}
|
||||||
|
}
|
27
Source/Core/DolphinQt/MarioPartyNetplay/DownloadWorker.h
Normal file
27
Source/Core/DolphinQt/MarioPartyNetplay/DownloadWorker.h
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* Dolphin for Mario Party Netplay
|
||||||
|
* Copyright (C) 2025 Tabitha Hanegan <tabithahanegan.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class DownloadWorker : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
DownloadWorker(const QString& url, const QString& filename);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void startDownload();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void progressUpdated(qint64 size, qint64 total);
|
||||||
|
void finished();
|
||||||
|
void errorOccurred(const QString& errorMsg);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString url;
|
||||||
|
QString filename;
|
||||||
|
};
|
|
@ -9,21 +9,26 @@
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
#include <QDialog>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QProgressBar>
|
#include <QProgressBar>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <mz_compat.h>
|
#include "Common/MinizipUtil.h"
|
||||||
|
#include <Common/Logging/Log.h>
|
||||||
|
|
||||||
// Constructor implementation
|
// Constructor implementation
|
||||||
InstallUpdateDialog::InstallUpdateDialog(QWidget *parent, QString installationDirectory, QString temporaryDirectory, QString filename)
|
InstallUpdateDialog::InstallUpdateDialog(QWidget *parent, QString installationDirectory, QString temporaryDirectory, QString filename)
|
||||||
: DownloadUpdateDialog(parent, QUrl(), filename),
|
: QDialog(parent), // Only pass the parent
|
||||||
installationDirectory(installationDirectory),
|
installationDirectory(installationDirectory),
|
||||||
temporaryDirectory(temporaryDirectory)
|
temporaryDirectory(temporaryDirectory),
|
||||||
|
filename(filename) // Initialize member variables
|
||||||
{
|
{
|
||||||
|
setWindowTitle(QStringLiteral("Installing %1...").arg(this->filename));
|
||||||
|
|
||||||
// Create UI components
|
// Create UI components
|
||||||
QVBoxLayout* layout = new QVBoxLayout(this);
|
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||||
QLabel* label = new QLabel(QStringLiteral("Installing %1...").arg(this->filename), this);
|
label = new QLabel(QStringLiteral("Installing %1...").arg(this->filename), this);
|
||||||
QProgressBar* progressBar = new QProgressBar(this);
|
progressBar = new QProgressBar(this);
|
||||||
|
|
||||||
// Set up the layout
|
// Set up the layout
|
||||||
layout->addWidget(label);
|
layout->addWidget(label);
|
||||||
|
@ -41,7 +46,7 @@ InstallUpdateDialog::~InstallUpdateDialog(void)
|
||||||
|
|
||||||
void InstallUpdateDialog::install(void)
|
void InstallUpdateDialog::install(void)
|
||||||
{
|
{
|
||||||
QString fullFilePath = this->temporaryDirectory + QStringLiteral("/") + this->filename;
|
QString fullFilePath = QCoreApplication::applicationDirPath() + QStringLiteral("/") + this->filename;
|
||||||
QString appPath = QCoreApplication::applicationDirPath();
|
QString appPath = QCoreApplication::applicationDirPath();
|
||||||
QString appPid = QString::number(QCoreApplication::applicationPid());
|
QString appPid = QString::number(QCoreApplication::applicationPid());
|
||||||
|
|
||||||
|
@ -76,21 +81,28 @@ void InstallUpdateDialog::install(void)
|
||||||
this->label->setText(QStringLiteral("Extracting %1...").arg(this->filename));
|
this->label->setText(QStringLiteral("Extracting %1...").arg(this->filename));
|
||||||
this->progressBar->setValue(50);
|
this->progressBar->setValue(50);
|
||||||
|
|
||||||
|
QString extractDirectory = this->temporaryDirectory + QDir::separator() + QStringLiteral("Dolphin-MPN");
|
||||||
|
|
||||||
|
// Ensure the extract directory exists before attempting to unzip
|
||||||
QDir dir(this->temporaryDirectory);
|
QDir dir(this->temporaryDirectory);
|
||||||
if (!dir.mkdir(QStringLiteral("extract")))
|
if (!QDir(extractDirectory).exists())
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, QStringLiteral("Error"), QStringLiteral("Failed to create extract directory."));
|
if (!dir.mkdir(QStringLiteral("Dolphin-MPN")))
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, QStringLiteral("Error"),
|
||||||
|
QStringLiteral("Failed to create extract directory."));
|
||||||
this->reject();
|
this->reject();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString extractDirectory = this->temporaryDirectory + QStringLiteral("/extract");
|
// Attempt to unzip files into the extract directory
|
||||||
|
|
||||||
if (!unzipFile(fullFilePath.toStdString(), extractDirectory.toStdString()))
|
if (!unzipFile(fullFilePath.toStdString(), extractDirectory.toStdString()))
|
||||||
{
|
{
|
||||||
QMessageBox::critical(this, QStringLiteral("Error"), QStringLiteral("Unzip failed: Unable to extract files."));
|
QMessageBox::critical(this, QStringLiteral("Error"),
|
||||||
this->reject();
|
QStringLiteral("Unzip failed: Unable to extract files."));
|
||||||
return;
|
this->reject();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->label->setText(QStringLiteral("Executing update script..."));
|
this->label->setText(QStringLiteral("Executing update script..."));
|
||||||
|
@ -122,64 +134,65 @@ void InstallUpdateDialog::install(void)
|
||||||
|
|
||||||
bool InstallUpdateDialog::unzipFile(const std::string& zipFilePath, const std::string& destDir)
|
bool InstallUpdateDialog::unzipFile(const std::string& zipFilePath, const std::string& destDir)
|
||||||
{
|
{
|
||||||
unzFile zipFile = unzOpen(zipFilePath.c_str());
|
unzFile zipFile = unzOpen(zipFilePath.c_str());
|
||||||
if (!zipFile)
|
if (!zipFile)
|
||||||
{
|
{
|
||||||
return false; // Failed to open zip file
|
return false; // Failed to open zip file
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unzGoToFirstFile(zipFile) != UNZ_OK)
|
|
||||||
{
|
|
||||||
unzClose(zipFile);
|
|
||||||
return false; // No files in zip
|
|
||||||
}
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
char filename[256];
|
|
||||||
unz_file_info fileInfo;
|
|
||||||
if (unzGetCurrentFileInfo(zipFile, &fileInfo, filename, sizeof(filename), nullptr, 0, nullptr, 0) != UNZ_OK)
|
|
||||||
{
|
|
||||||
unzClose(zipFile);
|
|
||||||
return false; // Failed to get file info
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create full path for the extracted file using std::string concatenation
|
|
||||||
std::string fullPath = destDir + "/" + std::string(filename);
|
|
||||||
|
|
||||||
// Create directories if needed
|
|
||||||
QString qFullPath = QString::fromStdString(fullPath);
|
|
||||||
QDir().mkpath(QFileInfo(qFullPath).path());
|
|
||||||
|
|
||||||
// Open the file in the zip
|
|
||||||
if (unzOpenCurrentFile(zipFile) != UNZ_OK)
|
|
||||||
{
|
|
||||||
unzClose(zipFile);
|
|
||||||
return false; // Failed to open file in zip
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the file to disk
|
|
||||||
QFile outFile(qFullPath);
|
|
||||||
if (!outFile.open(QIODevice::WriteOnly))
|
|
||||||
{
|
|
||||||
unzCloseCurrentFile(zipFile);
|
|
||||||
unzClose(zipFile);
|
|
||||||
return false; // Failed to create output file
|
|
||||||
}
|
|
||||||
|
|
||||||
char buffer[8192];
|
|
||||||
int bytesRead;
|
|
||||||
while ((bytesRead = unzReadCurrentFile(zipFile, buffer, sizeof(buffer))) > 0)
|
|
||||||
{
|
|
||||||
outFile.write(buffer, bytesRead);
|
|
||||||
}
|
|
||||||
|
|
||||||
outFile.close();
|
|
||||||
unzCloseCurrentFile(zipFile);
|
|
||||||
} while (unzGoToNextFile(zipFile) == UNZ_OK);
|
|
||||||
|
|
||||||
|
if (unzGoToFirstFile(zipFile) != UNZ_OK)
|
||||||
|
{
|
||||||
unzClose(zipFile);
|
unzClose(zipFile);
|
||||||
return true; // Successfully unzipped all files
|
return false; // No files in zip
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
char filename[256];
|
||||||
|
unz_file_info fileInfo;
|
||||||
|
if (unzGetCurrentFileInfo(zipFile, &fileInfo, filename, sizeof(filename), nullptr, 0, nullptr,
|
||||||
|
0) != UNZ_OK)
|
||||||
|
{
|
||||||
|
unzClose(zipFile);
|
||||||
|
return false; // Failed to get file info
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create full path for the extracted file
|
||||||
|
std::string fullPath = destDir + "/" + std::string(filename);
|
||||||
|
QString qFullPath = QString::fromStdString(fullPath);
|
||||||
|
|
||||||
|
// Handle directories
|
||||||
|
if (filename[std::strlen(filename) - 1] == '/')
|
||||||
|
{
|
||||||
|
QDir().mkpath(qFullPath);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the directory structure exists
|
||||||
|
QDir().mkpath(QFileInfo(qFullPath).path());
|
||||||
|
|
||||||
|
// Prepare a buffer to store file data
|
||||||
|
std::vector<u8> fileData(fileInfo.uncompressed_size);
|
||||||
|
if (!Common::ReadFileFromZip(zipFile, &fileData))
|
||||||
|
{
|
||||||
|
unzClose(zipFile);
|
||||||
|
return false; // Failed to read file from zip
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the file data to disk
|
||||||
|
QFile outFile(qFullPath);
|
||||||
|
if (!outFile.open(QIODevice::WriteOnly))
|
||||||
|
{
|
||||||
|
unzClose(zipFile);
|
||||||
|
return false; // Failed to create output file
|
||||||
|
}
|
||||||
|
|
||||||
|
outFile.write(reinterpret_cast<const char*>(fileData.data()), fileData.size());
|
||||||
|
outFile.close();
|
||||||
|
} while (unzGoToNextFile(zipFile) == UNZ_OK);
|
||||||
|
|
||||||
|
unzClose(zipFile);
|
||||||
|
return true; // Successfully unzipped all files
|
||||||
}
|
}
|
||||||
|
|
||||||
void InstallUpdateDialog::writeAndRunScript(QStringList stringList)
|
void InstallUpdateDialog::writeAndRunScript(QStringList stringList)
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
* Copyright (C) 2025 Tabitha Hanegan <tabithahanegan.com>
|
* Copyright (C) 2025 Tabitha Hanegan <tabithahanegan.com>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "DownloadUpdateDialog.h"
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
|
@ -16,7 +15,7 @@ class QLabel;
|
||||||
class QProgressBar;
|
class QProgressBar;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
class InstallUpdateDialog : public DownloadUpdateDialog
|
class InstallUpdateDialog : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QTemporaryDir>
|
#include <QTemporaryDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
@ -16,9 +17,11 @@
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
|
#include <QWidget>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
|
#include "../../Common/Logging/Log.h"
|
||||||
|
|
||||||
using namespace UserInterface::Dialog;
|
using namespace UserInterface::Dialog;
|
||||||
|
|
||||||
|
@ -62,51 +65,43 @@ UpdateDialog::~UpdateDialog()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
QString UpdateDialog::GetFileName()
|
|
||||||
{
|
|
||||||
return this->filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
QUrl UpdateDialog::GetUrl()
|
|
||||||
{
|
|
||||||
return this->url;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdateDialog::accept()
|
void UpdateDialog::accept()
|
||||||
{
|
{
|
||||||
QJsonArray jsonArray = jsonObject[QStringLiteral("assets")].toArray();
|
QJsonArray jsonArray = jsonObject[QStringLiteral("assets")].toArray();
|
||||||
QString filenameToDownload;
|
QString filenameToDownload;
|
||||||
QUrl urlToDownload;
|
QString urlToDownload;
|
||||||
|
|
||||||
for (const QJsonValue& value : jsonArray)
|
for (const QJsonValue& value : jsonArray)
|
||||||
{
|
{
|
||||||
QJsonObject object = value.toObject();
|
QJsonObject object = value.toObject();
|
||||||
|
|
||||||
QString filename = object.value(QStringLiteral("name")).toString();
|
QString filenameBlob = object.value(QStringLiteral("name")).toString();
|
||||||
QUrl url(object.value(QStringLiteral("browser_download_url")).toString());
|
QString downloadUrl(object.value(QStringLiteral("browser_download_url")).toString());
|
||||||
|
|
||||||
if (QSysInfo::productType() == QStringLiteral("windows")) {
|
#ifdef _WIN32
|
||||||
if (filename.contains(QStringLiteral("win32")) ||
|
if (filenameBlob.contains(QStringLiteral("win32")) ||
|
||||||
filename.contains(QStringLiteral("windows")) ||
|
filenameBlob.contains(QStringLiteral("windows")) ||
|
||||||
filename.contains(QStringLiteral("win64")))
|
filenameBlob.contains(QStringLiteral("win64")))
|
||||||
{
|
{
|
||||||
filenameToDownload = filename;
|
filenameToDownload = filenameBlob;
|
||||||
urlToDownload = url;
|
urlToDownload = downloadUrl;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (QSysInfo::productType() == QStringLiteral("macos")) {
|
#endif
|
||||||
if (filename.contains(QStringLiteral("darwin")) ||
|
#ifdef __APPLE__
|
||||||
filename.contains(QStringLiteral("macOS")))
|
if (filenameBlob.contains(QStringLiteral("darwin")) ||
|
||||||
{
|
filenameBlob.contains(QStringLiteral("macOS")))
|
||||||
filenameToDownload = filename;
|
{
|
||||||
urlToDownload = url;
|
filenameToDownload = filenameBlob;
|
||||||
break;
|
urlToDownload = downloadUrl;
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
this->url = urlToDownload;
|
this->url = urlToDownload;
|
||||||
this->filename = filenameToDownload;
|
this->filename = filenameToDownload;
|
||||||
QDialog::accept();
|
QDialog::accept();
|
||||||
|
|
||||||
|
DownloadUpdateDialog downloadDialog(this, urlToDownload, filenameToDownload);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QUrl>
|
#include <QString>
|
||||||
|
|
||||||
class QLabel;
|
class QLabel;
|
||||||
class QTextEdit;
|
class QTextEdit;
|
||||||
|
@ -24,7 +24,7 @@ class UpdateDialog : public QDialog
|
||||||
private:
|
private:
|
||||||
QJsonObject jsonObject;
|
QJsonObject jsonObject;
|
||||||
QString filename;
|
QString filename;
|
||||||
QUrl url;
|
QString url;
|
||||||
|
|
||||||
// UI elements
|
// UI elements
|
||||||
QLabel* label;
|
QLabel* label;
|
||||||
|
@ -38,12 +38,9 @@ private:
|
||||||
public:
|
public:
|
||||||
explicit UpdateDialog(QWidget *parent, QJsonObject jsonObject, bool forced);
|
explicit UpdateDialog(QWidget *parent, QJsonObject jsonObject, bool forced);
|
||||||
~UpdateDialog();
|
~UpdateDialog();
|
||||||
|
|
||||||
QString GetFileName();
|
|
||||||
QUrl GetUrl();
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void accept() override;
|
void accept(void) Q_DECL_OVERRIDE;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Dialog
|
} // namespace Dialog
|
||||||
|
|
|
@ -620,26 +620,31 @@ void MenuBar::AddHelpMenu()
|
||||||
{
|
{
|
||||||
QMenu* help_menu = addMenu(tr("&Help"));
|
QMenu* help_menu = addMenu(tr("&Help"));
|
||||||
|
|
||||||
QAction* website = help_menu->addAction(tr("&Website"));
|
QAction* website = help_menu->addAction(tr("&Dolphin Website"));
|
||||||
|
|
||||||
QAction* updaterCheck = help_menu->addAction(tr("Check For &Updates"));
|
|
||||||
connect(updaterCheck, &QAction::triggered, this, &MenuBar::ShowUpdateDialog);
|
|
||||||
|
|
||||||
connect(website, &QAction::triggered, this,
|
connect(website, &QAction::triggered, this,
|
||||||
[]() { QDesktopServices::openUrl(QUrl(QStringLiteral("https://dolphin-emu.org/"))); });
|
[]() { QDesktopServices::openUrl(QUrl(QStringLiteral("https://dolphin-emu.org/"))); });
|
||||||
QAction* documentation = help_menu->addAction(tr("Online &Documentation"));
|
help_menu->addSeparator();
|
||||||
connect(documentation, &QAction::triggered, this, []() {
|
QAction* discord = help_menu->addAction(tr("&MPN Discord"));
|
||||||
QDesktopServices::openUrl(QUrl(QStringLiteral("https://dolphin-emu.org/docs/guides")));
|
connect(discord, &QAction::triggered, this,
|
||||||
});
|
[]() { QDesktopServices::openUrl(QUrl(QStringLiteral("https://discord.gg/marioparty/"))); });
|
||||||
QAction* github = help_menu->addAction(tr("&GitHub Repository"));
|
QAction* github = help_menu->addAction(tr("&GitHub Repository"));
|
||||||
connect(github, &QAction::triggered, this, []() {
|
connect(github, &QAction::triggered, this, []() {
|
||||||
QDesktopServices::openUrl(QUrl(QStringLiteral("https://github.com/MarioPartyNetplay/Dolphin-MPN")));
|
QDesktopServices::openUrl(QUrl(QStringLiteral("https://github.com/MarioPartyNetplay/Dolphin-MPN")));
|
||||||
});
|
});
|
||||||
|
QAction* documentation = help_menu->addAction(tr("&How-to Netplay"));
|
||||||
|
connect(documentation, &QAction::triggered, this, []() {
|
||||||
|
QDesktopServices::openUrl(
|
||||||
|
QUrl(QStringLiteral("https://rentry.org/mario-party-netplay-gcn-wii")));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
help_menu->addSeparator();
|
help_menu->addSeparator();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
QAction* updaterCheck = help_menu->addAction(tr("Check For &Updates"));
|
||||||
|
connect(updaterCheck, &QAction::triggered, this, &MenuBar::ShowUpdateDialog);
|
||||||
|
|
||||||
help_menu->addAction(tr("&About"), this, &MenuBar::ShowAboutDialog);
|
help_menu->addAction(tr("&About"), this, &MenuBar::ShowAboutDialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue