Initial support for stop and restart game now relaunches emulator when clicked stop or restart button.

This commit is contained in:
Dmugetsu 2025-02-14 18:49:59 -06:00
parent 03156fd49c
commit 3bdb9645fd
9 changed files with 206 additions and 3 deletions

View File

@ -3,6 +3,12 @@
#include <set>
#include <fmt/core.h>
#include <QString>
#include <QFile>
#include <QSettings>
#include <QProcess>
#include <QThread>
#include <QDebug>
#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<std::string> 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::vector<std::str
// Load the module with the linker
const auto eboot_path = mnt->GetHostPath("/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<std::str
std::quick_exit(0);
}
void Emulator::saveLastEbootPath(const QString& path) {
lastEbootPath = path;
}
QString Emulator::getLastEbootPath() {
return lastEbootPath;
}
Emulator& Emulator::GetInstance() {
static Emulator instance;
return instance;
}
void Emulator::StopEmulation() {
if (!is_running)
return;
is_running = false;
qDebug() << "Stopping emulator...";
}
void Emulator::Restart() {
if (!isRunning) {
LOG_INFO(Loader, "Emulator is not running. Starting normally...");
return;
}
LOG_INFO(Loader, "Restarting the emulator...");
// Stop current emulator session
StopEmulation();
// Wait a moment before restarting
QThread::sleep(2);
// Retrieve last known EBOOT path
QString lastEbootPath = getLastEbootPath();
if (lastEbootPath.isEmpty()) {
LOG_ERROR(Loader, "No previous EBOOT path found! Cannot restart.");
return;
}
// Relaunch emulator with last game
#ifdef Q_OS_WIN
QString emulatorPath =
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/shadps4.exe";
QProcess::startDetached(emulatorPath, QStringList() << lastEbootPath);
#elif defined(Q_OS_LINUX)
QString emulatorPath =
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/Shadps4-qt.AppImage";
QProcess::startDetached(emulatorPath, QStringList() << lastEbootPath);
#elif defined(Q_OS_MAC)
QString emulatorPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) +
"/shadps4.app/Contents/MacOS/shadps4";
QProcess::startDetached(emulatorPath, QStringList() << lastEbootPath);
#endif
LOG_INFO(Loader, "Emulator restarted successfully.");
isRunning = true;
}
void Emulator::LoadSystemModules(const std::string& game_serial) {
constexpr std::array<SysModules, 11> ModulesToLoad{
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2},

View File

@ -5,11 +5,13 @@
#include <filesystem>
#include <thread>
#include "common/elf_info.h"
#include "common/singleton.h"
#include "core/linker.h"
#include "input/controller.h"
#include "sdl_window.h"
#include <QString>
namespace Core {
@ -27,10 +29,20 @@ public:
void Run(const std::filesystem::path& file, const std::vector<std::string> 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;

BIN
src/images/restart_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View File

@ -1,10 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fstream>
#include <iostream>
#include <QDockWidget>
#include <QKeyEvent>
#include <QPlainTextEdit>
#include <QProgressDialog>
#include <SDL3/SDL_events.h>
#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<std::string> 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));
}

View File

@ -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();

View File

@ -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");

View File

@ -1,6 +1,11 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <QCoreApplication>
#include <QFileInfo>
#include <QProcess>
#include <QStandardPaths>
#include <QThread>
#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);

View File

@ -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

View File

@ -32,5 +32,6 @@
<file>images/website.png</file>
<file>images/ps4_controller.png</file>
<file>images/keyboard_icon.png</file>
<file>images/restart_icon.png</file>
</qresource>
</RCC>