mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-08-03 06:39:33 +00:00
initial updater work
This commit is contained in:
parent
df2bbe1ee3
commit
e37df05d75
17 changed files with 657 additions and 11 deletions
|
@ -239,10 +239,11 @@ HttpRequest::Response HttpRequest::Impl::Fetch(const std::string& url, Method me
|
||||||
m_response_headers.clear();
|
m_response_headers.clear();
|
||||||
curl_easy_setopt(m_curl.get(), CURLOPT_POST, method == Method::POST);
|
curl_easy_setopt(m_curl.get(), CURLOPT_POST, method == Method::POST);
|
||||||
curl_easy_setopt(m_curl.get(), CURLOPT_URL, url.c_str());
|
curl_easy_setopt(m_curl.get(), CURLOPT_URL, url.c_str());
|
||||||
|
|
||||||
if (method == Method::POST && multiform.empty())
|
if (method == Method::POST && multiform.empty())
|
||||||
{
|
{
|
||||||
curl_easy_setopt(m_curl.get(), CURLOPT_POSTFIELDS, payload);
|
curl_easy_setopt(m_curl.get(), CURLOPT_POSTFIELDS, payload);
|
||||||
curl_easy_setopt(m_curl.get(), CURLOPT_POSTFIELDSIZE, size);
|
curl_easy_setopt(m_curl.get(), CURLOPT_POSTFIELDSIZE, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_mime* form = nullptr;
|
curl_mime* form = nullptr;
|
||||||
|
@ -271,6 +272,9 @@ HttpRequest::Response HttpRequest::Impl::Fetch(const std::string& url, Method me
|
||||||
else
|
else
|
||||||
list = curl_slist_append(list, (name + ": " + *value).c_str());
|
list = curl_slist_append(list, (name + ": " + *value).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list = curl_slist_append(list, "User-Agent: Dolphin-MPN/1.0");
|
||||||
|
|
||||||
curl_easy_setopt(m_curl.get(), CURLOPT_HTTPHEADER, list);
|
curl_easy_setopt(m_curl.get(), CURLOPT_HTTPHEADER, list);
|
||||||
|
|
||||||
curl_easy_setopt(m_curl.get(), CURLOPT_HEADERFUNCTION, header_callback);
|
curl_easy_setopt(m_curl.get(), CURLOPT_HEADERFUNCTION, header_callback);
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Dolphin for Mario Party Netplay
|
||||||
|
* Copyright (C) 2025 Tabitha Hanegan <tabithahanegan.com>
|
||||||
|
*/
|
||||||
|
|
||||||
#include "Discord.h"
|
#include "Discord.h"
|
||||||
#include <Core/State.h>
|
#include <Core/State.h>
|
||||||
#include "Core/Config/NetplaySettings.h"
|
#include "Core/Config/NetplaySettings.h"
|
||||||
|
@ -8,9 +13,6 @@
|
||||||
|
|
||||||
bool mpn_update_discord()
|
bool mpn_update_discord()
|
||||||
{
|
{
|
||||||
// if (!Memory::IsInitialized())
|
|
||||||
// return false;
|
|
||||||
|
|
||||||
DiscordRichPresence RichPresence = {};
|
DiscordRichPresence RichPresence = {};
|
||||||
|
|
||||||
RichPresence.largeImageKey = CurrentState.Image ? CurrentState.Image : "default";
|
RichPresence.largeImageKey = CurrentState.Image ? CurrentState.Image : "default";
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Dolphin for Mario Party Netplay
|
||||||
|
* Copyright (C) 2025 Tabitha Hanegan <tabithahanegan.com>
|
||||||
|
*/
|
||||||
|
|
||||||
#include <discord_rpc.h>
|
#include <discord_rpc.h>
|
||||||
#include "Gamestate.h"
|
#include "Gamestate.h"
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Dolphin for Mario Party Netplay
|
||||||
|
* Copyright (C) 2025 Tabitha Hanegan <tabithahanegan.com>
|
||||||
|
*/
|
||||||
|
|
||||||
#include "Gamestate.h"
|
#include "Gamestate.h"
|
||||||
#include "Core/System.h"
|
#include "Core/System.h"
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Dolphin for Mario Party Netplay
|
||||||
|
* Copyright (C) 2025 Tabitha Hanegan <tabithahanegan.com>
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef MPN_GAMESTATE_H
|
#ifndef MPN_GAMESTATE_H
|
||||||
#define MPN_GAMESTATE_H
|
#define MPN_GAMESTATE_H
|
||||||
|
|
||||||
|
|
|
@ -274,6 +274,12 @@ add_executable(dolphin-mpn
|
||||||
Main.cpp
|
Main.cpp
|
||||||
MainWindow.cpp
|
MainWindow.cpp
|
||||||
MainWindow.h
|
MainWindow.h
|
||||||
|
MarioPartyNetplay/DownloadUpdateDialog.cpp
|
||||||
|
MarioPartyNetplay/DownloadUpdateDialog.h
|
||||||
|
MarioPartyNetplay/InstallUpdateDialog.cpp
|
||||||
|
MarioPartyNetplay/InstallUpdateDialog.h
|
||||||
|
MarioPartyNetplay/UpdateDialog.cpp
|
||||||
|
MarioPartyNetplay/UpdateDialog.h
|
||||||
MenuBar.cpp
|
MenuBar.cpp
|
||||||
MenuBar.h
|
MenuBar.h
|
||||||
NetPlay/ChunkedProgressDialog.cpp
|
NetPlay/ChunkedProgressDialog.cpp
|
||||||
|
|
|
@ -186,6 +186,9 @@
|
||||||
<ClCompile Include="InfinityBase/InfinityBaseWindow.cpp" />
|
<ClCompile Include="InfinityBase/InfinityBaseWindow.cpp" />
|
||||||
<ClCompile Include="Main.cpp" />
|
<ClCompile Include="Main.cpp" />
|
||||||
<ClCompile Include="MainWindow.cpp" />
|
<ClCompile Include="MainWindow.cpp" />
|
||||||
|
<ClCompile Include="MarioPartyNetplay/DownloadUpdateDialog.cpp" />
|
||||||
|
<ClCompile Include="MarioPartyNetplay/InstallUpdateDialog.cpp" />
|
||||||
|
<ClCompile Include="MarioPartyNetplay/UpdateDialog.cpp" />
|
||||||
<ClCompile Include="MenuBar.cpp" />
|
<ClCompile Include="MenuBar.cpp" />
|
||||||
<ClCompile Include="NetPlay\ChunkedProgressDialog.cpp" />
|
<ClCompile Include="NetPlay\ChunkedProgressDialog.cpp" />
|
||||||
<ClCompile Include="NetPlay\GameDigestDialog.cpp" />
|
<ClCompile Include="NetPlay\GameDigestDialog.cpp" />
|
||||||
|
@ -401,6 +404,9 @@
|
||||||
<QtMoc Include="HotkeyScheduler.h" />
|
<QtMoc Include="HotkeyScheduler.h" />
|
||||||
<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/InstallUpdateDialog.h" />
|
||||||
|
<QtMoc Include="MarioPartyNetplay/UpdateDialog.h" />
|
||||||
<QtMoc Include="MenuBar.h" />
|
<QtMoc Include="MenuBar.h" />
|
||||||
<QtMoc Include="NetPlay\ChunkedProgressDialog.h" />
|
<QtMoc Include="NetPlay\ChunkedProgressDialog.h" />
|
||||||
<QtMoc Include="NetPlay\GameDigestDialog.h" />
|
<QtMoc Include="NetPlay\GameDigestDialog.h" />
|
||||||
|
@ -481,6 +487,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(ExternalsDir)bzip2\exports.props" />
|
<Import Project="$(ExternalsDir)bzip2\exports.props" />
|
||||||
<Import Project="$(ExternalsDir)cpp-optparse\exports.props" />
|
<Import Project="$(ExternalsDir)cpp-optparse\exports.props" />
|
||||||
|
<Import Project="$(ExternalsDir)curl\exports.props" />
|
||||||
<Import Project="$(ExternalsDir)discord-rpc\exports.props" />
|
<Import Project="$(ExternalsDir)discord-rpc\exports.props" />
|
||||||
<Import Project="$(ExternalsDir)enet\exports.props" />
|
<Import Project="$(ExternalsDir)enet\exports.props" />
|
||||||
<Import Project="$(ExternalsDir)fmt\exports.props" />
|
<Import Project="$(ExternalsDir)fmt\exports.props" />
|
||||||
|
@ -489,6 +496,7 @@
|
||||||
<Import Project="$(ExternalsDir)liblzma\exports.props" />
|
<Import Project="$(ExternalsDir)liblzma\exports.props" />
|
||||||
<Import Project="$(ExternalsDir)mbedtls\exports.props" />
|
<Import Project="$(ExternalsDir)mbedtls\exports.props" />
|
||||||
<Import Project="$(ExternalsDir)mGBA\exports.props" />
|
<Import Project="$(ExternalsDir)mGBA\exports.props" />
|
||||||
|
<Import Project="$(ExternalsDir)minizip-ng\exports.props" />
|
||||||
<Import Project="$(ExternalsDir)picojson\exports.props" />
|
<Import Project="$(ExternalsDir)picojson\exports.props" />
|
||||||
<Import Project="$(ExternalsDir)rcheevos\exports.props" />
|
<Import Project="$(ExternalsDir)rcheevos\exports.props" />
|
||||||
<Import Project="$(ExternalsDir)SFML\exports.props" />
|
<Import Project="$(ExternalsDir)SFML\exports.props" />
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
#include <QStyleHints>
|
#include <QStyleHints>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
@ -37,6 +40,9 @@
|
||||||
#include "Common/ScopeGuard.h"
|
#include "Common/ScopeGuard.h"
|
||||||
#include "Common/Version.h"
|
#include "Common/Version.h"
|
||||||
#include "Common/WindowSystemInfo.h"
|
#include "Common/WindowSystemInfo.h"
|
||||||
|
#include "Common/HttpRequest.h"
|
||||||
|
#include "Common/ScopeGuard.h"
|
||||||
|
#include "Common/StringUtil.h"
|
||||||
|
|
||||||
#include "Core/AchievementManager.h"
|
#include "Core/AchievementManager.h"
|
||||||
#include "Core/Boot/Boot.h"
|
#include "Core/Boot/Boot.h"
|
||||||
|
@ -101,6 +107,9 @@
|
||||||
#include "DolphinQt/HotkeyScheduler.h"
|
#include "DolphinQt/HotkeyScheduler.h"
|
||||||
#include "DolphinQt/InfinityBase/InfinityBaseWindow.h"
|
#include "DolphinQt/InfinityBase/InfinityBaseWindow.h"
|
||||||
#include "DolphinQt/MenuBar.h"
|
#include "DolphinQt/MenuBar.h"
|
||||||
|
#include "DolphinQt/MarioPartyNetplay/DownloadUpdateDialog.h"
|
||||||
|
#include "DolphinQt/MarioPartyNetplay/InstallUpdateDialog.h"
|
||||||
|
#include "DolphinQt/MarioPartyNetplay/UpdateDialog.h"
|
||||||
#include "DolphinQt/NKitWarningDialog.h"
|
#include "DolphinQt/NKitWarningDialog.h"
|
||||||
#include "DolphinQt/NetPlay/NetPlayBrowser.h"
|
#include "DolphinQt/NetPlay/NetPlayBrowser.h"
|
||||||
#include "DolphinQt/NetPlay/NetPlayDialog.h"
|
#include "DolphinQt/NetPlay/NetPlayDialog.h"
|
||||||
|
@ -601,6 +610,7 @@ void MainWindow::ConnectMenuBar()
|
||||||
&GameList::OnGameListVisibilityChanged);
|
&GameList::OnGameListVisibilityChanged);
|
||||||
|
|
||||||
connect(m_menu_bar, &MenuBar::ShowAboutDialog, this, &MainWindow::ShowAboutDialog);
|
connect(m_menu_bar, &MenuBar::ShowAboutDialog, this, &MainWindow::ShowAboutDialog);
|
||||||
|
connect(m_menu_bar, &MenuBar::ShowUpdateDialog, this, &MainWindow::ShowUpdateDialog);
|
||||||
|
|
||||||
connect(m_game_list, &GameList::SelectionChanged, m_menu_bar, &MenuBar::SelectionChanged);
|
connect(m_game_list, &GameList::SelectionChanged, m_menu_bar, &MenuBar::SelectionChanged);
|
||||||
connect(this, &MainWindow::ReadOnlyModeChanged, m_menu_bar, &MenuBar::ReadOnlyModeChanged);
|
connect(this, &MainWindow::ReadOnlyModeChanged, m_menu_bar, &MenuBar::ReadOnlyModeChanged);
|
||||||
|
@ -1320,6 +1330,35 @@ void MainWindow::ShowAboutDialog()
|
||||||
about.exec();
|
about.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::ShowUpdateDialog()
|
||||||
|
{
|
||||||
|
Common::HttpRequest httpRequest;
|
||||||
|
|
||||||
|
// Make the GET request
|
||||||
|
auto response = httpRequest.Get("https://api.github.com/repos/MarioPartyNetplay/Dolphin-MPN/releases/latest");
|
||||||
|
|
||||||
|
if (response)
|
||||||
|
{
|
||||||
|
// Access the underlying vector and convert it to QByteArray
|
||||||
|
QByteArray responseData(reinterpret_cast<const char*>(response->data()), response->size());
|
||||||
|
|
||||||
|
// Parse the JSON response
|
||||||
|
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData);
|
||||||
|
QJsonObject jsonObject = jsonDoc.object();
|
||||||
|
|
||||||
|
// Create and show the UpdateDialog with the fetched data
|
||||||
|
bool forced = false; // Set this based on your logic
|
||||||
|
UserInterface::Dialog::UpdateDialog updater(this, jsonObject, forced);
|
||||||
|
SetQWidgetWindowDecorations(&updater);
|
||||||
|
updater.exec();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Handle error
|
||||||
|
QMessageBox::critical(this, tr("Error"), tr("Failed to fetch update information."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::ShowHotkeyDialog()
|
void MainWindow::ShowHotkeyDialog()
|
||||||
{
|
{
|
||||||
if (!m_hotkey_window)
|
if (!m_hotkey_window)
|
||||||
|
|
|
@ -169,6 +169,7 @@ private:
|
||||||
void ShowGraphicsWindow();
|
void ShowGraphicsWindow();
|
||||||
void ShowFreeLookWindow();
|
void ShowFreeLookWindow();
|
||||||
void ShowAboutDialog();
|
void ShowAboutDialog();
|
||||||
|
void ShowUpdateDialog();
|
||||||
void ShowHotkeyDialog();
|
void ShowHotkeyDialog();
|
||||||
void ShowNetPlaySetupDialog();
|
void ShowNetPlaySetupDialog();
|
||||||
void ShowNetPlayBrowser();
|
void ShowNetPlayBrowser();
|
||||||
|
|
116
Source/Core/DolphinQt/MarioPartyNetplay/DownloadUpdateDialog.cpp
Normal file
116
Source/Core/DolphinQt/MarioPartyNetplay/DownloadUpdateDialog.cpp
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
/*
|
||||||
|
* Dolphin for Mario Party Netplay
|
||||||
|
* Copyright (C) 2025 Tabitha Hanegan <tabithahanegan.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DownloadUpdateDialog.h"
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
// 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
|
||||||
|
DownloadUpdateDialog::DownloadUpdateDialog(QWidget* parent, const QUrl& url, const QString& filename)
|
||||||
|
: QDialog(parent), filename(filename)
|
||||||
|
{
|
||||||
|
// Create UI components
|
||||||
|
QVBoxLayout* layout = new QVBoxLayout(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(progressBar);
|
||||||
|
|
||||||
|
// Set the layout for the dialog
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
// Start the download
|
||||||
|
startDownload(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destructor implementation
|
||||||
|
DownloadUpdateDialog::~DownloadUpdateDialog()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the temporary directory
|
||||||
|
QString DownloadUpdateDialog::GetTempDirectory() const
|
||||||
|
{
|
||||||
|
return temporaryDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the filename
|
||||||
|
QString DownloadUpdateDialog::GetFileName() const
|
||||||
|
{
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the progress bar
|
||||||
|
void DownloadUpdateDialog::updateProgress(qint64 size, qint64 total)
|
||||||
|
{
|
||||||
|
progressBar->setMaximum(total);
|
||||||
|
progressBar->setMinimum(0);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Dolphin for Mario Party Netplay
|
||||||
|
* Copyright (C) 2025 Tabitha Hanegan <tabithahanegan.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QString>
|
||||||
|
#include <QProgressBar>
|
||||||
|
|
||||||
|
class DownloadUpdateDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
DownloadUpdateDialog(QWidget* parent, const QUrl& url, const QString& filename);
|
||||||
|
~DownloadUpdateDialog();
|
||||||
|
|
||||||
|
QString GetTempDirectory() const;
|
||||||
|
QString GetFileName() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString filename; // Member variable for filename
|
||||||
|
QString temporaryDirectory; // Member variable for temporary directory
|
||||||
|
QProgressBar* progressBar; // Declare progressBar as a member variable
|
||||||
|
|
||||||
|
void startDownload(const QUrl& url);
|
||||||
|
void updateProgress(qint64 size, qint64 total); // Method to update progress
|
||||||
|
};
|
222
Source/Core/DolphinQt/MarioPartyNetplay/InstallUpdateDialog.cpp
Normal file
222
Source/Core/DolphinQt/MarioPartyNetplay/InstallUpdateDialog.cpp
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
/*
|
||||||
|
* Dolphin for Mario Party Netplay
|
||||||
|
* Copyright (C) 2025 Tabitha Hanegan <tabithahanegan.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "InstallUpdateDialog.h"
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QProgressBar>
|
||||||
|
#include <QMessageBox>
|
||||||
|
#include <mz_compat.h>
|
||||||
|
|
||||||
|
// Constructor implementation
|
||||||
|
InstallUpdateDialog::InstallUpdateDialog(QWidget *parent, QString installationDirectory, QString temporaryDirectory, QString filename)
|
||||||
|
: DownloadUpdateDialog(parent, QUrl(), filename),
|
||||||
|
installationDirectory(installationDirectory),
|
||||||
|
temporaryDirectory(temporaryDirectory)
|
||||||
|
{
|
||||||
|
// Create UI components
|
||||||
|
QVBoxLayout* layout = new QVBoxLayout(this);
|
||||||
|
QLabel* label = new QLabel(QStringLiteral("Installing %1...").arg(this->filename), this);
|
||||||
|
QProgressBar* progressBar = new QProgressBar(this);
|
||||||
|
|
||||||
|
// Set up the layout
|
||||||
|
layout->addWidget(label);
|
||||||
|
layout->addWidget(progressBar);
|
||||||
|
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
startTimer(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destructor implementation
|
||||||
|
InstallUpdateDialog::~InstallUpdateDialog(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallUpdateDialog::install(void)
|
||||||
|
{
|
||||||
|
QString fullFilePath = this->temporaryDirectory + QStringLiteral("/") + this->filename;
|
||||||
|
QString appPath = QCoreApplication::applicationDirPath();
|
||||||
|
QString appPid = QString::number(QCoreApplication::applicationPid());
|
||||||
|
|
||||||
|
// Convert paths to use the right path separator
|
||||||
|
this->temporaryDirectory = QDir::toNativeSeparators(this->temporaryDirectory);
|
||||||
|
fullFilePath = QDir::toNativeSeparators(fullFilePath);
|
||||||
|
appPath = QDir::toNativeSeparators(appPath);
|
||||||
|
|
||||||
|
if (this->filename.endsWith(QStringLiteral(".exe")))
|
||||||
|
{
|
||||||
|
this->label->setText(QStringLiteral("Executing %1...").arg(this->filename));
|
||||||
|
QStringList scriptLines =
|
||||||
|
{
|
||||||
|
QStringLiteral("@echo off"),
|
||||||
|
QStringLiteral("("),
|
||||||
|
QStringLiteral(" echo == Attempting to kill PID ") + appPid,
|
||||||
|
QStringLiteral(" taskkill /F /PID:") + appPid,
|
||||||
|
QStringLiteral(" echo == Attempting to start '") + fullFilePath + QStringLiteral("'"),
|
||||||
|
QStringLiteral(" \"") + fullFilePath + QStringLiteral("\" /CLOSEAPPLICATIONS /NOCANCEL /MERGETASKS=\"!desktopicon\" /SILENT /DIR=\"") + appPath + QStringLiteral("\""),
|
||||||
|
QStringLiteral(")"),
|
||||||
|
QStringLiteral("IF NOT ERRORLEVEL 0 ("),
|
||||||
|
QStringLiteral(" start \"\" cmd /c \"echo Update failed, check the log for more information && pause\""),
|
||||||
|
QStringLiteral(")"),
|
||||||
|
// Remove temporary directory at last
|
||||||
|
QStringLiteral("rmdir /S /Q \"") + this->temporaryDirectory + QStringLiteral("\""),
|
||||||
|
};
|
||||||
|
this->writeAndRunScript(scriptLines);
|
||||||
|
this->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->label->setText(QStringLiteral("Extracting %1...").arg(this->filename));
|
||||||
|
this->progressBar->setValue(50);
|
||||||
|
|
||||||
|
QDir dir(this->temporaryDirectory);
|
||||||
|
if (!dir.mkdir(QStringLiteral("extract")))
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, QStringLiteral("Error"), QStringLiteral("Failed to create extract directory."));
|
||||||
|
this->reject();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString extractDirectory = this->temporaryDirectory + QStringLiteral("/extract");
|
||||||
|
|
||||||
|
if (!unzipFile(fullFilePath.toStdString(), extractDirectory.toStdString()))
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, QStringLiteral("Error"), QStringLiteral("Unzip failed: Unable to extract files."));
|
||||||
|
this->reject();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->label->setText(QStringLiteral("Executing update script..."));
|
||||||
|
this->progressBar->setValue(100);
|
||||||
|
|
||||||
|
extractDirectory = QDir::toNativeSeparators(extractDirectory);
|
||||||
|
|
||||||
|
QStringList scriptLines =
|
||||||
|
{
|
||||||
|
QStringLiteral("@echo off"),
|
||||||
|
QStringLiteral("("),
|
||||||
|
QStringLiteral(" echo == Attempting to remove '") + fullFilePath + QStringLiteral("'"),
|
||||||
|
QStringLiteral(" del /F /Q \"") + fullFilePath + QStringLiteral("\""),
|
||||||
|
QStringLiteral(" echo == Attempting to kill PID ") + appPid,
|
||||||
|
QStringLiteral(" taskkill /F /PID:") + appPid,
|
||||||
|
QStringLiteral(" echo == Attempting to copy '") + extractDirectory + QStringLiteral("' to '") + appPath + QStringLiteral("'"),
|
||||||
|
QStringLiteral(" xcopy /S /Y /I \"") + extractDirectory + QStringLiteral("\\*\" \"") + appPath + QStringLiteral("\""),
|
||||||
|
QStringLiteral(" echo == Attempting to start '") + appPath + QStringLiteral("\\Dolphin-MPN.exe'"),
|
||||||
|
QStringLiteral(" start \"\" \"") + appPath + QStringLiteral("\\Dolphin-MPN.exe\""),
|
||||||
|
QStringLiteral(")"),
|
||||||
|
QStringLiteral("IF NOT ERRORLEVEL 0 ("),
|
||||||
|
QStringLiteral(" start \"\" cmd /c \"echo Update failed && pause\""),
|
||||||
|
QStringLiteral(")"),
|
||||||
|
// Remove temporary directory at last
|
||||||
|
QStringLiteral("rmdir /S /Q \"") + this->temporaryDirectory + QStringLiteral("\""),
|
||||||
|
};
|
||||||
|
this->writeAndRunScript(scriptLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InstallUpdateDialog::unzipFile(const std::string& zipFilePath, const std::string& destDir)
|
||||||
|
{
|
||||||
|
unzFile zipFile = unzOpen(zipFilePath.c_str());
|
||||||
|
if (!zipFile)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
unzClose(zipFile);
|
||||||
|
return true; // Successfully unzipped all files
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallUpdateDialog::writeAndRunScript(QStringList stringList)
|
||||||
|
{
|
||||||
|
QString scriptPath = this->temporaryDirectory + QStringLiteral("/update.bat");
|
||||||
|
|
||||||
|
QFile scriptFile(scriptPath);
|
||||||
|
if (!scriptFile.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||||
|
{
|
||||||
|
QMessageBox::critical(this, tr("Error"), tr("Failed to open file for writing: %1").arg(filename));
|
||||||
|
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::timerEvent(QTimerEvent *event)
|
||||||
|
{
|
||||||
|
this->killTimer(event->timerId());
|
||||||
|
this->install();
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Dolphin for Mario Party Netplay
|
||||||
|
* Copyright (C) 2025 Tabitha Hanegan <tabithahanegan.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DownloadUpdateDialog.h"
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QString>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QProgressBar>
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QString;
|
||||||
|
class QLabel;
|
||||||
|
class QProgressBar;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class InstallUpdateDialog : public DownloadUpdateDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
InstallUpdateDialog(QWidget *parent, QString installationDirectory, QString temporaryDirectory, QString filename);
|
||||||
|
~InstallUpdateDialog();
|
||||||
|
|
||||||
|
void install(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString installationDirectory;
|
||||||
|
QString temporaryDirectory;
|
||||||
|
QString filename;
|
||||||
|
QLabel* label;
|
||||||
|
QProgressBar* progressBar;
|
||||||
|
|
||||||
|
void writeAndRunScript(QStringList stringList);
|
||||||
|
void launchProcess(QString file, QStringList arguments);
|
||||||
|
void timerEvent(QTimerEvent* event);
|
||||||
|
bool unzipFile(const std::string& zipFilePath, const std::string& destDir);
|
||||||
|
};
|
112
Source/Core/DolphinQt/MarioPartyNetplay/UpdateDialog.cpp
Normal file
112
Source/Core/DolphinQt/MarioPartyNetplay/UpdateDialog.cpp
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
/*
|
||||||
|
* Dolphin for Mario Party Netplay
|
||||||
|
* Copyright (C) 2025 Tabitha Hanegan <tabithahanegan.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "UpdateDialog.h"
|
||||||
|
#include "DownloadUpdateDialog.h"
|
||||||
|
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QTemporaryDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QSysInfo>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QTextEdit>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
|
||||||
|
using namespace UserInterface::Dialog;
|
||||||
|
|
||||||
|
UpdateDialog::UpdateDialog(QWidget *parent, QJsonObject jsonObject, bool forced)
|
||||||
|
: QDialog(parent)
|
||||||
|
{
|
||||||
|
this->jsonObject = jsonObject;
|
||||||
|
|
||||||
|
// Create UI components
|
||||||
|
QVBoxLayout* mainLayout = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
// Create and set up the label
|
||||||
|
label = new QLabel(this);
|
||||||
|
QString tagName = jsonObject.value(QStringLiteral("tag_name")).toString();
|
||||||
|
label->setText(QStringLiteral("%1 Available").arg(tagName));
|
||||||
|
mainLayout->addWidget(label);
|
||||||
|
|
||||||
|
// Create and set up the text edit
|
||||||
|
textEdit = new QTextEdit(this);
|
||||||
|
textEdit->setText(jsonObject.value(QStringLiteral("body")).toString());
|
||||||
|
textEdit->setReadOnly(true);
|
||||||
|
mainLayout->addWidget(textEdit);
|
||||||
|
|
||||||
|
// Create and set up the button box
|
||||||
|
buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||||
|
QPushButton* updateButton = buttonBox->button(QDialogButtonBox::Ok);
|
||||||
|
updateButton->setText(QStringLiteral("Update"));
|
||||||
|
mainLayout->addWidget(buttonBox);
|
||||||
|
|
||||||
|
// Connect signals
|
||||||
|
connect(buttonBox, &QDialogButtonBox::accepted, this, &UpdateDialog::accept);
|
||||||
|
connect(buttonBox, &QDialogButtonBox::rejected, this, &UpdateDialog::reject);
|
||||||
|
|
||||||
|
// Set the layout
|
||||||
|
setLayout(mainLayout);
|
||||||
|
setWindowTitle(QStringLiteral("Update Available"));
|
||||||
|
resize(400, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateDialog::~UpdateDialog()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString UpdateDialog::GetFileName()
|
||||||
|
{
|
||||||
|
return this->filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl UpdateDialog::GetUrl()
|
||||||
|
{
|
||||||
|
return this->url;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateDialog::accept()
|
||||||
|
{
|
||||||
|
QJsonArray jsonArray = jsonObject[QStringLiteral("assets")].toArray();
|
||||||
|
QString filenameToDownload;
|
||||||
|
QUrl urlToDownload;
|
||||||
|
|
||||||
|
for (const QJsonValue& value : jsonArray)
|
||||||
|
{
|
||||||
|
QJsonObject object = value.toObject();
|
||||||
|
|
||||||
|
QString filename = object.value(QStringLiteral("name")).toString();
|
||||||
|
QUrl url(object.value(QStringLiteral("browser_download_url")).toString());
|
||||||
|
|
||||||
|
if (QSysInfo::productType() == QStringLiteral("windows")) {
|
||||||
|
if (filename.contains(QStringLiteral("win32")) ||
|
||||||
|
filename.contains(QStringLiteral("windows")) ||
|
||||||
|
filename.contains(QStringLiteral("win64")))
|
||||||
|
{
|
||||||
|
filenameToDownload = filename;
|
||||||
|
urlToDownload = url;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (QSysInfo::productType() == QStringLiteral("macos")) {
|
||||||
|
if (filename.contains(QStringLiteral("darwin")) ||
|
||||||
|
filename.contains(QStringLiteral("macOS")))
|
||||||
|
{
|
||||||
|
filenameToDownload = filename;
|
||||||
|
urlToDownload = url;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->url = urlToDownload;
|
||||||
|
this->filename = filenameToDownload;
|
||||||
|
QDialog::accept();
|
||||||
|
}
|
50
Source/Core/DolphinQt/MarioPartyNetplay/UpdateDialog.h
Normal file
50
Source/Core/DolphinQt/MarioPartyNetplay/UpdateDialog.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Dolphin for Mario Party Netplay
|
||||||
|
* Copyright (C) 2025 Tabitha Hanegan <tabithahanegan.com>
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
class QLabel;
|
||||||
|
class QTextEdit;
|
||||||
|
class QDialogButtonBox;
|
||||||
|
|
||||||
|
namespace UserInterface
|
||||||
|
{
|
||||||
|
namespace Dialog
|
||||||
|
{
|
||||||
|
|
||||||
|
class UpdateDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
QJsonObject jsonObject;
|
||||||
|
QString filename;
|
||||||
|
QUrl url;
|
||||||
|
|
||||||
|
// UI elements
|
||||||
|
QLabel* label;
|
||||||
|
QTextEdit* textEdit;
|
||||||
|
QDialogButtonBox* buttonBox;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
bool isWin32Setup = false;
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit UpdateDialog(QWidget *parent, QJsonObject jsonObject, bool forced);
|
||||||
|
~UpdateDialog();
|
||||||
|
|
||||||
|
QString GetFileName();
|
||||||
|
QUrl GetUrl();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void accept() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Dialog
|
||||||
|
} // namespace UserInterface
|
|
@ -621,6 +621,10 @@ 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("&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"));
|
QAction* documentation = help_menu->addAction(tr("Online &Documentation"));
|
||||||
|
@ -629,12 +633,7 @@ void MenuBar::AddHelpMenu()
|
||||||
});
|
});
|
||||||
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/dolphin-emu/dolphin")));
|
QDesktopServices::openUrl(QUrl(QStringLiteral("https://github.com/MarioPartyNetplay/Dolphin-MPN")));
|
||||||
});
|
|
||||||
QAction* bugtracker = help_menu->addAction(tr("&Bug Tracker"));
|
|
||||||
connect(bugtracker, &QAction::triggered, this, []() {
|
|
||||||
QDesktopServices::openUrl(
|
|
||||||
QUrl(QStringLiteral("https://bugs.dolphin-emu.org/projects/emulator")));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
#ifndef __APPLE__
|
#ifndef __APPLE__
|
||||||
|
|
|
@ -87,6 +87,7 @@ signals:
|
||||||
void BootGameCubeIPL(DiscIO::Region region);
|
void BootGameCubeIPL(DiscIO::Region region);
|
||||||
void ShowFIFOPlayer();
|
void ShowFIFOPlayer();
|
||||||
void ShowAboutDialog();
|
void ShowAboutDialog();
|
||||||
|
void ShowUpdateDialog();
|
||||||
void ShowCheatsManager();
|
void ShowCheatsManager();
|
||||||
void ShowResourcePackManager();
|
void ShowResourcePackManager();
|
||||||
void ShowSkylanderPortal();
|
void ShowSkylanderPortal();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue