mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-08-04 16:32:39 +00:00
Initial support for stop and restart game now relaunches emulator when clicked stop or restart button.
This commit is contained in:
parent
03156fd49c
commit
3bdb9645fd
@ -3,6 +3,12 @@
|
|||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
#include <QString>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
#include "common/debug.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) {
|
void Emulator::Run(const std::filesystem::path& file, const std::vector<std::string> args) {
|
||||||
|
isRunning = true;
|
||||||
|
|
||||||
const auto eboot_name = file.filename().string();
|
const auto eboot_name = file.filename().string();
|
||||||
auto game_folder = file.parent_path();
|
auto game_folder = file.parent_path();
|
||||||
if (const auto game_folder_name = game_folder.filename().string();
|
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
|
// Load the module with the linker
|
||||||
const auto eboot_path = mnt->GetHostPath("/app0/" + eboot_name);
|
const auto eboot_path = mnt->GetHostPath("/app0/" + eboot_name);
|
||||||
linker->LoadModule(eboot_path);
|
linker->LoadModule(eboot_path);
|
||||||
|
|
||||||
// check if we have system modules to load
|
// check if we have system modules to load
|
||||||
LoadSystemModules(game_info.game_serial);
|
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);
|
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) {
|
void Emulator::LoadSystemModules(const std::string& game_serial) {
|
||||||
constexpr std::array<SysModules, 11> ModulesToLoad{
|
constexpr std::array<SysModules, 11> ModulesToLoad{
|
||||||
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2},
|
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterlibSceNgs2},
|
||||||
|
@ -5,11 +5,13 @@
|
|||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include "common/elf_info.h"
|
||||||
|
|
||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
#include "core/linker.h"
|
#include "core/linker.h"
|
||||||
#include "input/controller.h"
|
#include "input/controller.h"
|
||||||
#include "sdl_window.h"
|
#include "sdl_window.h"
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
@ -27,10 +29,20 @@ public:
|
|||||||
|
|
||||||
void Run(const std::filesystem::path& file, const std::vector<std::string> args = {});
|
void Run(const std::filesystem::path& file, const std::vector<std::string> args = {});
|
||||||
void UpdatePlayTime(const std::string& serial);
|
void UpdatePlayTime(const std::string& serial);
|
||||||
|
static Emulator& GetInstance();
|
||||||
|
void StopEmulation();
|
||||||
|
bool is_running = false;
|
||||||
|
void Restart();
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void LoadSystemModules(const std::string& game_serial);
|
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;
|
Core::MemoryManager* memory;
|
||||||
Input::GameController* controller;
|
Input::GameController* controller;
|
||||||
Core::Linker* linker;
|
Core::Linker* linker;
|
||||||
|
BIN
src/images/restart_icon.png
Normal file
BIN
src/images/restart_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 102 KiB |
@ -1,10 +1,13 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
#include <QDockWidget>
|
#include <QDockWidget>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
#include <QProgressDialog>
|
#include <QProgressDialog>
|
||||||
|
#include <SDL3/SDL_events.h>
|
||||||
|
|
||||||
#include "about_dialog.h"
|
#include "about_dialog.h"
|
||||||
#include "cheats_patches.h"
|
#include "cheats_patches.h"
|
||||||
@ -143,6 +146,7 @@ void MainWindow::AddUiWidgets() {
|
|||||||
ui->toolBar->addWidget(ui->settingsButton);
|
ui->toolBar->addWidget(ui->settingsButton);
|
||||||
ui->toolBar->addWidget(ui->controllerButton);
|
ui->toolBar->addWidget(ui->controllerButton);
|
||||||
ui->toolBar->addWidget(ui->keyboardButton);
|
ui->toolBar->addWidget(ui->keyboardButton);
|
||||||
|
ui->toolBar->addWidget(ui->restartButton);
|
||||||
QFrame* line = new QFrame(this);
|
QFrame* line = new QFrame(this);
|
||||||
line->setFrameShape(QFrame::StyledPanel);
|
line->setFrameShape(QFrame::StyledPanel);
|
||||||
line->setFrameShadow(QFrame::Sunken);
|
line->setFrameShadow(QFrame::Sunken);
|
||||||
@ -276,6 +280,8 @@ void MainWindow::CreateConnects() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
connect(ui->playButton, &QPushButton::clicked, this, &MainWindow::StartGame);
|
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,
|
connect(m_game_grid_frame.get(), &QTableWidget::cellDoubleClicked, this,
|
||||||
&MainWindow::StartGame);
|
&MainWindow::StartGame);
|
||||||
connect(m_game_list_frame.get(), &QTableWidget::cellDoubleClicked, this,
|
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) {
|
void MainWindow::InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg) {
|
||||||
if (Loader::DetectFileType(file) == Loader::FileTypes::Pkg) {
|
if (Loader::DetectFileType(file) == Loader::FileTypes::Pkg) {
|
||||||
std::string failreason;
|
std::string failreason;
|
||||||
@ -1113,6 +1123,7 @@ void MainWindow::SetUiIcons(bool isWhite) {
|
|||||||
ui->settingsButton->setIcon(RecolorIcon(ui->settingsButton->icon(), isWhite));
|
ui->settingsButton->setIcon(RecolorIcon(ui->settingsButton->icon(), isWhite));
|
||||||
ui->controllerButton->setIcon(RecolorIcon(ui->controllerButton->icon(), isWhite));
|
ui->controllerButton->setIcon(RecolorIcon(ui->controllerButton->icon(), isWhite));
|
||||||
ui->keyboardButton->setIcon(RecolorIcon(ui->keyboardButton->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->refreshGameListAct->setIcon(RecolorIcon(ui->refreshGameListAct->icon(), isWhite));
|
||||||
ui->menuGame_List_Mode->setIcon(RecolorIcon(ui->menuGame_List_Mode->icon(), isWhite));
|
ui->menuGame_List_Mode->setIcon(RecolorIcon(ui->menuGame_List_Mode->icon(), isWhite));
|
||||||
ui->pkgViewerAct->setIcon(RecolorIcon(ui->pkgViewerAct->icon(), isWhite));
|
ui->pkgViewerAct->setIcon(RecolorIcon(ui->pkgViewerAct->icon(), isWhite));
|
||||||
@ -1242,3 +1253,45 @@ void MainWindow::StartEmulator(std::filesystem::path path) {
|
|||||||
emulator_thread.detach();
|
emulator_thread.detach();
|
||||||
#endif
|
#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));
|
||||||
|
}
|
||||||
|
@ -38,6 +38,8 @@ public:
|
|||||||
void InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg);
|
void InstallDragDropPkg(std::filesystem::path file, int pkgNum, int nPkg);
|
||||||
void InstallDirectory();
|
void InstallDirectory();
|
||||||
void StartGame();
|
void StartGame();
|
||||||
|
void StopGame();
|
||||||
|
void RestartGame();
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void ConfigureGuiFromSettings();
|
void ConfigureGuiFromSettings();
|
||||||
@ -65,6 +67,7 @@ private:
|
|||||||
void SetUiIcons(bool isWhite);
|
void SetUiIcons(bool isWhite);
|
||||||
void InstallPkg();
|
void InstallPkg();
|
||||||
void BootGame();
|
void BootGame();
|
||||||
|
QString getLastEbootPath();
|
||||||
void AddRecentFiles(QString filePath);
|
void AddRecentFiles(QString filePath);
|
||||||
void LoadTranslation();
|
void LoadTranslation();
|
||||||
void PlayBackgroundMusic();
|
void PlayBackgroundMusic();
|
||||||
|
@ -48,6 +48,8 @@ public:
|
|||||||
QPushButton* settingsButton;
|
QPushButton* settingsButton;
|
||||||
QPushButton* controllerButton;
|
QPushButton* controllerButton;
|
||||||
QPushButton* keyboardButton;
|
QPushButton* keyboardButton;
|
||||||
|
QPushButton* restartButton;
|
||||||
|
|
||||||
|
|
||||||
QWidget* sizeSliderContainer;
|
QWidget* sizeSliderContainer;
|
||||||
QHBoxLayout* sizeSliderContainer_layout;
|
QHBoxLayout* sizeSliderContainer_layout;
|
||||||
@ -215,6 +217,10 @@ public:
|
|||||||
keyboardButton->setFlat(true);
|
keyboardButton->setFlat(true);
|
||||||
keyboardButton->setIcon(QIcon(":images/keyboard_icon.png"));
|
keyboardButton->setIcon(QIcon(":images/keyboard_icon.png"));
|
||||||
keyboardButton->setIconSize(QSize(48, 44));
|
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 = new QWidget(centralWidget);
|
||||||
sizeSliderContainer->setObjectName("sizeSliderContainer");
|
sizeSliderContainer->setObjectName("sizeSliderContainer");
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// 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_events.h"
|
||||||
#include "SDL3/SDL_hints.h"
|
#include "SDL3/SDL_hints.h"
|
||||||
#include "SDL3/SDL_init.h"
|
#include "SDL3/SDL_init.h"
|
||||||
@ -363,14 +368,67 @@ void WindowSDL::WaitEvent() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_EVENT_QUIT:
|
case SDL_EVENT_QUIT:
|
||||||
is_open = false;
|
is_open = false;
|
||||||
break;
|
break;
|
||||||
default:
|
case SDL_EVENT_QUIT + 1:
|
||||||
|
is_open = false;
|
||||||
|
RelaunchEmulator();
|
||||||
break;
|
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() {
|
void WindowSDL::InitTimers() {
|
||||||
SDL_AddTimer(100, &PollController, controller);
|
SDL_AddTimer(100, &PollController, controller);
|
||||||
SDL_AddTimer(33, Input::MousePolling, (void*)controller);
|
SDL_AddTimer(33, Input::MousePolling, (void*)controller);
|
||||||
|
@ -97,6 +97,7 @@ private:
|
|||||||
void OnResize();
|
void OnResize();
|
||||||
void OnKeyboardMouseInput(const SDL_Event* event);
|
void OnKeyboardMouseInput(const SDL_Event* event);
|
||||||
void OnGamepadEvent(const SDL_Event* event);
|
void OnGamepadEvent(const SDL_Event* event);
|
||||||
|
void RelaunchEmulator();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
s32 width;
|
s32 width;
|
||||||
@ -106,6 +107,7 @@ private:
|
|||||||
SDL_Window* window{};
|
SDL_Window* window{};
|
||||||
bool is_shown{};
|
bool is_shown{};
|
||||||
bool is_open{true};
|
bool is_open{true};
|
||||||
|
bool restartRequested = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Frontend
|
} // namespace Frontend
|
||||||
|
@ -32,5 +32,6 @@
|
|||||||
<file>images/website.png</file>
|
<file>images/website.png</file>
|
||||||
<file>images/ps4_controller.png</file>
|
<file>images/ps4_controller.png</file>
|
||||||
<file>images/keyboard_icon.png</file>
|
<file>images/keyboard_icon.png</file>
|
||||||
|
<file>images/restart_icon.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
Loading…
Reference in New Issue
Block a user