diff --git a/src/emulator.cpp b/src/emulator.cpp index cd981add2..7edfc15b7 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -102,6 +102,7 @@ Emulator::~Emulator() { } void Emulator::Run(const std::filesystem::path& file, const std::vector args) { + is_running= true; const auto eboot_name = file.filename().string(); auto game_folder = file.parent_path(); if (const auto game_folder_name = game_folder.filename().string(); @@ -288,6 +289,27 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector ModulesToLoad{ {{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2}, diff --git a/src/emulator.h b/src/emulator.h index 08c2807a1..ac5c595b1 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -5,6 +5,10 @@ #include #include +#include "common/elf_info.h" +#ifdef ENABLE_QT_GUI +#include +#endif #include "common/singleton.h" #include "core/linker.h" @@ -27,10 +31,17 @@ public: void Run(const std::filesystem::path& file, const std::vector args = {}); void UpdatePlayTime(const std::string& serial); + static Emulator& GetInstance(); + void StopEmulation(); + bool is_running = false; + void saveLastEbootPath(const std::string& path); + std::string getLastEbootPath() const; private: void LoadSystemModules(const std::string& game_serial); + Common::ElfInfo game_info; + std::string lastEbootPath; Core::MemoryManager* memory; Input::GameController* controller; Core::Linker* linker; diff --git a/src/qt_gui/check_update.cpp b/src/qt_gui/check_update.cpp index 5cae6c41a..f6f8b3f41 100644 --- a/src/qt_gui/check_update.cpp +++ b/src/qt_gui/check_update.cpp @@ -29,8 +29,8 @@ using namespace Common::FS; -CheckUpdate::CheckUpdate(const bool showMessage, QWidget* parent) - : QDialog(parent), networkManager(new QNetworkAccessManager(this)) { +CheckUpdate::CheckUpdate(MainWindow* mainWindow, bool showMessage, QWidget* parent) + : QDialog(parent), networkManager(new QNetworkAccessManager(this)), m_mainWindow(mainWindow) { setWindowTitle(tr("Auto Updater")); setFixedSize(0, 0); CheckForUpdates(showMessage); @@ -422,6 +422,13 @@ void CheckUpdate::DownloadUpdate(const QString& url) { } void CheckUpdate::Install() { + if (m_mainWindow && m_mainWindow->isGameRunning) { + QMessageBox::warning(this, tr("Update Warning"), + tr("A game is currently running. Please close the game to prevent " + "save file corruption.")); + m_mainWindow->StopGameforUpdate(false); + } + QString userPath; Common::FS::PathToQString(userPath, Common::FS::GetUserPath(Common::FS::PathType::UserDir)); diff --git a/src/qt_gui/check_update.h b/src/qt_gui/check_update.h index dfeb95e73..f974a1af8 100644 --- a/src/qt_gui/check_update.h +++ b/src/qt_gui/check_update.h @@ -3,17 +3,20 @@ #ifndef CHECKUPDATE_H #define CHECKUPDATE_H +#pragma once #include #include #include #include +#include "main_window.h" class CheckUpdate : public QDialog { Q_OBJECT public: - explicit CheckUpdate(const bool showMessage, QWidget* parent = nullptr); + explicit CheckUpdate(MainWindow* mainWindow, bool showMessage, QWidget* parent = nullptr); + CheckUpdate(const bool showMessage, QWidget* parent); ~CheckUpdate(); private slots: @@ -35,6 +38,7 @@ private: QString updateDownloadUrl; QNetworkAccessManager* networkManager; + MainWindow* m_mainWindow; }; #endif // CHECKUPDATE_H diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 0da6428aa..af0adefbc 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -1,10 +1,13 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include #include #include #include #include +#include #include "about_dialog.h" #include "cheats_patches.h" @@ -30,6 +33,7 @@ #ifdef ENABLE_DISCORD_RPC #include "common/discord_rpc_handler.h" #endif +#include MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); @@ -222,12 +226,10 @@ void MainWindow::LoadGameLists() { #ifdef ENABLE_UPDATER void MainWindow::CheckUpdateMain(bool checkSave) { - if (checkSave) { - if (!Config::autoUpdate()) { - return; - } + if (checkSave && !Config::autoUpdate()) { + return; } - auto checkUpdate = new CheckUpdate(false); + auto checkUpdate = new CheckUpdate(this, false, this); checkUpdate->exec(); } #endif @@ -276,6 +278,7 @@ void MainWindow::CreateConnects() { }); connect(ui->playButton, &QPushButton::clicked, this, &MainWindow::StartGame); + connect(ui->stopButton, &QPushButton::clicked, this, &MainWindow::StopGame); connect(m_game_grid_frame.get(), &QTableWidget::cellDoubleClicked, this, &MainWindow::StartGame); connect(m_game_list_frame.get(), &QTableWidget::cellDoubleClicked, this, @@ -335,7 +338,7 @@ void MainWindow::CreateConnects() { #ifdef ENABLE_UPDATER connect(ui->updaterAct, &QAction::triggered, this, [this]() { - auto checkUpdate = new CheckUpdate(true); + auto checkUpdate = new CheckUpdate(this, false, this); checkUpdate->exec(); }); #endif @@ -1151,6 +1154,10 @@ void MainWindow::HandleResize(QResizeEvent* event) { } } +std::string MainWindow::getLastEbootPath() { + return std::string(); +} + void MainWindow::AddRecentFiles(QString filePath) { std::vector vec = Config::getRecentFiles(); if (!vec.empty()) { @@ -1226,6 +1233,10 @@ void MainWindow::OnLanguageChanged(const std::string& locale) { LoadTranslation(); } +static void ShowMessageBox(const std::string& title, const std::string& message) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, title.c_str(), message.c_str(), NULL); +} + bool MainWindow::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::KeyPress) { QKeyEvent* keyEvent = static_cast(event); @@ -1258,3 +1269,33 @@ void MainWindow::StartEmulator(std::filesystem::path path) { emulator_thread.detach(); #endif } + +void MainWindow::StopGameforUpdate(bool shouldRelaunch) { + if (!isGameRunning) { + return; + } + + Core::Emulator& emulator = Core::Emulator::GetInstance(); + emulator.StopEmulation(); + + SDL_Event quitEvent; + quitEvent.type = SDL_EVENT_QUIT; + SDL_PushEvent(&quitEvent); + + isGameRunning = false; +} + +void MainWindow::StopGame() { + if (!isGameRunning) { + return; + } + + Core::Emulator& emulator = Core::Emulator::GetInstance(); + emulator.StopEmulation(); + + if (isGameRunning == true) + ; + SDL_Event quitEvent; + quitEvent.type = SDL_EVENT_QUIT + 1; + SDL_PushEvent(&quitEvent); +} \ No newline at end of file diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index 5ac56e44c..098b9f40b 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -38,6 +38,11 @@ public: void InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg); void InstallDirectory(); void StartGame(); + void StopGameforUpdate(bool shouldRelaunch); + void StopGame(); + std::string lastGamePath; + std::string getLastEbootPath(); + bool isGameRunning = false; private Q_SLOTS: void ConfigureGuiFromSettings(); @@ -65,6 +70,8 @@ private: void SetUiIcons(bool isWhite); void InstallPkg(); void BootGame(); + bool stopButtonClicked = false; + void AddRecentFiles(QString filePath); void LoadTranslation(); void PlayBackgroundMusic(); @@ -72,7 +79,6 @@ private: void StartEmulator(std::filesystem::path); bool isIconBlack = false; bool isTableList = true; - bool isGameRunning = false; QActionGroup* m_icon_size_act_group = nullptr; QActionGroup* m_list_mode_act_group = nullptr; QActionGroup* m_theme_act_group = nullptr; @@ -127,4 +133,5 @@ protected: std::filesystem::path last_install_dir = ""; bool delete_file_on_install = false; bool use_for_all_queued = false; + }; diff --git a/src/qt_gui/settings_dialog.cpp b/src/qt_gui/settings_dialog.cpp index bebb16c9a..c095c0f0d 100644 --- a/src/qt_gui/settings_dialog.cpp +++ b/src/qt_gui/settings_dialog.cpp @@ -6,6 +6,7 @@ #include #include #include +#include "main_window.h" #include "common/config.h" #include "common/version.h" @@ -167,9 +168,13 @@ SettingsDialog::SettingsDialog(std::span physical_devices, } }); - connect(ui->checkUpdateButton, &QPushButton::clicked, this, []() { - auto checkUpdate = new CheckUpdate(true); - checkUpdate->exec(); + connect(ui->checkUpdateButton, &QPushButton::clicked, this, [this, parent = (parent)]() { + MainWindow* mainWindow = dynamic_cast(parent); // Use the captured 'parent' + if (mainWindow) { + auto checkUpdate = + new CheckUpdate(mainWindow, true, this); // Pass MainWindow* and bool + checkUpdate->exec(); + } }); #else ui->updaterGroupBox->setVisible(false); diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index db6f37e2a..b694afcdc 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -1,6 +1,13 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include +#include +#include + #include "SDL3/SDL_events.h" #include "SDL3/SDL_hints.h" #include "SDL3/SDL_init.h" @@ -24,6 +31,12 @@ #include "SDL3/SDL_metal.h" #endif +#ifdef _WIN32 +#include +#else +#include +#endif + namespace Input { using Libraries::Pad::OrbisPadButtonDataOffset; @@ -392,11 +405,79 @@ void WindowSDL::WaitEvent() { case SDL_EVENT_QUIT: is_open = false; break; - default: + case SDL_EVENT_QUIT + 1: + is_open = false; + RelaunchEmulator(); break; } } +void WindowSDL::RelaunchEmulator() { +#ifdef _WIN32 + char emulatorPath[MAX_PATH]; + GetModuleFileNameA(nullptr, emulatorPath, MAX_PATH); // Get current executable path + + std::string emulatorDir = std::string(emulatorPath); + size_t pos = emulatorDir.find_last_of("\\/"); + if (pos != std::string::npos) { + emulatorDir = emulatorDir.substr(0, pos); // Extract directory + } + + std::string scriptFileName = std::getenv("TEMP") + std::string("\\relaunch.ps1"); + std::ofstream scriptFile(scriptFileName); + + if (scriptFile.is_open()) { + scriptFile << "Start-Sleep -Seconds 2\n" + << "Start-Process -FilePath '" << emulatorPath + << "' -WorkingDirectory ([WildcardPattern]::Escape('" << emulatorDir << "'))\n"; + scriptFile.close(); + + // Execute PowerShell script + std::string command = + "powershell.exe -ExecutionPolicy Bypass -File \"" + scriptFileName + "\""; + system(command.c_str()); + } else { + std::cerr << "Failed to create relaunch script" << std::endl; + } + +#elif defined(__linux__) || defined(__APPLE__) + char emulatorPath[1024]; + ssize_t count = readlink("/proc/self/exe", emulatorPath, sizeof(emulatorPath) - 1); + if (count != -1) { + emulatorPath[count] = '\0'; + } else { + std::cerr << "Failed to get executable path" << std::endl; + return; + } + + std::string emulatorDir = std::string(emulatorPath); + size_t pos = emulatorDir.find_last_of("/"); + if (pos != std::string::npos) { + emulatorDir = emulatorDir.substr(0, pos); // Extract directory + } + + std::string scriptFileName = "/tmp/relaunch.sh"; + std::ofstream scriptFile(scriptFileName); + + if (scriptFile.is_open()) { + scriptFile << "#!/bin/bash\n" + << "sleep 2\n" + << "cd '" << emulatorDir << "'\n" + << "./'" << emulatorPath + pos + 1 << "' &\n"; + scriptFile.close(); + + // Make script executable + chmod(scriptFileName.c_str(), S_IRWXU); + + // Execute bash script + std::string command = "bash " + scriptFileName; + system(command.c_str()); + } else { + std::cerr << "Failed to create relaunch script" << std::endl; + } +#endif +} + void WindowSDL::InitTimers() { SDL_AddTimer(100, &PollController, controller); SDL_AddTimer(33, Input::MousePolling, (void*)controller); diff --git a/src/sdl_window.h b/src/sdl_window.h index 9acd2b16b..60815e286 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -88,6 +88,8 @@ public: } void WaitEvent(); + void OnStopButtonClicked(); + void RelaunchEmulator(); void InitTimers(); void RequestKeyboard(); @@ -106,6 +108,7 @@ private: SDL_Window* window{}; bool is_shown{}; bool is_open{true}; + bool stopButtonClicked = false; }; } // namespace Frontend