mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-12-09 13:19:00 +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}
|
${DEV_TOOLS}
|
||||||
src/core/debug_state.cpp
|
src/core/debug_state.cpp
|
||||||
src/core/debug_state.h
|
src/core/debug_state.h
|
||||||
|
src/core/debugger.cpp
|
||||||
|
src/core/debugger.h
|
||||||
src/core/linker.cpp
|
src/core/linker.cpp
|
||||||
src/core/linker.h
|
src/core/linker.h
|
||||||
src/core/memory.cpp
|
src/core/memory.cpp
|
||||||
|
|||||||
@@ -61,8 +61,9 @@ private:
|
|||||||
*/
|
*/
|
||||||
class FileBackend {
|
class FileBackend {
|
||||||
public:
|
public:
|
||||||
explicit FileBackend(const std::filesystem::path& filename)
|
explicit FileBackend(const std::filesystem::path& filename, bool should_append = false)
|
||||||
: file{filename, FS::FileAccessMode::Write, FS::FileType::TextFile} {}
|
: file{filename, should_append ? FS::FileAccessMode::Append : FS::FileAccessMode::Write,
|
||||||
|
FS::FileType::TextFile} {}
|
||||||
|
|
||||||
~FileBackend() = default;
|
~FileBackend() = default;
|
||||||
|
|
||||||
@@ -145,6 +146,11 @@ public:
|
|||||||
initialization_in_progress_suppress_logging = false;
|
initialization_in_progress_suppress_logging = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ResetInstance() {
|
||||||
|
initialization_in_progress_suppress_logging = true;
|
||||||
|
instance.reset();
|
||||||
|
}
|
||||||
|
|
||||||
static bool IsActive() {
|
static bool IsActive() {
|
||||||
return instance != nullptr;
|
return instance != nullptr;
|
||||||
}
|
}
|
||||||
@@ -157,6 +163,10 @@ public:
|
|||||||
instance->StopBackendThread();
|
instance->StopBackendThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void SetAppend() {
|
||||||
|
should_append = true;
|
||||||
|
}
|
||||||
|
|
||||||
Impl(const Impl&) = delete;
|
Impl(const Impl&) = delete;
|
||||||
Impl& operator=(const Impl&) = delete;
|
Impl& operator=(const Impl&) = delete;
|
||||||
|
|
||||||
@@ -218,7 +228,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
|
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;
|
~Impl() = default;
|
||||||
|
|
||||||
@@ -264,6 +274,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter};
|
static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter};
|
||||||
|
static inline bool should_append{false};
|
||||||
|
|
||||||
Filter filter;
|
Filter filter;
|
||||||
DebuggerBackend debugger_backend{};
|
DebuggerBackend debugger_backend{};
|
||||||
@@ -292,6 +303,11 @@ void Stop() {
|
|||||||
Impl::Stop();
|
Impl::Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Denitializer() {
|
||||||
|
Impl::Stop();
|
||||||
|
Impl::ResetInstance();
|
||||||
|
}
|
||||||
|
|
||||||
void SetGlobalFilter(const Filter& filter) {
|
void SetGlobalFilter(const Filter& filter) {
|
||||||
Impl::Instance().SetGlobalFilter(filter);
|
Impl::Instance().SetGlobalFilter(filter);
|
||||||
}
|
}
|
||||||
@@ -300,6 +316,10 @@ void SetColorConsoleBackendEnabled(bool enabled) {
|
|||||||
Impl::Instance().SetColorConsoleBackendEnabled(enabled);
|
Impl::Instance().SetColorConsoleBackendEnabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetAppend() {
|
||||||
|
Impl::SetAppend();
|
||||||
|
}
|
||||||
|
|
||||||
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
||||||
unsigned int line_num, const char* function, const char* format,
|
unsigned int line_num, const char* function, const char* format,
|
||||||
const fmt::format_args& args) {
|
const fmt::format_args& args) {
|
||||||
|
|||||||
@@ -21,9 +21,14 @@ void Start();
|
|||||||
/// Explictily stops the logger thread and flushes the buffers
|
/// Explictily stops the logger thread and flushes the buffers
|
||||||
void Stop();
|
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.
|
/// The global filter will prevent any messages from even being processed if they are filtered.
|
||||||
void SetGlobalFilter(const Filter& filter);
|
void SetGlobalFilter(const Filter& filter);
|
||||||
|
|
||||||
void SetColorConsoleBackendEnabled(bool enabled);
|
void SetColorConsoleBackendEnabled(bool enabled);
|
||||||
|
|
||||||
|
void SetAppend();
|
||||||
|
|
||||||
} // namespace Common::Log
|
} // 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
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "common/thread.h"
|
#include "common/thread.h"
|
||||||
#include "common/types.h"
|
#include "common/types.h"
|
||||||
#include "core/debug_state.h"
|
#include "core/debug_state.h"
|
||||||
|
#include "core/debugger.h"
|
||||||
#include "input/input_handler.h"
|
#include "input/input_handler.h"
|
||||||
#include "sdl_window.h"
|
#include "sdl_window.h"
|
||||||
|
|
||||||
@@ -40,6 +41,7 @@
|
|||||||
* Command list:
|
* Command list:
|
||||||
* - CAPABILITIES:
|
* - CAPABILITIES:
|
||||||
* - ENABLE_MEMORY_PATCH: enables PATCH_MEMORY command
|
* - ENABLE_MEMORY_PATCH: enables PATCH_MEMORY command
|
||||||
|
* - ENABLE_EMU_CONTROL: enables PAUSE, RESUME, STOP, TOGGLE_FULLSCREEN commands
|
||||||
* - INPUT CMD:
|
* - INPUT CMD:
|
||||||
* - RUN: start the emulator execution
|
* - RUN: start the emulator execution
|
||||||
* - START: start the game execution
|
* - START: start the game execution
|
||||||
@@ -53,7 +55,7 @@
|
|||||||
* - STOP: stop and quit the emulator
|
* - STOP: stop and quit the emulator
|
||||||
* - TOGGLE_FULLSCREEN: enable / disable fullscreen
|
* - TOGGLE_FULLSCREEN: enable / disable fullscreen
|
||||||
* - OUTPUT CMD:
|
* - OUTPUT CMD:
|
||||||
* - N/A
|
* - RESTART(argn: number, argv: ...string): Request restart of the emulator, must call STOP
|
||||||
**/
|
**/
|
||||||
|
|
||||||
void IPC::Init() {
|
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() {
|
void IPC::InputLoop() {
|
||||||
auto next_str = [&] -> const std::string& {
|
auto next_str = [&] -> const std::string& {
|
||||||
static std::string line_buffer;
|
static std::string line_buffer;
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
|
|
||||||
#include <semaphore>
|
#include <semaphore>
|
||||||
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
class IPC {
|
class IPC {
|
||||||
bool enabled{false};
|
bool enabled{false};
|
||||||
@@ -34,6 +36,8 @@ public:
|
|||||||
start_semaphore.acquire();
|
start_semaphore.acquire();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendRestart(const std::vector<std::string>& args);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[noreturn]] void InputLoop();
|
[[noreturn]] void InputLoop();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
|
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/singleton.h"
|
||||||
|
#include "core/file_sys/fs.h"
|
||||||
#include "core/libraries/libs.h"
|
#include "core/libraries/libs.h"
|
||||||
#include "core/libraries/system/systemservice.h"
|
#include "core/libraries/system/systemservice.h"
|
||||||
#include "core/libraries/system/systemservice_error.h"
|
#include "core/libraries/system/systemservice_error.h"
|
||||||
|
#include "emulator.h"
|
||||||
|
|
||||||
namespace Libraries::SystemService {
|
namespace Libraries::SystemService {
|
||||||
|
|
||||||
@@ -1866,8 +1869,18 @@ int PS4_SYSV_ABI sceSystemServiceLaunchWebBrowser() {
|
|||||||
return ORBIS_OK;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI sceSystemServiceLoadExec() {
|
int PS4_SYSV_ABI sceSystemServiceLoadExec(const char* path, const char* argv[]) {
|
||||||
LOG_ERROR(Lib_SystemService, "(STUBBED) called");
|
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;
|
return ORBIS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -501,7 +501,7 @@ int PS4_SYSV_ABI sceSystemServiceLaunchEventDetails();
|
|||||||
int PS4_SYSV_ABI sceSystemServiceLaunchTournamentList();
|
int PS4_SYSV_ABI sceSystemServiceLaunchTournamentList();
|
||||||
int PS4_SYSV_ABI sceSystemServiceLaunchTournamentsTeamProfile();
|
int PS4_SYSV_ABI sceSystemServiceLaunchTournamentsTeamProfile();
|
||||||
int PS4_SYSV_ABI sceSystemServiceLaunchWebBrowser();
|
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 sceSystemServiceNavigateToAnotherApp();
|
||||||
int PS4_SYSV_ABI sceSystemServiceNavigateToGoBack();
|
int PS4_SYSV_ABI sceSystemServiceNavigateToGoBack();
|
||||||
int PS4_SYSV_ABI sceSystemServiceNavigateToGoBackWithValue();
|
int PS4_SYSV_ABI sceSystemServiceNavigateToGoBackWithValue();
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ Linker::Linker() : memory{Memory::Instance()} {}
|
|||||||
|
|
||||||
Linker::~Linker() = default;
|
Linker::~Linker() = default;
|
||||||
|
|
||||||
void Linker::Execute(const std::vector<std::string> args) {
|
void Linker::Execute(const std::vector<std::string>& args) {
|
||||||
if (Config::debugDump()) {
|
if (Config::debugDump()) {
|
||||||
DebugDump();
|
DebugDump();
|
||||||
}
|
}
|
||||||
@@ -115,7 +115,7 @@ void Linker::Execute(const std::vector<std::string> args) {
|
|||||||
0, "SceKernelInternalMemory");
|
0, "SceKernelInternalMemory");
|
||||||
ASSERT_MSG(ret == 0, "Unable to perform sceKernelInternalMemory mapping");
|
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");
|
Common::SetCurrentThreadName("GAME_MainThread");
|
||||||
if (auto& ipc = IPC::Instance()) {
|
if (auto& ipc = IPC::Instance()) {
|
||||||
ipc.WaitForStart();
|
ipc.WaitForStart();
|
||||||
@@ -140,9 +140,9 @@ void Linker::Execute(const std::vector<std::string> args) {
|
|||||||
params.argc = 1;
|
params.argc = 1;
|
||||||
params.argv[0] = "eboot.bin";
|
params.argv[0] = "eboot.bin";
|
||||||
if (!args.empty()) {
|
if (!args.empty()) {
|
||||||
params.argc = args.size() + 1;
|
params.argc = args.size();
|
||||||
for (int i = 0; i < args.size() && i < 32; i++) {
|
for (int i = 0; i < args.size() && i < 33; i++) {
|
||||||
params.argv[i + 1] = args[i].c_str();
|
params.argv[i] = args[i].c_str();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
params.entry_addr = module->GetEntryAddress();
|
params.entry_addr = module->GetEntryAddress();
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ public:
|
|||||||
void Relocate(Module* module);
|
void Relocate(Module* module);
|
||||||
bool Resolve(const std::string& name, Loader::SymbolType type, Module* module,
|
bool Resolve(const std::string& name, Loader::SymbolType type, Module* module,
|
||||||
Loader::SymbolRecord* return_info);
|
Loader::SymbolRecord* return_info);
|
||||||
void Execute(const std::vector<std::string> args = {});
|
void Execute(const std::vector<std::string>& args = {});
|
||||||
void DebugDump();
|
void DebugDump();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
159
src/emulator.cpp
159
src/emulator.cpp
@@ -3,15 +3,18 @@
|
|||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
#include <fmt/xchar.h>
|
||||||
#include <hwinfo/hwinfo.h>
|
#include <hwinfo/hwinfo.h>
|
||||||
|
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
#include "common/debug.h"
|
#include "common/debug.h"
|
||||||
#include "common/logging/backend.h"
|
#include "common/logging/backend.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "core/ipc/ipc.h"
|
||||||
#ifdef ENABLE_QT_GUI
|
#ifdef ENABLE_QT_GUI
|
||||||
#include <QtCore>
|
#include <QtCore>
|
||||||
#endif
|
#endif
|
||||||
@@ -19,9 +22,6 @@
|
|||||||
#ifdef ENABLE_DISCORD_RPC
|
#ifdef ENABLE_DISCORD_RPC
|
||||||
#include "common/discord_rpc_handler.h"
|
#include "common/discord_rpc_handler.h"
|
||||||
#endif
|
#endif
|
||||||
#ifdef _WIN32
|
|
||||||
#include <WinSock2.h>
|
|
||||||
#endif
|
|
||||||
#include "common/elf_info.h"
|
#include "common/elf_info.h"
|
||||||
#include "common/memory_patcher.h"
|
#include "common/memory_patcher.h"
|
||||||
#include "common/ntapi.h"
|
#include "common/ntapi.h"
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "common/polyfill_thread.h"
|
#include "common/polyfill_thread.h"
|
||||||
#include "common/scm_rev.h"
|
#include "common/scm_rev.h"
|
||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
|
#include "core/debugger.h"
|
||||||
#include "core/devtools/widget/module_list.h"
|
#include "core/devtools/widget/module_list.h"
|
||||||
#include "core/file_format/psf.h"
|
#include "core/file_format/psf.h"
|
||||||
#include "core/file_format/trp.h"
|
#include "core/file_format/trp.h"
|
||||||
@@ -45,6 +46,15 @@
|
|||||||
#include "emulator.h"
|
#include "emulator.h"
|
||||||
#include "video_core/renderdoc.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;
|
Frontend::WindowSDL* g_window = nullptr;
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
@@ -63,25 +73,35 @@ Emulator::Emulator() {
|
|||||||
|
|
||||||
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)) {
|
if (std::filesystem::is_directory(file)) {
|
||||||
file /= "eboot.bin";
|
file /= "eboot.bin";
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto eboot_name = file.filename().string();
|
std::filesystem::path game_folder;
|
||||||
|
if (p_game_folder.has_value()) {
|
||||||
auto game_folder = file.parent_path();
|
game_folder = p_game_folder.value();
|
||||||
if (const auto game_folder_name = game_folder.filename().string();
|
} else {
|
||||||
game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch")) {
|
game_folder = file.parent_path();
|
||||||
// If an executable was launched from a separate update directory,
|
if (const auto game_folder_name = game_folder.filename().string();
|
||||||
// use the base game directory as the game folder.
|
game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch")) {
|
||||||
const std::string base_name = game_folder_name.substr(0, game_folder_name.rfind('-'));
|
// If an executable was launched from a separate update directory,
|
||||||
const auto base_path = game_folder.parent_path() / base_name;
|
// use the base game directory as the game folder.
|
||||||
if (std::filesystem::is_directory(base_path)) {
|
const std::string base_name = game_folder_name.substr(0, game_folder_name.rfind('-'));
|
||||||
game_folder = base_path;
|
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.
|
// Applications expect to be run from /app0 so mount the file's parent path as app0.
|
||||||
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
auto* mnt = Common::Singleton<Core::FileSys::MntPoints>::Instance();
|
||||||
mnt->Mount(game_folder, "/app0", true);
|
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());
|
Libraries::InitHLELibs(&linker->GetHLESymbols());
|
||||||
|
|
||||||
// Load the module with the linker
|
// 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) {
|
if (linker->LoadModule(eboot_path) == -1) {
|
||||||
LOG_CRITICAL(Loader, "Failed to load game's eboot.bin: {}",
|
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);
|
std::quick_exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,6 +355,7 @@ void Emulator::Run(std::filesystem::path file, const std::vector<std::string> ar
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
args.insert(args.begin(), eboot_name.generic_string());
|
||||||
linker->Execute(args);
|
linker->Execute(args);
|
||||||
|
|
||||||
window->InitTimers();
|
window->InitTimers();
|
||||||
@@ -348,6 +370,109 @@ void Emulator::Run(std::filesystem::path file, const std::vector<std::string> ar
|
|||||||
std::quick_exit(0);
|
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) {
|
void Emulator::LoadSystemModules(const std::string& game_serial) {
|
||||||
constexpr auto ModulesToLoad = std::to_array<SysModules>(
|
constexpr auto ModulesToLoad = std::to_array<SysModules>(
|
||||||
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterLib},
|
{{"libSceNgs2.sprx", &Libraries::Ngs2::RegisterLib},
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <optional>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#include "common/singleton.h"
|
#include "common/singleton.h"
|
||||||
@@ -25,9 +26,19 @@ public:
|
|||||||
Emulator();
|
Emulator();
|
||||||
~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);
|
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:
|
private:
|
||||||
void LoadSystemModules(const std::string& game_serial);
|
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
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "functional"
|
#include "functional"
|
||||||
@@ -9,8 +9,10 @@
|
|||||||
|
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
|
#include "common/logging/backend.h"
|
||||||
#include "common/memory_patcher.h"
|
#include "common/memory_patcher.h"
|
||||||
#include "common/path_util.h"
|
#include "common/path_util.h"
|
||||||
|
#include "core/debugger.h"
|
||||||
#include "core/file_sys/fs.h"
|
#include "core/file_sys/fs.h"
|
||||||
#include "core/ipc/ipc.h"
|
#include "core/ipc/ipc.h"
|
||||||
#include "emulator.h"
|
#include "emulator.h"
|
||||||
@@ -32,6 +34,10 @@ int main(int argc, char* argv[]) {
|
|||||||
bool has_game_argument = false;
|
bool has_game_argument = false;
|
||||||
std::string game_path;
|
std::string game_path;
|
||||||
std::vector<std::string> game_args{};
|
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
|
// Map of argument strings to lambda functions
|
||||||
std::unordered_map<std::string, std::function<void(int&)>> arg_map = {
|
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"
|
"state. Does not overwrite the config file.\n"
|
||||||
" --add-game-folder <folder> Adds a new game folder to the config.\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"
|
" --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";
|
" -h, --help Display this help message\n";
|
||||||
exit(0);
|
exit(0);
|
||||||
}},
|
}},
|
||||||
@@ -119,7 +131,8 @@ int main(int argc, char* argv[]) {
|
|||||||
std::cout << "Game folder successfully saved.\n";
|
std::cout << "Game folder successfully saved.\n";
|
||||||
exit(0);
|
exit(0);
|
||||||
}},
|
}},
|
||||||
{"--set-addon-folder", [&](int& i) {
|
{"--set-addon-folder",
|
||||||
|
[&](int& i) {
|
||||||
if (++i >= argc) {
|
if (++i >= argc) {
|
||||||
std::cerr << "Error: Missing argument for --add-addon-folder\n";
|
std::cerr << "Error: Missing argument for --add-addon-folder\n";
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -136,6 +149,29 @@ int main(int argc, char* argv[]) {
|
|||||||
Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml");
|
Config::save(Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "config.toml");
|
||||||
std::cout << "Addon folder successfully saved.\n";
|
std::cout << "Addon folder successfully saved.\n";
|
||||||
exit(0);
|
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) {
|
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
|
// Run the emulator with the resolved eboot path
|
||||||
Core::Emulator emulator;
|
Core::Emulator* emulator = Common::Singleton<Core::Emulator>::Instance();
|
||||||
emulator.Run(eboot_path, game_args);
|
emulator->executableName = argv[0];
|
||||||
|
emulator->waitForDebuggerBeforeRun = waitForDebugger;
|
||||||
|
emulator->Run(eboot_path, game_args, game_folder);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
#include "unordered_map"
|
#include "unordered_map"
|
||||||
|
|
||||||
#include "common/config.h"
|
#include "common/config.h"
|
||||||
|
#include "common/logging/backend.h"
|
||||||
#include "common/memory_patcher.h"
|
#include "common/memory_patcher.h"
|
||||||
|
#include "core/debugger.h"
|
||||||
#include "core/file_sys/fs.h"
|
#include "core/file_sys/fs.h"
|
||||||
#include "emulator.h"
|
#include "emulator.h"
|
||||||
#include "game_install_dialog.h"
|
#include "game_install_dialog.h"
|
||||||
@@ -36,6 +38,10 @@ int main(int argc, char* argv[]) {
|
|||||||
bool show_gui = false, has_game_argument = false;
|
bool show_gui = false, has_game_argument = false;
|
||||||
std::string game_path;
|
std::string game_path;
|
||||||
std::vector<std::string> game_args{};
|
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
|
// Map of argument strings to lambda functions
|
||||||
std::unordered_map<std::string, std::function<void(int&)>> arg_map = {
|
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 "
|
" -f, --fullscreen <true|false> Specify window initial fullscreen "
|
||||||
"state. Does not overwrite the config file.\n"
|
"state. Does not overwrite the config file.\n"
|
||||||
" --add-game-folder <folder> Adds a new game folder to the config.\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";
|
" -h, --help Display this help message\n";
|
||||||
exit(0);
|
exit(0);
|
||||||
}},
|
}},
|
||||||
@@ -129,7 +141,29 @@ int main(int argc, char* argv[]) {
|
|||||||
std::cout << "Game folder successfully saved.\n";
|
std::cout << "Game folder successfully saved.\n";
|
||||||
exit(0);
|
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
|
// Parse command-line arguments using the map
|
||||||
for (int i = 1; i < argc; ++i) {
|
for (int i = 1; i < argc; ++i) {
|
||||||
@@ -181,6 +215,14 @@ int main(int argc, char* argv[]) {
|
|||||||
exit(1);
|
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
|
// Process game path or ID if provided
|
||||||
if (has_game_argument) {
|
if (has_game_argument) {
|
||||||
std::filesystem::path game_file_path(game_path);
|
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
|
// Run the emulator with the resolved game path
|
||||||
Core::Emulator emulator;
|
emulator->Run(game_file_path.string(), game_args, game_folder);
|
||||||
emulator.Run(game_file_path.string(), game_args);
|
|
||||||
if (!show_gui) {
|
if (!show_gui) {
|
||||||
return 0; // Exit after running the emulator without showing the 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;
|
isGameRunning = true;
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
// SDL on macOS requires main thread.
|
// SDL on macOS requires main thread.
|
||||||
Core::Emulator emulator;
|
Core::Emulator* emulator = Common::Singleton<Core::Emulator>::Instance();
|
||||||
emulator.Run(path);
|
emulator->Run(path);
|
||||||
#else
|
#else
|
||||||
std::thread emulator_thread([=] {
|
std::thread emulator_thread([=] {
|
||||||
Core::Emulator emulator;
|
Core::Emulator* emulator = Common::Singleton<Core::Emulator>::Instance();
|
||||||
emulator.Run(path);
|
emulator->Run(path);
|
||||||
});
|
});
|
||||||
emulator_thread.detach();
|
emulator_thread.detach();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user