diff --git a/src/emulator.cpp b/src/emulator.cpp index cd981add2..5fb8a2196 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -3,6 +3,12 @@ #include #include +#include +#include +#include +#include +#include +#include #include "common/config.h" #include "common/debug.h" @@ -102,6 +108,8 @@ Emulator::~Emulator() { } void Emulator::Run(const std::filesystem::path& file, const std::vector args) { + isRunning = true; + const auto eboot_name = file.filename().string(); auto game_folder = file.parent_path(); if (const auto game_folder_name = game_folder.filename().string(); @@ -251,7 +259,6 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorGetHostPath("/app0/" + eboot_name); linker->LoadModule(eboot_path); - // check if we have system modules to load LoadSystemModules(game_info.game_serial); @@ -288,6 +295,67 @@ 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..258e5b6b5 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -5,11 +5,13 @@ #include #include +#include "common/elf_info.h" #include "common/singleton.h" #include "core/linker.h" #include "input/controller.h" #include "sdl_window.h" +#include namespace Core { @@ -27,10 +29,20 @@ 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 Restart(); + void Shutdown(); + private: void LoadSystemModules(const std::string& game_serial); - + Common::ElfInfo game_info; + QString lastEbootPath; + void saveLastEbootPath(const QString& path); + QString getLastEbootPath(); + bool isRunning = false; Core::MemoryManager* memory; Input::GameController* controller; Core::Linker* linker; diff --git a/src/images/restart_icon.png b/src/images/restart_icon.png new file mode 100644 index 000000000..1ef414220 Binary files /dev/null and b/src/images/restart_icon.png differ diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 5cbce1884..ce43838dd 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" @@ -143,6 +146,7 @@ void MainWindow::AddUiWidgets() { ui->toolBar->addWidget(ui->settingsButton); ui->toolBar->addWidget(ui->controllerButton); ui->toolBar->addWidget(ui->keyboardButton); + ui->toolBar->addWidget(ui->restartButton); QFrame* line = new QFrame(this); line->setFrameShape(QFrame::StyledPanel); line->setFrameShadow(QFrame::Sunken); @@ -276,6 +280,8 @@ void MainWindow::CreateConnects() { }); connect(ui->playButton, &QPushButton::clicked, this, &MainWindow::StartGame); + connect(ui->stopButton, &QPushButton::clicked, this, &MainWindow::StopGame); + connect(ui->restartButton, &QPushButton::clicked, this, &MainWindow::RestartGame); connect(m_game_grid_frame.get(), &QTableWidget::cellDoubleClicked, this, &MainWindow::StartGame); connect(m_game_list_frame.get(), &QTableWidget::cellDoubleClicked, this, @@ -761,6 +767,10 @@ void MainWindow::BootGame() { } } +QString MainWindow::getLastEbootPath() { + return QString(); +} + void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg) { if (Loader::DetectFileType(file) == Loader::FileTypes::Pkg) { std::string failreason; @@ -1113,6 +1123,7 @@ void MainWindow::SetUiIcons(bool isWhite) { ui->settingsButton->setIcon(RecolorIcon(ui->settingsButton->icon(), isWhite)); ui->controllerButton->setIcon(RecolorIcon(ui->controllerButton->icon(), isWhite)); ui->keyboardButton->setIcon(RecolorIcon(ui->keyboardButton->icon(), isWhite)); + ui->restartButton->setIcon(RecolorIcon(ui->restartButton->icon(), isWhite)); ui->refreshGameListAct->setIcon(RecolorIcon(ui->refreshGameListAct->icon(), isWhite)); ui->menuGame_List_Mode->setIcon(RecolorIcon(ui->menuGame_List_Mode->icon(), isWhite)); ui->pkgViewerAct->setIcon(RecolorIcon(ui->pkgViewerAct->icon(), isWhite)); @@ -1242,3 +1253,45 @@ void MainWindow::StartEmulator(std::filesystem::path path) { emulator_thread.detach(); #endif } + +void MainWindow::StopGame() { + if (!isGameRunning) { + QMessageBox::information(this, tr("Stop Game"), tr("No game is currently running.")); + return; + } + + // Get the emulator instance and stop it + Core::Emulator& emulator = Core::Emulator::GetInstance(); // Use the correct instance method + emulator.StopEmulation(); + + isGameRunning = false; + QMessageBox::information(this, tr("Stop Game"), tr("Game has been stopped successfully.")); + + SDL_Event quitEvent; + quitEvent.type = SDL_EVENT_QUIT + 1; + SDL_PushEvent(&quitEvent); +} + +void MainWindow::RestartGame() { + if (!isGameRunning) { + QMessageBox::warning(this, tr("Restart Game"), tr("No game is running to restart.")); + return; + } + + std::vector recentFiles = Config::getRecentFiles(); + if (recentFiles.empty()) { + QMessageBox::warning(this, tr("Restart Game"), tr("No recent game found.")); + return; + } + + QString lastGamePath = QString::fromStdString(recentFiles.front()); + + // Stop the current game first + StopGame(); + + // Wait for cleanup + QThread::sleep(1); + + // Restart the game + StartEmulator(Common::FS::PathFromQString(lastGamePath)); +} diff --git a/src/qt_gui/main_window.h b/src/qt_gui/main_window.h index 5ac56e44c..69d5d8800 100644 --- a/src/qt_gui/main_window.h +++ b/src/qt_gui/main_window.h @@ -38,6 +38,8 @@ public: void InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg); void InstallDirectory(); void StartGame(); + void StopGame(); + void RestartGame(); private Q_SLOTS: void ConfigureGuiFromSettings(); @@ -65,6 +67,7 @@ private: void SetUiIcons(bool isWhite); void InstallPkg(); void BootGame(); + QString getLastEbootPath(); void AddRecentFiles(QString filePath); void LoadTranslation(); void PlayBackgroundMusic(); diff --git a/src/qt_gui/main_window_ui.h b/src/qt_gui/main_window_ui.h index e74ffcacb..79456180b 100644 --- a/src/qt_gui/main_window_ui.h +++ b/src/qt_gui/main_window_ui.h @@ -48,6 +48,8 @@ public: QPushButton* settingsButton; QPushButton* controllerButton; QPushButton* keyboardButton; + QPushButton* restartButton; + QWidget* sizeSliderContainer; QHBoxLayout* sizeSliderContainer_layout; @@ -215,6 +217,10 @@ public: keyboardButton->setFlat(true); keyboardButton->setIcon(QIcon(":images/keyboard_icon.png")); keyboardButton->setIconSize(QSize(48, 44)); + restartButton = new QPushButton(centralWidget); + restartButton->setFlat(true); + restartButton->setIcon(QIcon(":images/restart_icon.png")); + restartButton->setIconSize(QSize(48, 44)); sizeSliderContainer = new QWidget(centralWidget); sizeSliderContainer->setObjectName("sizeSliderContainer"); diff --git a/src/sdl_window.cpp b/src/sdl_window.cpp index ccc369c53..fb170440e 100644 --- a/src/sdl_window.cpp +++ b/src/sdl_window.cpp @@ -1,6 +1,11 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include +#include #include "SDL3/SDL_events.h" #include "SDL3/SDL_hints.h" #include "SDL3/SDL_init.h" @@ -363,14 +368,67 @@ void WindowSDL::WaitEvent() { break; } break; + case SDL_EVENT_QUIT: is_open = false; break; - default: + case SDL_EVENT_QUIT + 1: + is_open = false; + RelaunchEmulator(); break; } } +void WindowSDL::RelaunchEmulator() { + QString emulatorPath = QCoreApplication::applicationFilePath(); // Get current executable path + QString emulatorDir = QFileInfo(emulatorPath).absolutePath(); // Get working directory + +#ifdef Q_OS_WIN + QString scriptFileName = + QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/relaunch.ps1"; + QString scriptContent = + QStringLiteral( + "Start-Sleep -Seconds 2\n" + "Start-Process -FilePath '%1' -WorkingDirectory ([WildcardPattern]::Escape('%2'))\n") + .arg(emulatorPath, emulatorDir); + + QFile scriptFile(scriptFileName); + if (scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream out(&scriptFile); + scriptFile.write("\xEF\xBB\xBF"); // UTF-8 BOM + out << scriptContent; + scriptFile.close(); + + // Execute PowerShell script + QProcess::startDetached("powershell.exe", QStringList() << "-ExecutionPolicy" + << "Bypass" + << "-File" << scriptFileName); + } + +#elif defined(Q_OS_LINUX) || defined(Q_OS_MAC) + QString scriptFileName = "/tmp/relaunch.sh"; + QString scriptContent = QStringLiteral("#!/bin/bash\n" + "sleep 2\n" + "cd '%2'\n" + "./'%1' &\n") + .arg(QFileInfo(emulatorPath).fileName(), emulatorDir); + + QFile scriptFile(scriptFileName); + if (scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream out(&scriptFile); + out << scriptContent; + scriptFile.close(); + + // Make script executable + scriptFile.setPermissions(QFileDevice::ExeOwner | QFileDevice::ReadOwner | + QFileDevice::WriteOwner); + + // Execute bash script + QProcess::startDetached("bash", QStringList() << scriptFileName); + } +#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..930bf693f 100644 --- a/src/sdl_window.h +++ b/src/sdl_window.h @@ -97,6 +97,7 @@ private: void OnResize(); void OnKeyboardMouseInput(const SDL_Event* event); void OnGamepadEvent(const SDL_Event* event); + void RelaunchEmulator(); private: s32 width; @@ -106,6 +107,7 @@ private: SDL_Window* window{}; bool is_shown{}; bool is_open{true}; + bool restartRequested = false; }; } // namespace Frontend diff --git a/src/shadps4.qrc b/src/shadps4.qrc index 14b50f7a5..0fc65f5a2 100644 --- a/src/shadps4.qrc +++ b/src/shadps4.qrc @@ -32,5 +32,6 @@ images/website.png images/ps4_controller.png images/keyboard_icon.png + images/restart_icon.png