diff --git a/rpcs3/Emu/Cell/lv2/sys_process.cpp b/rpcs3/Emu/Cell/lv2/sys_process.cpp index 42570d9dcc..746bcebe99 100644 --- a/rpcs3/Emu/Cell/lv2/sys_process.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_process.cpp @@ -411,7 +411,8 @@ void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr ar if (disc.empty() && !Emu.GetTitleID().empty()) disc = vfs::get(Emu.GetDir()); - Emu.CallAfter([path = std::move(path), argv = std::move(argv), envp = std::move(envp), data = std::move(data), disc = std::move(disc), hdd1 = std::move(hdd1), klic = g_fxo->get().devKlic.load()]() mutable + Emu.CallAfter([path = std::move(path), argv = std::move(argv), envp = std::move(envp), data = std::move(data), disc = std::move(disc) + , hdd1 = std::move(hdd1), klic = g_fxo->get().devKlic.load(), old_config = Emu.GetUsedConfig()]() mutable { sys_process.success("Process finished -> %s", argv[0]); Emu.SetForceBoot(true); @@ -429,7 +430,7 @@ void _sys_process_exit2(ppu_thread& ppu, s32 status, vm::ptr ar Emu.SetForceBoot(true); - auto res = Emu.BootGame(path, "", true); + auto res = Emu.BootGame(path, "", true, false, old_config); if (res != game_boot_result::no_errors) { diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 45aba5843f..5d8cb909cb 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -121,6 +121,7 @@ void Emulator::Init(bool add_only) // Reset defaults, cache them g_cfg.from_default(); + g_cfg.name = cfg_keys::_default; // Not all renderers are known at compile time, so set a provided default if possible if (m_default_renderer == video_renderer::vulkan && !m_default_graphics_adapter.empty()) @@ -131,31 +132,31 @@ void Emulator::Init(bool add_only) g_cfg_defaults = g_cfg.to_string(); - // Reload override configuration set via command line - if (!m_config_override_path.empty()) + // Load config file + if (m_config_path.find_first_of('/') != umax) { - if (const fs::file cfg_file{m_config_override_path, fs::read + fs::create}) + if (const fs::file cfg_file{m_config_path, fs::read + fs::create}) { if (!g_cfg.from_string(cfg_file.to_string())) { - sys_log.fatal("Failed to apply config override: %s. Proceeding with regular configuration.", m_config_override_path); - m_config_override_path.clear(); + sys_log.fatal("Failed to apply config: %s. Proceeding with regular configuration.", m_config_path); + m_config_path = cfg_keys::title_id; } else { - sys_log.success("Applied config override: %s", m_config_override_path); - g_cfg.name = m_config_override_path; + sys_log.success("Applied config override: %s", m_config_path); + g_cfg.name = m_config_path; } } else { - sys_log.fatal("Failed to access config override: %s (%s). Proceeding with regular configuration.", m_config_override_path, fs::g_tls_error); - m_config_override_path.clear(); + sys_log.fatal("Failed to access config: %s (%s). Proceeding with regular configuration.", m_config_path, fs::g_tls_error); + m_config_path = cfg_keys::title_id; } } // Reload global configuration - if (m_config_override_path.empty()) + if (m_config_path == cfg_keys::global || m_config_path == cfg_keys::title_id) { const auto cfg_path = fs::get_config_dir() + "/config.yml"; @@ -431,7 +432,7 @@ bool Emulator::BootRsxCapture(const std::string& path) return true; } -game_boot_result Emulator::BootGame(const std::string& path, const std::string& title_id, bool direct, bool add_only, bool force_global_config) +game_boot_result Emulator::BootGame(const std::string& path, const std::string& title_id, bool direct, bool add_only, const std::string& config_path) { if (!fs::exists(path)) { @@ -440,10 +441,12 @@ game_boot_result Emulator::BootGame(const std::string& path, const std::string& m_path_old = m_path; + m_config_path = config_path; + if (direct || fs::is_file(path)) { m_path = path; - return Load(title_id, add_only, force_global_config); + return Load(title_id, add_only); } game_boot_result result = game_boot_result::nothing_to_boot; @@ -463,7 +466,7 @@ game_boot_result Emulator::BootGame(const std::string& path, const std::string& if (fs::is_file(elf)) { m_path = elf; - result = Load(title_id, add_only, force_global_config); + result = Load(title_id, add_only); break; } } @@ -484,7 +487,7 @@ game_boot_result Emulator::BootGame(const std::string& path, const std::string& if (fs::is_file(elf)) { m_path = elf; - if (const auto err = Load(title_id, add_only, force_global_config); err != game_boot_result::no_errors) + if (const auto err = Load(title_id, add_only); err != game_boot_result::no_errors) { result = err; } @@ -500,9 +503,8 @@ void Emulator::SetForceBoot(bool force_boot) m_force_boot = force_boot; } -game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool force_global_config, bool is_disc_patch) +game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool is_disc_patch) { - m_force_global_config = force_global_config; const std::string resolved_path = GetCallbacks().resolve_path(m_path); if (!IsStopped()) @@ -599,7 +601,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool sys_log.notice("Category: %s", GetCat()); sys_log.notice("Version: APP_VER=%s VERSION=%s", version_app, version_disc); - if (!add_only && !force_global_config && m_config_override_path.empty()) + if (!add_only && m_config_path == cfg_keys::title_id) { const std::string config_path = rpcs3::utils::get_custom_config_path(m_title_id); @@ -611,6 +613,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool if (g_cfg.from_string(cfg_file.to_string())) { g_cfg.name = config_path; + m_config_path = config_path; } else { @@ -623,7 +626,12 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool { sys_log.notice("Applying custom config: %s.yml", m_path); - if (!g_cfg.from_string(cfg_file.to_string())) + if (g_cfg.from_string(cfg_file.to_string())) + { + g_cfg.name = m_path + ".yml"; + m_config_path = g_cfg.name; + } + else { sys_log.fatal("Failed to apply custom config: %s.yml", m_path); } @@ -854,7 +862,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool if (fs::rename(elf_dir + "/../../", hdd0_disc + elf_dir.substr(hdd0_game.size()) + "/../../", false)) { sys_log.success("Disc game %s moved to special location /dev_hdd0/disc/", m_title_id); - return m_path = hdd0_disc + m_path.substr(hdd0_game.size()), Load(m_title_id, add_only, force_global_config); + return m_path = hdd0_disc + m_path.substr(hdd0_game.size()), Load(m_title_id, add_only); } sys_log.error("Failed to move disc game %s to /dev_hdd0/disc/ (%s)", m_title_id, fs::g_tls_error); @@ -1106,7 +1114,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool { // Booting game update sys_log.success("Updates found at /dev_hdd0/game/%s/", m_title_id); - return m_path = hdd0_boot, Load(m_title_id, false, force_global_config, true); + return m_path = hdd0_boot, Load(m_title_id, false, true); } if (!disc_psf_obj.empty()) @@ -1560,7 +1568,7 @@ void Emulator::Stop(bool restart) if (restart) { // Reload with prior configs. - if (const auto error = Load(m_title_id, false, m_force_global_config); error != game_boot_result::no_errors) + if (const auto error = Load(m_title_id); error != game_boot_result::no_errors) sys_log.error("Restart failed: %s", error); return; } @@ -1670,7 +1678,7 @@ void Emulator::Stop(bool restart) if (restart) { // Reload with prior configs. - if (const auto error = Load(m_title_id, false, m_force_global_config); error != game_boot_result::no_errors) + if (const auto error = Load(m_title_id); error != game_boot_result::no_errors) sys_log.error("Restart failed: %s", error); return; } @@ -1682,6 +1690,7 @@ void Emulator::Stop(bool restart) disc.clear(); klic.clear(); hdd1.clear(); + m_config_path = cfg_keys::global; // Always Enable display sleep, not only if it was prevented. enable_display_sleep(); diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 24c7ac1ecb..edd4b6b950 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -44,6 +44,13 @@ enum class game_boot_result : u32 unsupported_disc_type }; +namespace cfg_keys +{ + inline const std::string title_id = "title_id"; // No config override mode + inline const std::string global = "global"; // Force global config + inline const std::string _default = "default"; // Force virtual default constructed config.yml +} + struct EmuCallbacks { std::function)> call_after; @@ -83,7 +90,7 @@ class Emulator final video_renderer m_default_renderer; std::string m_default_graphics_adapter; - std::string m_config_override_path; + std::string m_config_path; std::string m_path; std::string m_path_old; std::string m_title_id; @@ -96,8 +103,6 @@ class Emulator final std::string m_usr{"00000001"}; u32 m_usrid{1}; - bool m_force_global_config = false; - // This flag should be adjusted before each Stop() or each BootGame() and similar because: // 1. It forces an application to boot immediately by calling Run() in Load(). // 2. It signifies that we don't want to exit on Stop(), for example if we want to transition to another application. @@ -215,12 +220,17 @@ public: return m_pause_amend_time; } - game_boot_result BootGame(const std::string& path, const std::string& title_id = "", bool direct = false, bool add_only = false, bool force_global_config = false); + const std::string& GetUsedConfig() const + { + return m_config_path; + } + + game_boot_result BootGame(const std::string& path, const std::string& title_id = "", bool direct = false, bool add_only = false, const std::string& config_path = cfg_keys::title_id); bool BootRsxCapture(const std::string& path); void SetForceBoot(bool force_boot); - game_boot_result Load(const std::string& title_id = "", bool add_only = false, bool force_global_config = false, bool is_disc_patch = false); + game_boot_result Load(const std::string& title_id = "", bool add_only = false, bool is_disc_patch = false); void Run(bool start_playtime); bool Pause(bool freeze_emulation = false); void Resume(); @@ -240,7 +250,6 @@ public: void SetDefaultRenderer(video_renderer renderer) { m_default_renderer = renderer; } void SetDefaultGraphicsAdapter(std::string adapter) { m_default_graphics_adapter = std::move(adapter); } - void SetConfigOverride(std::string path) { m_config_override_path = std::move(path); } std::string GetFormattedTitle(double fps) const; diff --git a/rpcs3/main.cpp b/rpcs3/main.cpp index 515d58fe81..501cb242a3 100644 --- a/rpcs3/main.cpp +++ b/rpcs3/main.cpp @@ -522,7 +522,7 @@ int main(int argc, char** argv) parser.addOption(QCommandLineOption(arg_styles, "Lists the available styles.")); parser.addOption(QCommandLineOption(arg_style, "Loads a custom style.", "style", "")); parser.addOption(QCommandLineOption(arg_stylesheet, "Loads a custom stylesheet.", "path", "")); - const QCommandLineOption config_option(arg_config, "Forces the emulator to use this configuration file.", "path", ""); + const QCommandLineOption config_option(arg_config, "Forces the emulator to use this configuration file for CLI-booted game.", "path", ""); parser.addOption(config_option); const QCommandLineOption installfw_option(arg_installfw, "Forces the emulator to install this firmware file.", "path", ""); parser.addOption(installfw_option); @@ -837,18 +837,6 @@ int main(int argc, char** argv) } #endif - if (parser.isSet(arg_config)) - { - const std::string config_override_path = parser.value(config_option).toStdString(); - - if (!fs::is_file(config_override_path)) - { - report_fatal_error(fmt::format("No config file found: %s", config_override_path)); - } - - Emu.SetConfigOverride(config_override_path); - } - // Force install firmware or pkg first if specified through command-line if (parser.isSet(arg_installfw) || parser.isSet(arg_installpkg)) { @@ -909,13 +897,25 @@ int main(int argc, char** argv) } } + std::string config_path = cfg_keys::title_id; + + if (parser.isSet(arg_config)) + { + config_path = parser.value(config_option).toStdString(); + + if (!fs::is_file(config_path)) + { + report_fatal_error(fmt::format("No config file found: %s", config_path)); + } + } + // Postpone startup to main event loop - Emu.CallAfter([path = sstr(QFileInfo(args.at(0)).absoluteFilePath()), rpcs3_argv = std::move(rpcs3_argv)]() mutable + Emu.CallAfter([path = sstr(QFileInfo(args.at(0)).absoluteFilePath()), rpcs3_argv = std::move(rpcs3_argv), config_path = std::move(config_path)]() mutable { Emu.argv = std::move(rpcs3_argv); Emu.SetForceBoot(true); - if (const game_boot_result error = Emu.BootGame(path, ""); error != game_boot_result::no_errors) + if (const game_boot_result error = Emu.BootGame(path, "", false, false, config_path); error != game_boot_result::no_errors) { sys_log.error("Booting '%s' with cli argument failed: reason: %s", path, error); diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index d222d3f2a5..63dba234f5 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -941,6 +941,36 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) } menu.addAction(boot); + + { + QAction* boot_default = menu.addAction(is_current_running_game + ? tr("&Reboot with default configuration") + : tr("&Boot with default configuration")); + + connect(boot_default, &QAction::triggered, [this, gameinfo] + { + sys_log.notice("Booting from gamelist per context menu..."); + Q_EMIT RequestBoot(gameinfo, cfg_keys::_default); + }); + + QAction* boot_manual = menu.addAction(is_current_running_game + ? tr("&Reboot with manually selected configuration") + : tr("&Boot with manually selected configuration")); + + connect(boot_manual, &QAction::triggered, [this, gameinfo] + { + if (std::string file_path = sstr(QFileDialog::getOpenFileName(this, "Select Config File", "", tr("Config Files (*.yml);;All files (*.*)"))); !file_path.empty()) + { + sys_log.notice("Booting from gamelist per context menu..."); + Q_EMIT RequestBoot(gameinfo, file_path); + } + else + { + sys_log.notice("Manual config selection aborted."); + } + }); + } + menu.addSeparator(); QAction* configure = menu.addAction(gameinfo->hasCustomConfig @@ -1191,7 +1221,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) connect(boot, &QAction::triggered, this, [this, gameinfo]() { sys_log.notice("Booting from gamelist per context menu..."); - Q_EMIT RequestBoot(gameinfo, gameinfo->hasCustomConfig); + Q_EMIT RequestBoot(gameinfo, cfg_keys::global); }); connect(configure, &QAction::triggered, this, [this, current_game, gameinfo]() { diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h index 2b81f69036..4a03f7e58a 100644 --- a/rpcs3/rpcs3qt/game_list_frame.h +++ b/rpcs3/rpcs3qt/game_list_frame.h @@ -83,7 +83,7 @@ private Q_SLOTS: Q_SIGNALS: void GameListFrameClosed(); void NotifyGameSelection(const game_info& game); - void RequestBoot(const game_info& game, bool force_global_config = false); + void RequestBoot(const game_info& game, const std::string& config_path = "title_id"); // "title_id" is cfg_keys::title_id void RequestIconSizeChange(const int& val); void NotifyEmuSettingsChange(); protected: diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 61f8a64d88..44eed55d4e 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -312,7 +312,7 @@ void main_window::OnPlayOrPause() { if (m_selected_game) { - Boot(m_selected_game->info.path, m_selected_game->info.serial, false, false, false); + Boot(m_selected_game->info.path, m_selected_game->info.serial); } else if (const auto path = Emu.GetBoot(); !path.empty()) { @@ -377,7 +377,7 @@ void main_window::show_boot_error(game_boot_result status) msg.exec(); } -void main_window::Boot(const std::string& path, const std::string& title_id, bool direct, bool add_only, bool force_global_config) +void main_window::Boot(const std::string& path, const std::string& title_id, bool direct, bool add_only, const std::string& config_path) { if (!m_gui_settings->GetBootConfirmation(this, gui::ib_confirm_boot)) { @@ -389,7 +389,7 @@ void main_window::Boot(const std::string& path, const std::string& title_id, boo Emu.SetForceBoot(true); Emu.Stop(); - if (const auto error = Emu.BootGame(path, title_id, direct, add_only, force_global_config); error != game_boot_result::no_errors) + if (const auto error = Emu.BootGame(path, title_id, direct, add_only, config_path); error != game_boot_result::no_errors) { gui_log.error("Boot failed: reason: %s, path: %s", error, path); show_boot_error(error); @@ -2482,9 +2482,9 @@ void main_window::CreateDockWindows() m_selected_game = game; }); - connect(m_game_list_frame, &game_list_frame::RequestBoot, this, [this](const game_info& game, bool force_global_config) + connect(m_game_list_frame, &game_list_frame::RequestBoot, this, [this](const game_info& game, const std::string& config_path) { - Boot(game->info.path, game->info.serial, false, false, force_global_config); + Boot(game->info.path, game->info.serial, false, false, config_path); }); connect(m_game_list_frame, &game_list_frame::NotifyEmuSettingsChange, this, &main_window::NotifyEmuSettingsChange); diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index ff13e8ea26..f0d73b6735 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -106,7 +106,7 @@ public Q_SLOTS: private Q_SLOTS: void OnPlayOrPause(); - void Boot(const std::string& path, const std::string& title_id = "", bool direct = false, bool add_only = false, bool force_global_config = false); + void Boot(const std::string& path, const std::string& title_id = "", bool direct = false, bool add_only = false, const std::string& config_path = "title_id"); // "title_id" is cfg_keys::title_id void BootElf(); void BootGame(); void BootVSH();