mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-12-08 20:58:41 +00:00
Impl sceSystemServiceLoadExec (#3647)
* Add support for restarting the emulator with new configurations - Implement `Restart` function in `Emulator` to enable process relaunch with updated parameters. - Modify `sceSystemServiceLoadExec` to use the restart functionality. * Add logging for emulator restart and system service load execution * Add IPC emulator PID output command Impl `PID` output command to return the emulator process ID - required for launches supporting emulator restart * Add log file append mode support (used after restarting to keep the same log file) * Keep game root between restarts * add --wait-for-debugger option flag * add --wait-for-pid flag used for sync between parent & child process during restart * impl restart via ipc * fix override game root * add qt flags to allow restart
This commit is contained in:
@@ -824,6 +824,8 @@ set(CORE src/core/aerolib/stubs.cpp
|
||||
${DEV_TOOLS}
|
||||
src/core/debug_state.cpp
|
||||
src/core/debug_state.h
|
||||
src/core/debugger.cpp
|
||||
src/core/debugger.h
|
||||
src/core/linker.cpp
|
||||
src/core/linker.h
|
||||
src/core/memory.cpp
|
||||
|
||||
@@ -61,8 +61,9 @@ private:
|
||||
*/
|
||||
class FileBackend {
|
||||
public:
|
||||
explicit FileBackend(const std::filesystem::path& filename)
|
||||
: file{filename, FS::FileAccessMode::Write, FS::FileType::TextFile} {}
|
||||
explicit FileBackend(const std::filesystem::path& filename, bool should_append = false)
|
||||
: file{filename, should_append ? FS::FileAccessMode::Append : FS::FileAccessMode::Write,
|
||||
FS::FileType::TextFile} {}
|
||||
|
||||
~FileBackend() = default;
|
||||
|
||||
@@ -145,6 +146,11 @@ public:
|
||||
initialization_in_progress_suppress_logging = false;
|
||||
}
|
||||
|
||||
static void ResetInstance() {
|
||||
initialization_in_progress_suppress_logging = true;
|
||||
instance.reset();
|
||||
}
|
||||
|
||||
static bool IsActive() {
|
||||
return instance != nullptr;
|
||||
}
|
||||
@@ -157,6 +163,10 @@ public:
|
||||
instance->StopBackendThread();
|
||||
}
|
||||
|
||||
static void SetAppend() {
|
||||
should_append = true;
|
||||
}
|
||||
|
||||
Impl(const Impl&) = delete;
|
||||
Impl& operator=(const Impl&) = delete;
|
||||
|
||||
@@ -218,7 +228,7 @@ public:
|
||||
|
||||
private:
|
||||
Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
|
||||
: filter{filter_}, file_backend{file_backend_filename} {}
|
||||
: filter{filter_}, file_backend{file_backend_filename, should_append} {}
|
||||
|
||||
~Impl() = default;
|
||||
|
||||
@@ -264,6 +274,7 @@ private:
|
||||
}
|
||||
|
||||
static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter};
|
||||
static inline bool should_append{false};
|
||||
|
||||
Filter filter;
|
||||
DebuggerBackend debugger_backend{};
|
||||
@@ -292,6 +303,11 @@ void Stop() {
|
||||
Impl::Stop();
|
||||
}
|
||||
|
||||
void Denitializer() {
|
||||
Impl::Stop();
|
||||
Impl::ResetInstance();
|
||||
}
|
||||
|
||||
void SetGlobalFilter(const Filter& filter) {
|
||||
Impl::Instance().SetGlobalFilter(filter);
|
||||
}
|
||||
@@ -300,6 +316,10 @@ void SetColorConsoleBackendEnabled(bool enabled) {
|
||||
Impl::Instance().SetColorConsoleBackendEnabled(enabled);
|
||||
}
|
||||
|
||||
void SetAppend() {
|
||||
Impl::SetAppend();
|
||||
}
|
||||
|
||||
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
||||
unsigned int line_num, const char* function, const char* format,
|
||||
const fmt::format_args& args) {
|
||||
|
||||
@@ -21,9 +21,14 @@ void Start();
|
||||
/// Explictily stops the logger thread and flushes the buffers
|
||||
void Stop();
|
||||
|
||||
/// Closes log files and stops the logger
|
||||
void Denitializer();
|
||||
|
||||
/// The global filter will prevent any messages from even being processed if they are filtered.
|
||||
void SetGlobalFilter(const Filter& filter);
|
||||
|
||||
void SetColorConsoleBackendEnabled(bool enabled);
|
||||
|
||||
void SetAppend();
|
||||
|
||||
} // namespace Common::Log
|
||||
|
||||
99
src/core/debugger.cpp
Normal file
99
src/core/debugger.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "debugger.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <Windows.h>
|
||||
#include <debugapi.h>
|
||||
#elif defined(__linux__)
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#elif defined(__APPLE__)
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
bool Core::Debugger::IsDebuggerAttached() {
|
||||
#if defined(_WIN32)
|
||||
return IsDebuggerPresent();
|
||||
#elif defined(__linux__)
|
||||
std::ifstream status_file("/proc/self/status");
|
||||
std::string line;
|
||||
while (std::getline(status_file, line)) {
|
||||
if (line.starts_with("TracerPid:")) {
|
||||
std::string tracer_pid = line.substr(10);
|
||||
tracer_pid.erase(0, tracer_pid.find_first_not_of(" \t"));
|
||||
return tracer_pid != "0";
|
||||
}
|
||||
}
|
||||
return false;
|
||||
#elif defined(__APPLE__)
|
||||
int mib[4];
|
||||
struct kinfo_proc info;
|
||||
size_t size = sizeof(info);
|
||||
|
||||
mib[0] = CTL_KERN;
|
||||
mib[1] = KERN_PROC;
|
||||
mib[2] = KERN_PROC_PID;
|
||||
mib[3] = getpid();
|
||||
|
||||
if (sysctl(mib, 4, &info, &size, nullptr, 0) == 0) {
|
||||
return (info.kp_proc.p_flag & P_TRACED) != 0;
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
}
|
||||
|
||||
void Core::Debugger::WaitForDebuggerAttach() {
|
||||
int count = 0;
|
||||
while (!IsDebuggerAttached()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
if (--count <= 0) {
|
||||
count = 10;
|
||||
std::cerr << "Waiting for debugger to attach..." << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
int Core::Debugger::GetCurrentPid() {
|
||||
#if defined(_WIN32)
|
||||
return GetCurrentProcessId();
|
||||
#elif defined(__APPLE__) || defined(__linux__)
|
||||
return getpid();
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
}
|
||||
|
||||
void Core::Debugger::WaitForPid(int pid) {
|
||||
#if defined(_WIN32)
|
||||
HANDLE process_handle = OpenProcess(SYNCHRONIZE, FALSE, pid);
|
||||
if (process_handle != nullptr) {
|
||||
std::cerr << "Waiting for process " << pid << " to exit..." << std::endl;
|
||||
WaitForSingleObject(process_handle, INFINITE);
|
||||
CloseHandle(process_handle);
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
std::string proc_path = "/proc/" + std::to_string(pid);
|
||||
|
||||
while (std::filesystem::exists(proc_path)) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
std::cerr << "Waiting for process " << pid << " to exit..." << std::endl;
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
while (kill(pid, 0) == 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
std::cerr << "Waiting for process " << pid << " to exit..." << std::endl;
|
||||
}
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
}
|
||||
16
src/core/debugger.h
Normal file
16
src/core/debugger.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Core::Debugger {
|
||||
|
||||
bool IsDebuggerAttached();
|
||||
|
||||
void WaitForDebuggerAttach();
|
||||
|
||||
int GetCurrentPid();
|
||||
|
||||
void WaitForPid(int pid);
|
||||
|
||||
} // namespace Core::Debugger
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "ipc.h"
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "common/thread.h"
|
||||
#include "common/types.h"
|
||||
#include "core/debug_state.h"
|
||||
#include "core/debugger.h"
|
||||
#include "input/input_handler.h"
|
||||
#include "sdl_window.h"
|
||||
|
||||
@@ -40,6 +41,7 @@
|
||||
* Command list:
|
||||
* - CAPABILITIES:
|
||||
* - ENABLE_MEMORY_PATCH: enables PATCH_MEMORY command
|
||||
* - ENABLE_EMU_CONTROL: enables PAUSE, RESUME, STOP, TOGGLE_FULLSCREEN commands
|
||||
* - INPUT CMD:
|
||||
* - RUN: start the emulator execution
|
||||
* - START: start the game execution
|
||||
@@ -53,7 +55,7 @@
|
||||
* - STOP: stop and quit the emulator
|
||||
* - TOGGLE_FULLSCREEN: enable / disable fullscreen
|
||||
* - OUTPUT CMD:
|
||||
* - N/A
|
||||
* - RESTART(argn: number, argv: ...string): Request restart of the emulator, must call STOP
|
||||
**/
|
||||
|
||||
void IPC::Init() {
|
||||
@@ -81,6 +83,15 @@ void IPC::Init() {
|
||||
}
|
||||
}
|
||||
|
||||
void IPC::SendRestart(const std::vector<std::string>& args) {
|
||||
std::cerr << ";RESTART\n";
|
||||
std::cerr << ";" << args.size() << "\n";
|
||||
for (const auto& arg : args) {
|
||||
std::cerr << ";" << arg << "\n";
|
||||
}
|
||||
std::cerr.flush();
|
||||
}
|
||||
|
||||
void IPC::InputLoop() {
|
||||
auto next_str = [&] -> const std::string& {
|
||||
static std::string line_buffer;
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
#include "common/singleton.h"
|
||||
|
||||
#include <semaphore>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
class IPC {
|
||||
bool enabled{false};
|
||||
@@ -34,6 +36,8 @@ public:
|
||||
start_semaphore.acquire();
|
||||
}
|
||||
|
||||
void SendRestart(const std::vector<std::string>& args);
|
||||
|
||||
private:
|
||||
[[noreturn]] void InputLoop();
|
||||
};
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/libs.h"
|
||||
#include "core/libraries/system/systemservice.h"
|
||||
#include "core/libraries/system/systemservice_error.h"
|
||||
#include "emulator.h"
|
||||
|
||||
namespace Libraries::SystemService {
|
||||
|
||||
@@ -1866,8 +1869,18 @@ int PS4_SYSV_ABI sceSystemServiceLaunchWebBrowser() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceSystemServiceLoadExec() {
|
||||
LOG_ERROR(Lib_SystemService, "(STUBBED) called");
|
||||
int PS4_SYSV_ABI sceSystemServiceLoadExec(const char* path, const char* argv[]) {
|
||||
LOG_DEBUG(Lib_SystemService, "called");
|
||||
auto emu = Common::Singleton<Core::Emulator>::Instance();
|
||||
auto mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
auto hostPath = mnt->GetHostPath(std::string_view(path));
|
||||
std::vector<std::string> args;
|
||||
if (argv != nullptr) {
|
||||
for (const char** ptr = argv; *ptr != nullptr; ptr++) {
|
||||
args.push_back(std::string(*ptr));
|
||||
}
|
||||
}
|
||||
emu->Restart(hostPath, args);
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -501,7 +501,7 @@ int PS4_SYSV_ABI sceSystemServiceLaunchEventDetails();
|
||||
int PS4_SYSV_ABI sceSystemServiceLaunchTournamentList();
|
||||
int PS4_SYSV_ABI sceSystemServiceLaunchTournamentsTeamProfile();
|
||||
int PS4_SYSV_ABI sceSystemServiceLaunchWebBrowser();
|
||||
int PS4_SYSV_ABI sceSystemServiceLoadExec();
|
||||
int PS4_SYSV_ABI sceSystemServiceLoadExec(const char* path, const char* argv[]);
|
||||
int PS4_SYSV_ABI sceSystemServiceNavigateToAnotherApp();
|
||||
int PS4_SYSV_ABI sceSystemServiceNavigateToGoBack();
|
||||
int PS4_SYSV_ABI sceSystemServiceNavigateToGoBackWithValue();
|
||||
|
||||
@@ -55,7 +55,7 @@ Linker::Linker() : memory{Memory::Instance()} {}
|
||||
|
||||
Linker::~Linker() = default;
|
||||
|
||||
void Linker::Execute(const std::vector<std::string> args) {
|
||||
void Linker::Execute(const std::vector<std::string>& args) {
|
||||
if (Config::debugDump()) {
|
||||
DebugDump();
|
||||
}
|
||||
@@ -115,7 +115,7 @@ void Linker::Execute(const std::vector<std::string> args) {
|
||||
0, "SceKernelInternalMemory");
|
||||
ASSERT_MSG(ret == 0, "Unable to perform sceKernelInternalMemory mapping");
|
||||
|
||||
main_thread.Run([this, module, args](std::stop_token) {
|
||||
main_thread.Run([this, module, &args](std::stop_token) {
|
||||
Common::SetCurrentThreadName("GAME_MainThread");
|
||||
if (auto& ipc = IPC::Instance()) {
|
||||
ipc.WaitForStart();
|
||||
@@ -140,9 +140,9 @@ void Linker::Execute(const std::vector<std::string> args) {
|
||||
params.argc = 1;
|
||||
params.argv[0] = "eboot.bin";
|
||||
if (!args.empty()) {
|
||||
params.argc = args.size() + 1;
|
||||
for (int i = 0; i < args.size() && i < 32; i++) {
|
||||
params.argv[i + 1] = args[i].c_str();
|
||||
params.argc = args.size();
|
||||
for (int i = 0; i < args.size() && i < 33; i++) {
|
||||
params.argv[i] = args[i].c_str();
|
||||
}
|
||||
}
|
||||
params.entry_addr = module->GetEntryAddress();
|
||||
|
||||
@@ -151,7 +151,7 @@ public:
|
||||
void Relocate(Module* module);
|
||||
bool Resolve(const std::string& name, Loader::SymbolType type, Module* module,
|
||||
Loader::SymbolRecord* return_info);
|
||||
void Execute(const std::vector<std::string> args = {});
|
||||
void Execute(const std::vector<std::string>& args = {});
|
||||
void DebugDump();
|
||||
|
||||
private:
|
||||
|
||||
159
src/emulator.cpp
159
src/emulator.cpp
@@ -3,15 +3,18 @@
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/xchar.h>
|
||||
#include <hwinfo/hwinfo.h>
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/ipc/ipc.h"
|
||||
#ifdef ENABLE_QT_GUI
|
||||
#include <QtCore>
|
||||
#endif
|
||||
@@ -19,9 +22,6 @@
|
||||
#ifdef ENABLE_DISCORD_RPC
|
||||
#include "common/discord_rpc_handler.h"
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#include <WinSock2.h>
|
||||
#endif
|
||||
#include "common/elf_info.h"
|
||||
#include "common/memory_patcher.h"
|
||||
#include "common/ntapi.h"
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/debugger.h"
|
||||
#include "core/devtools/widget/module_list.h"
|
||||
#include "core/file_format/psf.h"
|
||||
#include "core/file_format/trp.h"
|
||||
@@ -45,6 +46,15 @@
|
||||
#include "emulator.h"
|
||||
#include "video_core/renderdoc.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <WinSock2.h>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
Frontend::WindowSDL* g_window = nullptr;
|
||||
|
||||
namespace Core {
|
||||
@@ -63,25 +73,35 @@ Emulator::Emulator() {
|
||||
|
||||
Emulator::~Emulator() {}
|
||||
|
||||
void Emulator::Run(std::filesystem::path file, const std::vector<std::string> args) {
|
||||
void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
std::optional<std::filesystem::path> p_game_folder) {
|
||||
if (waitForDebuggerBeforeRun) {
|
||||
Debugger::WaitForDebuggerAttach();
|
||||
}
|
||||
|
||||
if (std::filesystem::is_directory(file)) {
|
||||
file /= "eboot.bin";
|
||||
}
|
||||
|
||||
const auto eboot_name = file.filename().string();
|
||||
|
||||
auto game_folder = file.parent_path();
|
||||
if (const auto game_folder_name = game_folder.filename().string();
|
||||
game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch")) {
|
||||
// If an executable was launched from a separate update directory,
|
||||
// use the base game directory as the game folder.
|
||||
const std::string base_name = game_folder_name.substr(0, game_folder_name.rfind('-'));
|
||||
const auto base_path = game_folder.parent_path() / base_name;
|
||||
if (std::filesystem::is_directory(base_path)) {
|
||||
game_folder = base_path;
|
||||
std::filesystem::path game_folder;
|
||||
if (p_game_folder.has_value()) {
|
||||
game_folder = p_game_folder.value();
|
||||
} else {
|
||||
game_folder = file.parent_path();
|
||||
if (const auto game_folder_name = game_folder.filename().string();
|
||||
game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch")) {
|
||||
// If an executable was launched from a separate update directory,
|
||||
// use the base game directory as the game folder.
|
||||
const std::string base_name = game_folder_name.substr(0, game_folder_name.rfind('-'));
|
||||
const auto base_path = game_folder.parent_path() / base_name;
|
||||
if (std::filesystem::is_directory(base_path)) {
|
||||
game_folder = base_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path eboot_name = std::filesystem::relative(file, game_folder);
|
||||
|
||||
// Applications expect to be run from /app0 so mount the file's parent path as app0.
|
||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
mnt->Mount(game_folder, "/app0", true);
|
||||
@@ -290,10 +310,11 @@ void Emulator::Run(std::filesystem::path file, const std::vector<std::string> ar
|
||||
Libraries::InitHLELibs(&linker->GetHLESymbols());
|
||||
|
||||
// Load the module with the linker
|
||||
const auto eboot_path = mnt->GetHostPath("/app0/" + eboot_name);
|
||||
auto guest_eboot_path = "/app0/" + eboot_name.generic_string();
|
||||
const auto eboot_path = mnt->GetHostPath(guest_eboot_path);
|
||||
if (linker->LoadModule(eboot_path) == -1) {
|
||||
LOG_CRITICAL(Loader, "Failed to load game's eboot.bin: {}",
|
||||
std::filesystem::absolute(eboot_path).string());
|
||||
Common::FS::PathToUTF8String(std::filesystem::absolute(eboot_path)));
|
||||
std::quick_exit(0);
|
||||
}
|
||||
|
||||
@@ -334,6 +355,7 @@ void Emulator::Run(std::filesystem::path file, const std::vector<std::string> ar
|
||||
}
|
||||
#endif
|
||||
|
||||
args.insert(args.begin(), eboot_name.generic_string());
|
||||
linker->Execute(args);
|
||||
|
||||
window->InitTimers();
|
||||
@@ -348,6 +370,109 @@ void Emulator::Run(std::filesystem::path file, const std::vector<std::string> ar
|
||||
std::quick_exit(0);
|
||||
}
|
||||
|
||||
void Emulator::Restart(std::filesystem::path eboot_path,
|
||||
const std::vector<std::string>& guest_args) {
|
||||
std::vector<std::string> args;
|
||||
|
||||
auto mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||
auto game_path = mnt->GetHostPath("/app0");
|
||||
|
||||
args.push_back("--log-append");
|
||||
args.push_back("--game");
|
||||
args.push_back(Common::FS::PathToUTF8String(eboot_path));
|
||||
|
||||
args.push_back("--override-root");
|
||||
args.push_back(Common::FS::PathToUTF8String(game_path));
|
||||
|
||||
if (FileSys::MntPoints::ignore_game_patches) {
|
||||
args.push_back("--ignore-game-patch");
|
||||
}
|
||||
|
||||
if (!MemoryPatcher::patchFile.empty()) {
|
||||
args.push_back("--patch");
|
||||
args.push_back(MemoryPatcher::patchFile);
|
||||
}
|
||||
|
||||
args.push_back("--wait-for-pid");
|
||||
args.push_back(std::to_string(Debugger::GetCurrentPid()));
|
||||
|
||||
if (waitForDebuggerBeforeRun) {
|
||||
args.push_back("--wait-for-debugger");
|
||||
}
|
||||
|
||||
if (guest_args.size() > 0) {
|
||||
args.push_back("--");
|
||||
for (const auto& arg : guest_args) {
|
||||
args.push_back(arg);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO(Common, "Restarting the emulator with args: {}", fmt::join(args, " "));
|
||||
Libraries::SaveData::Backup::StopThread();
|
||||
Common::Log::Denitializer();
|
||||
|
||||
auto& ipc = IPC::Instance();
|
||||
|
||||
if (ipc.IsEnabled()) {
|
||||
ipc.SendRestart(args);
|
||||
while (true) {
|
||||
std::this_thread::sleep_for(std::chrono::minutes(1));
|
||||
}
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
std::string cmdline;
|
||||
// Emulator executable
|
||||
cmdline += "\"";
|
||||
cmdline += executableName;
|
||||
cmdline += "\"";
|
||||
for (const auto& arg : args) {
|
||||
cmdline += " \"";
|
||||
cmdline += arg;
|
||||
cmdline += "\"";
|
||||
}
|
||||
cmdline += "\0";
|
||||
|
||||
STARTUPINFOA si{};
|
||||
si.cb = sizeof(si);
|
||||
PROCESS_INFORMATION pi{};
|
||||
bool success = CreateProcessA(nullptr, cmdline.data(), nullptr, nullptr, TRUE, 0, nullptr,
|
||||
nullptr, &si, &pi);
|
||||
|
||||
if (!success) {
|
||||
std::cerr << "Failed to restart game: {}" << GetLastError() << std::endl;
|
||||
std::quick_exit(1);
|
||||
}
|
||||
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
#elif defined(__APPLE__) || defined(__linux__)
|
||||
std::vector<char*> argv;
|
||||
|
||||
// Emulator executable
|
||||
argv.push_back(const_cast<char*>(executableName));
|
||||
|
||||
for (const auto& arg : args) {
|
||||
argv.push_back(const_cast<char*>(arg.c_str()));
|
||||
}
|
||||
argv.push_back(nullptr);
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
// Child process - execute the new instance
|
||||
execvp(executableName, argv.data());
|
||||
std::cerr << "Failed to restart game: execvp failed" << std::endl;
|
||||
std::quick_exit(1);
|
||||
} else if (pid < 0) {
|
||||
std::cerr << "Failed to restart game: fork failed" << std::endl;
|
||||
std::quick_exit(1);
|
||||
}
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
std::quick_exit(0);
|
||||
}
|
||||
|
||||
void Emulator::LoadSystemModules(const std::string& game_serial) {
|
||||
constexpr auto ModulesToLoad = std::to_array<SysModules>(
|
||||
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterLib},
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
|
||||
#include "common/singleton.h"
|
||||
@@ -25,9 +26,19 @@ public:
|
||||
Emulator();
|
||||
~Emulator();
|
||||
|
||||
void Run(std::filesystem::path file, const std::vector<std::string> args = {});
|
||||
void Run(std::filesystem::path file, std::vector<std::string> args = {},
|
||||
std::optional<std::filesystem::path> game_folder = {});
|
||||
void UpdatePlayTime(const std::string& serial);
|
||||
|
||||
/**
|
||||
* This will kill the current process and launch a new process with the same configuration
|
||||
* (using CLI args) but replacing the eboot image and guest arguments
|
||||
*/
|
||||
void Restart(std::filesystem::path eboot_path, const std::vector<std::string>& guest_args = {});
|
||||
|
||||
const char* executableName;
|
||||
bool waitForDebuggerBeforeRun{false};
|
||||
|
||||
private:
|
||||
void LoadSystemModules(const std::string& game_serial);
|
||||
|
||||
|
||||
50
src/main.cpp
50
src/main.cpp
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "functional"
|
||||
@@ -9,8 +9,10 @@
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include "common/config.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/memory_patcher.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/debugger.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/ipc/ipc.h"
|
||||
#include "emulator.h"
|
||||
@@ -32,6 +34,10 @@ int main(int argc, char* argv[]) {
|
||||
bool has_game_argument = false;
|
||||
std::string game_path;
|
||||
std::vector<std::string> game_args{};
|
||||
std::optional<std::filesystem::path> game_folder;
|
||||
|
||||
bool waitForDebugger = false;
|
||||
std::optional<int> waitPid;
|
||||
|
||||
// Map of argument strings to lambda functions
|
||||
std::unordered_map<std::string, std::function<void(int&)>> arg_map = {
|
||||
@@ -50,6 +56,12 @@ int main(int argc, char* argv[]) {
|
||||
"state. Does not overwrite the config file.\n"
|
||||
" --add-game-folder <folder> Adds a new game folder to the config.\n"
|
||||
" --set-addon-folder <folder> Sets the addon folder to the config.\n"
|
||||
" --log-append Append log output to file instead of "
|
||||
"overwriting it.\n"
|
||||
" --override-root <folder> Override the game root folder. Default is the "
|
||||
"parent of game path\n"
|
||||
" --wait-for-debugger Wait for debugger to attach\n"
|
||||
" --wait-for-pid <pid> Wait for process with specified PID to stop\n"
|
||||
" -h, --help Display this help message\n";
|
||||
exit(0);
|
||||
}},
|
||||
@@ -119,7 +131,8 @@ int main(int argc, char* argv[]) {
|
||||
std::cout << "Game folder successfully saved.\n";
|
||||
exit(0);
|
||||
}},
|
||||
{"--set-addon-folder", [&](int& i) {
|
||||
{"--set-addon-folder",
|
||||
[&](int& i) {
|
||||
if (++i >= argc) {
|
||||
std::cerr << "Error: Missing argument for --add-addon-folder\n";
|
||||
exit(1);
|
||||
@@ -136,6 +149,29 @@ int main(int argc, char* argv[]) {
|
||||
Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml");
|
||||
std::cout << "Addon folder successfully saved.\n";
|
||||
exit(0);
|
||||
}},
|
||||
{"--log-append", [&](int& i) { Common::Log::SetAppend(); }},
|
||||
{"--override-root",
|
||||
[&](int& i) {
|
||||
if (++i >= argc) {
|
||||
std::cerr << "Error: Missing argument for --override-root\n";
|
||||
exit(1);
|
||||
}
|
||||
std::string folder_str{argv[i]};
|
||||
std::filesystem::path folder{folder_str};
|
||||
if (!std::filesystem::exists(folder) || !std::filesystem::is_directory(folder)) {
|
||||
std::cerr << "Error: Folder does not exist: " << folder_str << "\n";
|
||||
exit(1);
|
||||
}
|
||||
game_folder = folder;
|
||||
}},
|
||||
{"--wait-for-debugger", [&](int& i) { waitForDebugger = true; }},
|
||||
{"--wait-for-pid", [&](int& i) {
|
||||
if (++i >= argc) {
|
||||
std::cerr << "Error: Missing argument for --wait-for-pid\n";
|
||||
exit(1);
|
||||
}
|
||||
waitPid = std::stoi(argv[i]);
|
||||
}}};
|
||||
|
||||
if (argc == 1) {
|
||||
@@ -206,9 +242,15 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (waitPid.has_value()) {
|
||||
Core::Debugger::WaitForPid(waitPid.value());
|
||||
}
|
||||
|
||||
// Run the emulator with the resolved eboot path
|
||||
Core::Emulator emulator;
|
||||
emulator.Run(eboot_path, game_args);
|
||||
Core::Emulator* emulator = Common::Singleton<Core::Emulator>::Instance();
|
||||
emulator->executableName = argv[0];
|
||||
emulator->waitForDebuggerBeforeRun = waitForDebugger;
|
||||
emulator->Run(eboot_path, game_args, game_folder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,9 @@
|
||||
#include "unordered_map"
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/memory_patcher.h"
|
||||
#include "core/debugger.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "emulator.h"
|
||||
#include "game_install_dialog.h"
|
||||
@@ -36,6 +38,10 @@ int main(int argc, char* argv[]) {
|
||||
bool show_gui = false, has_game_argument = false;
|
||||
std::string game_path;
|
||||
std::vector<std::string> game_args{};
|
||||
std::optional<std::filesystem::path> game_folder;
|
||||
|
||||
bool waitForDebugger = false;
|
||||
std::optional<int> waitPid;
|
||||
|
||||
// Map of argument strings to lambda functions
|
||||
std::unordered_map<std::string, std::function<void(int&)>> arg_map = {
|
||||
@@ -56,6 +62,12 @@ int main(int argc, char* argv[]) {
|
||||
" -f, --fullscreen <true|false> Specify window initial fullscreen "
|
||||
"state. Does not overwrite the config file.\n"
|
||||
" --add-game-folder <folder> Adds a new game folder to the config.\n"
|
||||
" --log-append Append log output to file instead of "
|
||||
"overwriting it.\n"
|
||||
" --override-root <folder> Override the game root folder. Default is the "
|
||||
"parent of game path\n"
|
||||
" --wait-for-debugger Wait for debugger to attach\n"
|
||||
" --wait-for-pid <pid> Wait for process with specified PID to stop\n"
|
||||
" -h, --help Display this help message\n";
|
||||
exit(0);
|
||||
}},
|
||||
@@ -129,7 +141,29 @@ int main(int argc, char* argv[]) {
|
||||
std::cout << "Game folder successfully saved.\n";
|
||||
exit(0);
|
||||
}},
|
||||
};
|
||||
{"--log-append", [&](int& i) { Common::Log::SetAppend(); }},
|
||||
{"--override-root",
|
||||
[&](int& i) {
|
||||
if (++i >= argc) {
|
||||
std::cerr << "Error: Missing argument for --override-root\n";
|
||||
exit(1);
|
||||
}
|
||||
std::string folder_str{argv[i]};
|
||||
std::filesystem::path folder{folder_str};
|
||||
if (!std::filesystem::exists(folder) || !std::filesystem::is_directory(folder)) {
|
||||
std::cerr << "Error: Folder does not exist: " << folder_str << "\n";
|
||||
exit(1);
|
||||
}
|
||||
game_folder = folder;
|
||||
}},
|
||||
{"--wait-for-debugger", [&](int& i) { waitForDebugger = true; }},
|
||||
{"--wait-for-pid", [&](int& i) {
|
||||
if (++i >= argc) {
|
||||
std::cerr << "Error: Missing argument for --wait-for-pid\n";
|
||||
exit(1);
|
||||
}
|
||||
waitPid = std::stoi(argv[i]);
|
||||
}}};
|
||||
|
||||
// Parse command-line arguments using the map
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
@@ -181,6 +215,14 @@ int main(int argc, char* argv[]) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (waitPid.has_value()) {
|
||||
Core::Debugger::WaitForPid(waitPid.value());
|
||||
}
|
||||
|
||||
Core::Emulator* emulator = Common::Singleton<Core::Emulator>::Instance();
|
||||
emulator->executableName = argv[0];
|
||||
emulator->waitForDebuggerBeforeRun = waitForDebugger;
|
||||
|
||||
// Process game path or ID if provided
|
||||
if (has_game_argument) {
|
||||
std::filesystem::path game_file_path(game_path);
|
||||
@@ -204,8 +246,7 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
// Run the emulator with the resolved game path
|
||||
Core::Emulator emulator;
|
||||
emulator.Run(game_file_path.string(), game_args);
|
||||
emulator->Run(game_file_path.string(), game_args, game_folder);
|
||||
if (!show_gui) {
|
||||
return 0; // Exit after running the emulator without showing the GUI
|
||||
}
|
||||
|
||||
@@ -1225,12 +1225,12 @@ void MainWindow::StartEmulator(std::filesystem::path path) {
|
||||
isGameRunning = true;
|
||||
#ifdef __APPLE__
|
||||
// SDL on macOS requires main thread.
|
||||
Core::Emulator emulator;
|
||||
emulator.Run(path);
|
||||
Core::Emulator* emulator = Common::Singleton<Core::Emulator>::Instance();
|
||||
emulator->Run(path);
|
||||
#else
|
||||
std::thread emulator_thread([=] {
|
||||
Core::Emulator emulator;
|
||||
emulator.Run(path);
|
||||
Core::Emulator* emulator = Common::Singleton<Core::Emulator>::Instance();
|
||||
emulator->Run(path);
|
||||
});
|
||||
emulator_thread.detach();
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user