diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index e1cdbe36c4..c0de5ebf33 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -586,7 +586,7 @@ void Emulator::LimitCacheSize() LOG_SUCCESS(GENERAL, "Cleaned disk cache, removed %.2f MB", size / 1024.0 / 1024.0); } -bool Emulator::BootGame(const std::string& path, bool direct, bool add_only) +bool Emulator::BootGame(const std::string& path, bool direct, bool add_only, bool force_global_config) { if (g_cfg.vfs.limit_cache_size) LimitCacheSize(); @@ -603,7 +603,7 @@ bool Emulator::BootGame(const std::string& path, bool direct, bool add_only) if (direct && fs::exists(path)) { m_path = path; - Load(add_only); + Load(add_only, force_global_config); return true; } @@ -614,7 +614,7 @@ bool Emulator::BootGame(const std::string& path, bool direct, bool add_only) if (fs::is_file(elf)) { m_path = elf; - Load(add_only); + Load(add_only, force_global_config); return true; } } @@ -697,12 +697,33 @@ std::string Emulator::GetSfoDirFromGamePath(const std::string& game_path, const return game_path; } +std::string Emulator::GetCustomConfigDir() +{ +#ifdef _WIN32 + return fs::get_config_dir() + "config/custom_configs/"; +#else + return fs::get_config_dir() + "custom_configs/"; +#endif +} + +std::string Emulator::GetCustomConfigPath(const std::string& title_id, bool get_deprecated_path) +{ + std::string path; + + if (get_deprecated_path) + path = fs::get_config_dir() + "data/" + title_id + "/config.yml"; + else + path = GetCustomConfigDir() + "config_" + title_id + ".yml"; + + return path; +} + void Emulator::SetForceBoot(bool force_boot) { m_force_boot = force_boot; } -void Emulator::Load(bool add_only) +void Emulator::Load(bool add_only, bool force_global_config) { if (!IsStopped()) { @@ -784,18 +805,31 @@ void Emulator::Load(bool add_only) LOG_NOTICE(LOADER, "Serial: %s", GetTitleID()); LOG_NOTICE(LOADER, "Category: %s", GetCat()); - // Load custom config-1 - if (fs::file cfg_file{fs::get_config_dir() + "data/" + m_title_id + "/config.yml"}) + if (!force_global_config) { - LOG_NOTICE(LOADER, "Applying custom config: data/%s/config.yml", m_title_id); - g_cfg.from_string(cfg_file.to_string()); - } + const std::string config_path_new = GetCustomConfigPath(m_title_id); + const std::string config_path_old = GetCustomConfigPath(m_title_id, true); - // Load custom config-2 - if (fs::file cfg_file{m_path + ".yml"}) - { - LOG_NOTICE(LOADER, "Applying custom config: %s.yml", m_path); - g_cfg.from_string(cfg_file.to_string()); + // Load custom config-1 + if (fs::file cfg_file{ config_path_old }) + { + LOG_NOTICE(LOADER, "Applying custom config: %s", config_path_old); + g_cfg.from_string(cfg_file.to_string()); + } + + // Load custom config-2 + if (fs::file cfg_file{ config_path_new }) + { + LOG_NOTICE(LOADER, "Applying custom config: %s", config_path_new); + g_cfg.from_string(cfg_file.to_string()); + } + + // Load custom config-3 + if (fs::file cfg_file{ m_path + ".yml" }) + { + LOG_NOTICE(LOADER, "Applying custom config: %s.yml", m_path); + g_cfg.from_string(cfg_file.to_string()); + } } #if defined(_WIN32) || defined(HAVE_VULKAN) diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index cf09ead762..202d0d0a65 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -309,7 +309,7 @@ public: std::string PPUCache() const; - bool BootGame(const std::string& path, bool direct = false, bool add_only = false); + bool BootGame(const std::string& path, bool direct = false, bool add_only = false, bool force_global_config = false); bool BootRsxCapture(const std::string& path); bool InstallPkg(const std::string& path); @@ -322,9 +322,12 @@ public: static std::string GetHddDir(); static std::string GetSfoDirFromGamePath(const std::string& game_path, const std::string& user); + static std::string GetCustomConfigDir(); + static std::string GetCustomConfigPath(const std::string& title_id, bool get_deprecated_path = false); + void SetForceBoot(bool force_boot); - void Load(bool add_only = false); + void Load(bool add_only = false, bool force_global_config = false); void Run(); bool Pause(); void Resume(); diff --git a/rpcs3/rpcs3qt/emu_settings.cpp b/rpcs3/rpcs3qt/emu_settings.cpp index cb622e9a75..fd5b68fed2 100644 --- a/rpcs3/rpcs3qt/emu_settings.cpp +++ b/rpcs3/rpcs3qt/emu_settings.cpp @@ -185,12 +185,12 @@ emu_settings::~emu_settings() { } -void emu_settings::LoadSettings(const std::string& path) +void emu_settings::LoadSettings(const std::string& title_id) { - m_path = path; + m_title_id = title_id; // Create config path if necessary - fs::create_path(fs::get_config_dir() + path); + fs::create_path(title_id.empty() ? fs::get_config_dir() : Emu.GetCustomConfigDir()); // Load default config m_defaultSettings = YAML::Load(g_cfg_defaults); @@ -202,11 +202,23 @@ void emu_settings::LoadSettings(const std::string& path) config.close(); // Add game config - if (!path.empty() && fs::is_file(fs::get_config_dir() + path + "/config.yml")) + if (!title_id.empty()) { - config = fs::file(fs::get_config_dir() + path + "/config.yml", fs::read + fs::write); - m_currentSettings += YAML::Load(config.to_string()); - config.close(); + const std::string config_path_new = Emu.GetCustomConfigPath(m_title_id); + const std::string config_path_old = Emu.GetCustomConfigPath(m_title_id, true); + + if (fs::is_file(config_path_new)) + { + config = fs::file(config_path_new, fs::read + fs::write); + m_currentSettings += YAML::Load(config.to_string()); + config.close(); + } + else if (fs::is_file(config_path_old)) + { + config = fs::file(config_path_old, fs::read + fs::write); + m_currentSettings += YAML::Load(config.to_string()); + config.close(); + } } } @@ -216,9 +228,9 @@ void emu_settings::SaveSettings() YAML::Emitter out; emitData(out, m_currentSettings); - if (!m_path.empty()) + if (!m_title_id.empty()) { - config = fs::file(fs::get_config_dir() + m_path + "/config.yml", fs::read + fs::write + fs::create); + config = fs::file(Emu.GetCustomConfigPath(m_title_id), fs::read + fs::write + fs::create); } else { diff --git a/rpcs3/rpcs3qt/emu_settings.h b/rpcs3/rpcs3qt/emu_settings.h index 821628c3ff..82303518bb 100644 --- a/rpcs3/rpcs3qt/emu_settings.h +++ b/rpcs3/rpcs3qt/emu_settings.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "Utilities/File.h" #include "Utilities/Log.h" @@ -223,7 +223,7 @@ public: Render_Creator m_render_creator; /** Loads the settings from path.*/ - void LoadSettings(const std::string& path = ""); + void LoadSettings(const std::string& title_id = ""); /** Fixes all registered invalid settings after asking the user for permission.*/ void OpenCorrectionDialog(QWidget* parent = Q_NULLPTR); @@ -355,5 +355,5 @@ private: YAML::Node m_defaultSettings; // The default settings as a YAML node. YAML::Node m_currentSettings; // The current settings as a YAML node. - std::string m_path; + std::string m_title_id; }; diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index ea3c581957..dbf6bed528 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -1,4 +1,4 @@ -#include "game_list_frame.h" +#include "game_list_frame.h" #include "qt_utils.h" #include "settings_dialog.h" #include "table_item_delegate.h" @@ -461,7 +461,7 @@ void game_list_frame::Refresh(const bool fromDrive, const bool scrollAfter) } const auto compat = m_game_compat->GetCompatibility(game.serial); - const bool hasCustomConfig = fs::is_file(fs::get_config_dir() + "data/" + game.serial + "/config.yml"); + const bool hasCustomConfig = fs::is_file(Emu.GetCustomConfigPath(game.serial)) || fs::is_file(Emu.GetCustomConfigPath(game.serial, true)); const QColor color = getGridCompatibilityColor(compat.color); const QPixmap pxmap = PaintedPixmap(img, hasCustomConfig, color); @@ -655,12 +655,29 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) const QString serial = qstr(currGame.serial); const QString name = qstr(currGame.name).simplified(); + const std::string cache_base_dir = fs::get_cache_dir() + "cache/" + currGame.serial; + const std::string data_base_dir = fs::get_config_dir() + "data/" + currGame.serial; + // Make Actions QMenu myMenu; - QAction* boot = myMenu.addAction(tr("&Boot")); + QAction* boot = new QAction(gameinfo->hasCustomConfig ? tr("&Boot with global configuration") : tr("&Boot")); QFont f = boot->font(); f.setBold(true); - boot->setFont(f); + if (gameinfo->hasCustomConfig) + { + QAction* boot_custom = myMenu.addAction(tr("&Boot with custom configuration")); + boot_custom->setFont(f); + connect(boot_custom, &QAction::triggered, [=] + { + LOG_NOTICE(LOADER, "Booting from gamelist per context menu..."); + Q_EMIT RequestBoot(currGame.path); + }); + } + else + { + boot->setFont(f); + } + myMenu.addAction(boot); QAction* configure = myMenu.addAction(tr("&Configure")); QAction* createPPUCache = myMenu.addAction(tr("&Create PPU Cache")); myMenu.addSeparator(); @@ -671,13 +688,57 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) myMenu.addSeparator(); QMenu* remove_menu = myMenu.addMenu(tr("&Remove")); QAction* removeGame = remove_menu->addAction(tr("&Remove %1").arg(qstr(currGame.category))); - QAction* removeConfig = remove_menu->addAction(tr("&Remove Custom Configuration")); - QAction* removeShadersCache = remove_menu->addAction(tr("&Remove Shaders Cache")); - QAction* removePPUCache = remove_menu->addAction(tr("&Remove PPU Cache")); - QAction* removeSPUCache = remove_menu->addAction(tr("&Remove SPU Cache")); + if (gameinfo->hasCustomConfig) + { + QAction* remove_custom_config = remove_menu->addAction(tr("&Remove Custom Configuration")); + connect(remove_custom_config, &QAction::triggered, [=]() + { + if (RemoveCustomConfiguration(currGame.serial, true)) + { + ShowCustomConfigIcon(item, false); + } + }); + } + if (fs::is_dir(cache_base_dir)) + { + QAction* removeShadersCache = remove_menu->addAction(tr("&Remove Shaders Cache")); + connect(removeShadersCache, &QAction::triggered, [=]() + { + RemoveShadersCache(cache_base_dir, true); + }); + QAction* removePPUCache = remove_menu->addAction(tr("&Remove PPU Cache")); + connect(removePPUCache, &QAction::triggered, [=]() + { + RemovePPUCache(cache_base_dir, true); + }); + QAction* removeSPUCache = remove_menu->addAction(tr("&Remove SPU Cache")); + connect(removeSPUCache, &QAction::triggered, [=]() + { + RemoveSPUCache(cache_base_dir, true); + }); + } myMenu.addSeparator(); QAction* openGameFolder = myMenu.addAction(tr("&Open Install Folder")); - QAction* openConfig = myMenu.addAction(tr("&Open Config Folder")); + if (gameinfo->hasCustomConfig) + { + QAction* open_config_dir = myMenu.addAction(tr("&Open Custom Config Folder")); + connect(open_config_dir, &QAction::triggered, [=]() + { + if (fs::is_file(Emu.GetCustomConfigPath(currGame.serial, true))) + open_dir(Emu.GetCustomConfigDir()); + + if (fs::is_file(Emu.GetCustomConfigPath(currGame.serial))) + open_dir(data_base_dir); + }); + } + if (fs::is_dir(data_base_dir)) + { + QAction* open_data_dir = myMenu.addAction(tr("&Open Data Folder")); + connect(open_data_dir, &QAction::triggered, [=]() + { + open_dir(data_base_dir); + }); + } myMenu.addSeparator(); QAction* checkCompat = myMenu.addAction(tr("&Check Game Compatibility")); QAction* downloadCompat = myMenu.addAction(tr("&Download Compatibility Database")); @@ -688,13 +749,10 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) QAction* copy_name = info_menu->addAction(tr("&Copy Name")); QAction* copy_serial = info_menu->addAction(tr("&Copy Serial")); - const std::string cache_base_dir = fs::get_cache_dir() + "cache/" + currGame.serial; - const std::string config_base_dir = fs::get_config_dir() + "data/" + currGame.serial; - connect(boot, &QAction::triggered, [=] { LOG_NOTICE(LOADER, "Booting from gamelist per context menu..."); - Q_EMIT RequestBoot(currGame.path); + Q_EMIT RequestBoot(currGame.path, gameinfo->hasCustomConfig); }); connect(configure, &QAction::triggered, [=] { @@ -739,7 +797,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) RemoveShadersCache(cache_base_dir); RemovePPUCache(cache_base_dir); RemoveSPUCache(cache_base_dir); - RemoveCustomConfiguration(config_base_dir); + RemoveCustomConfiguration(currGame.serial); } fs::remove_all(currGame.path); m_game_data.erase(std::remove(m_game_data.begin(), m_game_data.end(), gameinfo), m_game_data.end()); @@ -754,33 +812,10 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) LOG_SUCCESS(GENERAL, "Removed %s %s in %s", currGame.category, currGame.name, currGame.path); } }); - connect(removeConfig, &QAction::triggered, [=]() - { - if (RemoveCustomConfiguration(config_base_dir, true)) - { - ShowCustomConfigIcon(item, false); - } - }); - connect(removeShadersCache, &QAction::triggered, [=]() - { - RemoveShadersCache(cache_base_dir, true); - }); - connect(removePPUCache, &QAction::triggered, [=]() - { - RemovePPUCache(cache_base_dir, true); - }); - connect(removeSPUCache, &QAction::triggered, [=]() - { - RemoveSPUCache(cache_base_dir, true); - }); connect(openGameFolder, &QAction::triggered, [=]() { open_dir(currGame.path); }); - connect(openConfig, &QAction::triggered, [=]() - { - open_dir(fs::get_config_dir() + "data/" + currGame.serial); - }); connect(checkCompat, &QAction::triggered, [=] { QString link = "https://rpcs3.net/compatibility?g=" + serial; @@ -852,61 +887,57 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) QApplication::clipboard()->setText(serial); }); - //Disable options depending on software category + // Disable options depending on software category QString category = qstr(currGame.category); if (category == category::disc_game) { removeGame->setEnabled(false); } - else if (0) - { - boot->setEnabled(false), f.setBold(false), boot->setFont(f); - configure->setEnabled(false); - removeConfig->setEnabled(false); - openConfig->setEnabled(false); - checkCompat->setEnabled(false); - } else if (category != category::hdd_game) { checkCompat->setEnabled(false); } - // Disable removeconfig if no config exists. - removeConfig->setEnabled(gameinfo->hasCustomConfig); - - // remove options if necessary - if (!fs::is_dir(cache_base_dir)) - { - removeShadersCache->setEnabled(false); - removePPUCache->setEnabled(false); - removeSPUCache->setEnabled(false); - } - myMenu.exec(globalPos); } -bool game_list_frame::RemoveCustomConfiguration(const std::string& base_dir, bool is_interactive) +bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, bool is_interactive) { - const std::string config_path = base_dir + "/config.yml"; + const std::string config_path_new = Emu.GetCustomConfigPath(title_id); + const std::string config_path_old = Emu.GetCustomConfigPath(title_id, true); - if (!fs::is_file(config_path)) + if (!fs::is_file(config_path_new) && !fs::is_file(config_path_old)) return false; if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), tr("Remove custom game configuration?")) != QMessageBox::Yes) return false; - if (fs::remove_file(config_path)) + bool result = true; + + for (const std::string& path : { config_path_new, config_path_old }) { - LOG_SUCCESS(GENERAL, "Removed configuration file: %s", config_path); - return true; + if (!fs::is_file(path)) + { + continue; + } + if (fs::remove_file(path)) + { + LOG_SUCCESS(GENERAL, "Removed configuration file: %s", path); + } + else + { + LOG_FATAL(GENERAL, "Failed to remove configuration file: %s\nError: %s", path, fs::g_tls_error); + result = false; + } } - else + + if (is_interactive && !result) { QMessageBox::warning(this, tr("Warning!"), tr("Failed to remove configuration file!")); - LOG_FATAL(GENERAL, "Failed to remove configuration file: %s\nError: %s", config_path, fs::g_tls_error); - return false; } + + return result; } bool game_list_frame::RemoveShadersCache(const std::string& base_dir, bool is_interactive) diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h index 7377a99ddd..ee99d6825b 100644 --- a/rpcs3/rpcs3qt/game_list_frame.h +++ b/rpcs3/rpcs3qt/game_list_frame.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "stdafx.h" #include "Emu/GameInfo.h" @@ -225,7 +225,7 @@ private Q_SLOTS: void doubleClickedSlot(QTableWidgetItem *item); Q_SIGNALS: void GameListFrameClosed(); - void RequestBoot(const std::string& path); + void RequestBoot(const std::string& path, bool force_global_config = false); void RequestIconSizeChange(const int& val); protected: /** Override inherited method from Qt to allow signalling when close happened.*/ diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 9d5e2a62b8..d769be9ff0 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -268,7 +268,7 @@ void main_window::OnPlayOrPause() } } -void main_window::Boot(const std::string& path, bool direct, bool add_only) +void main_window::Boot(const std::string& path, bool direct, bool add_only, bool force_global_config) { if (!Emu.IsStopped()) { @@ -287,7 +287,7 @@ void main_window::Boot(const std::string& path, bool direct, bool add_only) Emu.SetForceBoot(true); Emu.Stop(); - if (Emu.BootGame(path, direct, add_only)) + if (Emu.BootGame(path, direct, add_only, force_global_config)) { LOG_SUCCESS(LOADER, "Boot successful."); const std::string serial = Emu.GetTitleID().empty() ? "" : "[" + Emu.GetTitleID() + "] "; @@ -1565,7 +1565,10 @@ void main_window::CreateDockWindows() } }); - connect(m_gameListFrame, &game_list_frame::RequestBoot, [this](const std::string& path){ Boot(path); }); + connect(m_gameListFrame, &game_list_frame::RequestBoot, [this](const std::string& path, bool force_global_config) + { + Boot(path, false, false, force_global_config); + }); } void main_window::ConfigureGuiFromSettings(bool configure_all) diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index 91eb25900a..2e39786cba 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -89,7 +89,7 @@ public Q_SLOTS: private Q_SLOTS: void OnPlayOrPause(); - void Boot(const std::string& path, bool direct = false, bool add_only = false); + void Boot(const std::string& path, bool direct = false, bool add_only = false, bool force_global_config = false); void BootElf(); void BootGame(); void BootRsxCapture(std::string path = ""); diff --git a/rpcs3/rpcs3qt/settings_dialog.cpp b/rpcs3/rpcs3qt/settings_dialog.cpp index 894ae17320..df23cb743d 100644 --- a/rpcs3/rpcs3qt/settings_dialog.cpp +++ b/rpcs3/rpcs3qt/settings_dialog.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -99,7 +99,7 @@ settings_dialog::settings_dialog(std::shared_ptr guiSettings, std: if (game) { - xemu_settings->LoadSettings("data/" + game->serial); + xemu_settings->LoadSettings(game->serial); setWindowTitle(tr("Settings: [") + qstr(game->serial) + "] " + qstr(game->name)); } else