From 9aa08313e30318f9c0ac7b529a7c4ddee20ef2f2 Mon Sep 17 00:00:00 2001 From: gidan80 Date: Tue, 20 Nov 2018 16:35:06 +0100 Subject: [PATCH] Qt: Add custom pad configs Add a custom pad config for every game. --- rpcs3/Emu/IdManager.h | 14 +- rpcs3/Emu/Io/PadHandler.cpp | 19 ++- rpcs3/Emu/Io/PadHandler.h | 29 +++- rpcs3/Emu/System.cpp | 59 +++++--- rpcs3/Emu/System.h | 6 +- rpcs3/Icons/combo_config_bordered.png | Bin 0 -> 54151 bytes rpcs3/Icons/controllers_2.png | Bin 0 -> 14384 bytes rpcs3/pad_thread.cpp | 9 +- rpcs3/pad_thread.h | 7 +- rpcs3/resources.qrc | 2 + rpcs3/rpcs3_app.cpp | 8 +- rpcs3/rpcs3qt/game_list_frame.cpp | 196 +++++++++++++++++++++++--- rpcs3/rpcs3qt/game_list_frame.h | 18 ++- rpcs3/rpcs3qt/main_window.cpp | 18 +-- rpcs3/rpcs3qt/main_window.ui | 8 +- rpcs3/rpcs3qt/pad_settings_dialog.cpp | 40 +++--- rpcs3/rpcs3qt/pad_settings_dialog.h | 4 +- 17 files changed, 332 insertions(+), 105 deletions(-) create mode 100644 rpcs3/Icons/combo_config_bordered.png create mode 100644 rpcs3/Icons/controllers_2.png diff --git a/rpcs3/Emu/IdManager.h b/rpcs3/Emu/IdManager.h index 7e536b9265..94d2d3ffd5 100644 --- a/rpcs3/Emu/IdManager.h +++ b/rpcs3/Emu/IdManager.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include "Utilities/types.h" #include "Utilities/mutex.h" @@ -633,8 +633,8 @@ public: } // Emplace the object returned by provider() and return it if no object exists - template - static auto import(F&& provider) -> decltype(static_cast>(provider())) + template + static auto import(F&& provider, Args&&... args) -> decltype(static_cast>(provider(std::forward(args)...))) { std::shared_ptr ptr; { @@ -644,7 +644,7 @@ public: if (!cur) { - ptr = provider(); + ptr = provider(std::forward(args)...); if (ptr) { @@ -662,8 +662,8 @@ public: } // Emplace the object return by provider() (old object will be removed if it exists) - template - static auto import_always(F&& provider) -> decltype(static_cast>(provider())) + template + static auto import_always(F&& provider, Args&&... args) -> decltype(static_cast>(provider(std::forward(args)...))) { std::shared_ptr ptr; std::shared_ptr old; @@ -672,7 +672,7 @@ public: auto& cur = g_vec[get_type()]; - ptr = provider(); + ptr = provider(std::forward(args)...); if (ptr) { diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index 151aa54884..b29d1195e5 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "PadHandler.h" +#include "pad_thread.h" cfg_input g_cfg_input; @@ -268,13 +269,25 @@ bool PadHandlerBase::has_led() return b_has_led; } -std::string PadHandlerBase::get_config_dir(pad_handler type) +std::string PadHandlerBase::get_config_dir(pad_handler type, const std::string& title_id) { + if (!title_id.empty()) + { + return Emu.GetCustomInputConfigDir(title_id) + fmt::format("%s", type) + "/"; + } return fs::get_config_dir() + "/InputConfigs/" + fmt::format("%s", type) + "/"; } -std::string PadHandlerBase::get_config_filename(int i) +std::string PadHandlerBase::get_config_filename(int i, const std::string& title_id) { + if (!title_id.empty() && fs::is_file(Emu.GetCustomInputConfigPath(title_id))) + { + const std::string path = Emu.GetCustomInputConfigDir(title_id) + g_cfg_input.player[i]->handler.to_string() + "/" + g_cfg_input.player[i]->profile.to_string() + ".yml"; + if (fs::is_file(path)) + { + return path; + } + } return fs::get_config_dir() + "/InputConfigs/" + g_cfg_input.player[i]->handler.to_string() + "/" + g_cfg_input.player[i]->profile.to_string() + ".yml"; } @@ -286,7 +299,7 @@ void PadHandlerBase::init_configs() { if (g_cfg_input.player[i]->handler == m_type) { - init_config(&m_pad_configs[index], get_config_filename(i)); + init_config(&m_pad_configs[index], get_config_filename(i, pad::g_title_id)); index++; } } diff --git a/rpcs3/Emu/Io/PadHandler.h b/rpcs3/Emu/Io/PadHandler.h index 414f2b142f..a1d57aa8ca 100644 --- a/rpcs3/Emu/Io/PadHandler.h +++ b/rpcs3/Emu/Io/PadHandler.h @@ -7,6 +7,7 @@ #include "Utilities/Config.h" #include "Utilities/types.h" #include "Emu/System.h" +#include "Emu/GameInfo.h" // TODO: HLE info (constants, structs, etc.) should not be available here @@ -268,7 +269,7 @@ struct cfg_player final : cfg::node struct cfg_input final : cfg::node { - const std::string cfg_name = fs::get_config_dir() + "/config_input.yml"; + std::string cfg_name = fs::get_config_dir() + "/config_input.yml"; cfg_player player1{ this, "Player 1 Input", pad_handler::keyboard }; cfg_player player2{ this, "Player 2 Input", pad_handler::null }; @@ -280,18 +281,32 @@ struct cfg_input final : cfg::node cfg_player *player[7]{ &player1, &player2, &player3, &player4, &player5, &player6, &player7 }; // Thanks gcc! - bool load() + bool load(const std::string& title_id = "") { - if (fs::file cfg_file{ cfg_name, fs::read }) + cfg_name = Emu.GetCustomInputConfigPath(title_id); + + if (!fs::is_file(cfg_name)) + { + cfg_name = fs::get_config_dir() + "/config_input.yml"; + } + + if (fs::file cfg_file{cfg_name, fs::read}) { return from_string(cfg_file.to_string()); } - return false; } - void save() + void save(const std::string& title_id = "") { + if (title_id.empty()) + { + cfg_name = fs::get_config_dir() + "/config_input.yml"; + } + else + { + cfg_name = Emu.GetCustomInputConfigPath(title_id); + } fs::file(cfg_name, fs::rewrite).write(to_string()); } }; @@ -460,8 +475,8 @@ public: bool has_deadzones(); bool has_led(); - static std::string get_config_dir(pad_handler type); - static std::string get_config_filename(int i); + static std::string get_config_dir(pad_handler type, const std::string& title_id = ""); + static std::string get_config_filename(int i, const std::string& title_id = ""); virtual bool Init() { return true; } PadHandlerBase(pad_handler type = pad_handler::null); diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index c09e35f49b..638e243540 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -39,7 +39,6 @@ #include "Utilities/GDBDebugServer.h" -#include "Utilities/sysinfo.h" #include "Utilities/JIT.h" #if defined(_WIN32) || defined(HAVE_VULKAN) @@ -303,31 +302,31 @@ void Emulator::Init() if (g_cfg.vfs.init_dirs) { - fs::create_path(dev_hdd0); - fs::create_path(dev_hdd1); - fs::create_path(dev_usb); - fs::create_dir(dev_hdd0 + "game/"); - fs::create_dir(dev_hdd0 + "game/TEST12345/"); - fs::create_dir(dev_hdd0 + "game/TEST12345/USRDIR/"); - fs::create_dir(dev_hdd0 + "game/.locks/"); - fs::create_dir(dev_hdd0 + "home/"); - fs::create_dir(dev_hdd0 + "home/" + m_usr + "/"); - fs::create_dir(dev_hdd0 + "home/" + m_usr + "/exdata/"); - fs::create_dir(dev_hdd0 + "home/" + m_usr + "/savedata/"); - fs::create_dir(dev_hdd0 + "home/" + m_usr + "/trophy/"); - fs::write_file(dev_hdd0 + "home/" + m_usr + "/localusername", fs::create + fs::excl + fs::write, "User"s); - fs::create_dir(dev_hdd0 + "disc/"); - fs::create_dir(dev_hdd0 + "savedata/"); - fs::create_dir(dev_hdd0 + "savedata/vmc/"); - fs::create_dir(dev_hdd1 + "cache/"); - fs::create_dir(dev_hdd1 + "game/"); + fs::create_path(dev_hdd0); + fs::create_path(dev_hdd1); + fs::create_path(dev_usb); + fs::create_dir(dev_hdd0 + "game/"); + fs::create_dir(dev_hdd0 + "game/TEST12345/"); + fs::create_dir(dev_hdd0 + "game/TEST12345/USRDIR/"); + fs::create_dir(dev_hdd0 + "game/.locks/"); + fs::create_dir(dev_hdd0 + "home/"); + fs::create_dir(dev_hdd0 + "home/" + m_usr + "/"); + fs::create_dir(dev_hdd0 + "home/" + m_usr + "/exdata/"); + fs::create_dir(dev_hdd0 + "home/" + m_usr + "/savedata/"); + fs::create_dir(dev_hdd0 + "home/" + m_usr + "/trophy/"); + fs::write_file(dev_hdd0 + "home/" + m_usr + "/localusername", fs::create + fs::excl + fs::write, "User"s); + fs::create_dir(dev_hdd0 + "disc/"); + fs::create_dir(dev_hdd0 + "savedata/"); + fs::create_dir(dev_hdd0 + "savedata/vmc/"); + fs::create_dir(dev_hdd1 + "cache/"); + fs::create_dir(dev_hdd1 + "game/"); } fs::create_path(fs::get_cache_dir() + "shaderlog/"); fs::create_path(fs::get_config_dir() + "captures/"); #ifdef WITH_GDB_DEBUGGER - LOG_SUCCESS(GENERAL, "GDB debug server will be started and listening on %d upon emulator boot", (int) g_cfg.misc.gdb_server_port); + LOG_SUCCESS(GENERAL, "GDB debug server will be started and listening on %d upon emulator boot", (int)g_cfg.misc.gdb_server_port); #endif // Initialize patch engine @@ -506,7 +505,7 @@ bool Emulator::BootRsxCapture(const std::string& path) GetCallbacks().on_ready(); auto gsrender = fxm::import(Emu.GetCallbacks().get_gs_render); - auto padhandler = fxm::import(Emu.GetCallbacks().get_pad_handler); + auto padhandler = fxm::import(Emu.GetCallbacks().get_pad_handler, ""); if (gsrender.get() == nullptr || padhandler.get() == nullptr) return false; @@ -727,6 +726,22 @@ std::string Emulator::GetCustomConfigPath(const std::string& title_id, bool get_ return path; } +std::string Emulator::GetCustomInputConfigDir(const std::string& title_id) +{ + // Notice: the extra folder for each title id may be removed at a later stage + // Warning: make sure to change any function that removes this directory as well +#ifdef _WIN32 + return fs::get_config_dir() + "config/custom_input_configs/" + title_id + "/"; +#else + return fs::get_config_dir() + "custom_input_configs/" + title_id + "/"; +#endif +} + +std::string Emulator::GetCustomInputConfigPath(const std::string& title_id) +{ + return GetCustomInputConfigDir(title_id) + "/config_input_" + title_id + ".yml"; +} + void Emulator::SetForceBoot(bool force_boot) { m_force_boot = force_boot; @@ -1343,7 +1358,7 @@ void Emulator::Load(bool add_only, bool force_global_config) } fxm::import(Emu.GetCallbacks().get_gs_render); // TODO: must be created in appropriate sys_rsx syscall - fxm::import(Emu.GetCallbacks().get_pad_handler); + fxm::import(Emu.GetCallbacks().get_pad_handler, m_title_id); network_thread_init(); } else if (ppu_prx.open(elf_file) == elf_error::ok) diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 99d1916014..40a5d2236d 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -189,12 +189,12 @@ struct EmuCallbacks std::function on_stop; std::function on_ready; std::function exit; - std::function reset_pads; + std::function reset_pads; std::function enable_pads; std::function handle_taskbar_progress; // (type, value) type: 0 for reset, 1 for increment, 2 for set_limit std::function()> get_kb_handler; std::function()> get_mouse_handler; - std::function()> get_pad_handler; + std::function(const std::string&)> get_pad_handler; std::function()> get_gs_frame; std::function()> get_gs_render; std::function()> get_audio; @@ -326,6 +326,8 @@ public: static std::string GetCustomConfigDir(); static std::string GetCustomConfigPath(const std::string& title_id, bool get_deprecated_path = false); + static std::string GetCustomInputConfigDir(const std::string& title_id); + static std::string GetCustomInputConfigPath(const std::string& title_id); void SetForceBoot(bool force_boot); diff --git a/rpcs3/Icons/combo_config_bordered.png b/rpcs3/Icons/combo_config_bordered.png new file mode 100644 index 0000000000000000000000000000000000000000..16a5e106ccd73cf40f4626db7b113dfdc91e0839 GIT binary patch literal 54151 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 literal 0 HcmV?d00001 diff --git a/rpcs3/Icons/controllers_2.png b/rpcs3/Icons/controllers_2.png new file mode 100644 index 0000000000000000000000000000000000000000..b26813ca35ae50bc7ebb513a4b40d7cb8b82f3ba GIT binary patch literal 14384 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 g_current = nullptr; std::recursive_mutex g_pad_mutex; + std::string g_title_id; } struct pad_setting @@ -23,8 +24,9 @@ struct pad_setting u32 device_type; }; -pad_thread::pad_thread(void *_curthread, void *_curwindow) : curthread(_curthread), curwindow(_curwindow) +pad_thread::pad_thread(void *_curthread, void *_curwindow, const std::string& title_id) : curthread(_curthread), curwindow(_curwindow) { + pad::g_title_id = title_id; Init(); thread = std::make_shared(&pad_thread::ThreadFunc, this); @@ -65,7 +67,7 @@ void pad_thread::Init() handlers.clear(); - g_cfg_input.load(); + g_cfg_input.load(pad::g_title_id); std::shared_ptr keyptr; @@ -142,8 +144,9 @@ void pad_thread::SetRumble(const u32 pad, u8 largeMotor, bool smallMotor) } } -void pad_thread::Reset() +void pad_thread::Reset(const std::string& title_id) { + pad::g_title_id = title_id; reset = active.load(); } diff --git a/rpcs3/pad_thread.h b/rpcs3/pad_thread.h index e918366bd4..499555eaa0 100644 --- a/rpcs3/pad_thread.h +++ b/rpcs3/pad_thread.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once #include #include @@ -16,14 +16,14 @@ struct PadInfo class pad_thread { public: - pad_thread(void *_curthread, void *_curwindow); // void * instead of QThread * and QWindow * because of include in emucore + pad_thread(void* _curthread, void* _curwindow, const std::string& title_id = ""); // void * instead of QThread * and QWindow * because of include in emucore ~pad_thread(); PadInfo& GetInfo() { return m_info; } auto& GetPads() { return m_pads; } void SetRumble(const u32 pad, u8 largeMotor, bool smallMotor); void Init(); - void Reset(); + void Reset(const std::string& title_id = ""); void SetEnabled(bool enabled); void SetIntercepted(bool intercepted); @@ -50,6 +50,7 @@ namespace pad { extern atomic_t g_current; extern std::recursive_mutex g_pad_mutex; + extern std::string g_title_id; static inline class pad_thread* get_current_handler() { diff --git a/rpcs3/resources.qrc b/rpcs3/resources.qrc index 3574e9ded1..14a3a0d619 100644 --- a/rpcs3/resources.qrc +++ b/rpcs3/resources.qrc @@ -18,5 +18,7 @@ Icons/open.png Icons/custom_config.png Icons/custom_config_2.png + Icons/controllers_2.png + Icons/combo_config_bordered.png diff --git a/rpcs3/rpcs3_app.cpp b/rpcs3/rpcs3_app.cpp index 2a7abe4e5e..1c3445a07b 100644 --- a/rpcs3/rpcs3_app.cpp +++ b/rpcs3/rpcs3_app.cpp @@ -142,9 +142,9 @@ void rpcs3_app::InitializeCallbacks() RequestCallAfter(std::move(func)); }; - callbacks.reset_pads = [this]() + callbacks.reset_pads = [this](const std::string& title_id = "") { - pad::get_current_handler()->Reset(); + pad::get_current_handler()->Reset(title_id); }; callbacks.enable_pads = [this](bool enable) { @@ -183,9 +183,9 @@ void rpcs3_app::InitializeCallbacks() } }; - callbacks.get_pad_handler = [this]() -> std::shared_ptr + callbacks.get_pad_handler = [this](const std::string& title_id) -> std::shared_ptr { - return std::make_shared(thread(), gameWindow); + return std::make_shared(thread(), gameWindow, title_id); }; callbacks.get_gs_frame = [this]() -> std::unique_ptr diff --git a/rpcs3/rpcs3qt/game_list_frame.cpp b/rpcs3/rpcs3qt/game_list_frame.cpp index 13aaa8a056..0fe493c2ac 100644 --- a/rpcs3/rpcs3qt/game_list_frame.cpp +++ b/rpcs3/rpcs3qt/game_list_frame.cpp @@ -1,6 +1,7 @@ #include "game_list_frame.h" #include "qt_utils.h" #include "settings_dialog.h" +#include "pad_settings_dialog.h" #include "table_item_delegate.h" #include "custom_table_widget_item.h" #include "input_dialog.h" @@ -114,6 +115,10 @@ game_list_frame::game_list_frame(std::shared_ptr guiSettings, std: QMenu* configure = new QMenu(this); configure->addActions(m_columnActs); configure->exec(mapToGlobal(pos)); + + QMenu* pad_configure = new QMenu(this); + pad_configure->addActions(m_columnActs); + pad_configure->exec(mapToGlobal(pos)); }); connect(m_xgrid, &QTableWidget::itemDoubleClicked, this, &game_list_frame::doubleClickedSlot); @@ -471,12 +476,14 @@ 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(Emu.GetCustomConfigPath(game.serial)) || fs::is_file(Emu.GetCustomConfigPath(game.serial, true)); + const bool hasCustomPadConfig = fs::is_file(Emu.GetCustomInputConfigPath(game.serial)); const QColor color = getGridCompatibilityColor(compat.color); - const QPixmap pxmap = PaintedPixmap(img, hasCustomConfig, color); + const QPixmap pxmap = PaintedPixmap(img, hasCustomConfig, hasCustomPadConfig, color); - m_game_data.push_back(game_info(new gui_game_info{ game, compat, img, pxmap, hasCustomConfig })); + m_game_data.push_back(game_info(new gui_game_info{game, compat, img, pxmap, hasCustomConfig, hasCustomPadConfig})); } catch (const std::exception& e) { @@ -695,6 +702,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) } myMenu.addAction(boot); QAction* configure = myMenu.addAction(tr("&Configure")); + QAction* pad_configure = myMenu.addAction(tr("&Configure pads")); QAction* createPPUCache = myMenu.addAction(tr("&Create PPU Cache")); myMenu.addSeparator(); QAction* renameTitle = myMenu.addAction(tr("&Rename In Game List")); @@ -709,9 +717,20 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) QAction* remove_custom_config = remove_menu->addAction(tr("&Remove Custom Configuration")); connect(remove_custom_config, &QAction::triggered, [=]() { - if (RemoveCustomConfiguration(currGame.serial, true)) + if (RemoveCustomConfiguration(currGame.serial, gameinfo, true)) { - ShowCustomConfigIcon(item, false); + ShowCustomConfigIcon(item, config::type::emu); + } + }); + } + if (gameinfo->hasCustomPadConfig) + { + QAction* remove_custom_pad_config = remove_menu->addAction(tr("&Remove Custom Pad Configuration")); + connect(remove_custom_pad_config, &QAction::triggered, [=]() + { + if (RemoveCustomPadConfiguration(currGame.serial, gameinfo, true)) + { + ShowCustomConfigIcon(item, config::type::pad); } }); } @@ -775,7 +794,33 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) settings_dialog dlg(m_gui_settings, m_emu_settings, 0, this, &currGame); if (dlg.exec() == QDialog::Accepted && !gameinfo->hasCustomConfig) { - ShowCustomConfigIcon(item, true); + gameinfo->hasCustomConfig = true; + ShowCustomConfigIcon(item, config::type::emu); + } + }); + connect(pad_configure, &QAction::triggered, [=] + { + if (!Emu.IsStopped()) + { + Emu.GetCallbacks().enable_pads(false); + } + pad_settings_dialog dlg(this, &currGame); + connect(&dlg, &QDialog::finished, [this](int/* result*/) + { + if (Emu.IsStopped()) + { + return; + } + Emu.GetCallbacks().reset_pads(Emu.GetTitleID()); + }); + if (dlg.exec() == QDialog::Accepted && !gameinfo->hasCustomPadConfig) + { + gameinfo->hasCustomPadConfig = true; + ShowCustomConfigIcon(item, config::type::pad); + } + if (!Emu.IsStopped()) + { + Emu.GetCallbacks().enable_pads(true); } }); connect(hide_serial, &QAction::triggered, [=](bool checked) @@ -801,7 +846,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) } QMessageBox* mb = new QMessageBox(QMessageBox::Question, tr("Confirm %1 Removal").arg(qstr(currGame.category)), tr("Permanently remove %0 from drive?\nPath: %1").arg(name).arg(qstr(currGame.path)), QMessageBox::Yes | QMessageBox::No, this); - mb->setCheckBox(new QCheckBox(tr("Remove caches and custom config"))); + mb->setCheckBox(new QCheckBox(tr("Remove caches and custom configs"))); mb->deleteLater(); if (mb->exec() == QMessageBox::Yes) { @@ -811,6 +856,7 @@ void game_list_frame::ShowContextMenu(const QPoint &pos) RemovePPUCache(cache_base_dir); RemoveSPUCache(cache_base_dir); RemoveCustomConfiguration(currGame.serial); + RemoveCustomPadConfiguration(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()); @@ -933,7 +979,7 @@ bool game_list_frame::CreatePPUCache(const std::string& path) return success; } -bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, bool is_interactive) +bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, game_info game, bool is_interactive) { const std::string config_path_new = Emu.GetCustomConfigPath(title_id); const std::string config_path_old = Emu.GetCustomConfigPath(title_id, true); @@ -954,6 +1000,10 @@ bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, boo } if (fs::remove_file(path)) { + if (game) + { + game->hasCustomConfig = false; + } LOG_SUCCESS(GENERAL, "Removed configuration file: %s", path); } else @@ -971,6 +1021,41 @@ bool game_list_frame::RemoveCustomConfiguration(const std::string& title_id, boo return result; } +bool game_list_frame::RemoveCustomPadConfiguration(const std::string& title_id, game_info game, bool is_interactive) +{ + const std::string config_dir = Emu.GetCustomInputConfigDir(title_id); + + if (!fs::is_dir(config_dir)) + return true; + + if (is_interactive && QMessageBox::question(this, tr("Confirm Removal"), (!Emu.IsStopped() && Emu.GetTitleID() == title_id) + ? tr("Remove custom pad configuration?\nYour configuration will revert to the global pad settings.") + : tr("Remove custom pad configuration?")) != QMessageBox::Yes) + return true; + + if (QDir(qstr(config_dir)).removeRecursively()) + { + if (game) + { + game->hasCustomPadConfig = false; + } + if (!Emu.IsStopped() && Emu.GetTitleID() == title_id) + { + Emu.GetCallbacks().enable_pads(false); + Emu.GetCallbacks().reset_pads(title_id); + Emu.GetCallbacks().enable_pads(true); + } + LOG_NOTICE(GENERAL, "Removed pad configuration directory: %s", config_dir); + return true; + } + else if (is_interactive) + { + QMessageBox::warning(this, tr("Warning!"), tr("Failed to completely remove pad configuration directory!")); + LOG_FATAL(GENERAL, "Failed to completely remove pad configuration directory: %s\nError: %s", config_dir, fs::g_tls_error); + } + return false; +} + bool game_list_frame::RemoveShadersCache(const std::string& base_dir, bool is_interactive) { if (!fs::is_dir(base_dir)) @@ -1260,6 +1345,52 @@ void game_list_frame::BatchRemoveCustomConfigurations() Refresh(true); } +void game_list_frame::BatchRemoveCustomPadConfigurations() +{ + std::set serials; + for (const auto& game : m_game_data) + { + if (game->hasCustomPadConfig && !serials.count(game->info.serial)) + { + serials.emplace(game->info.serial); + } + } + const u32 total = serials.size(); + + if (total == 0) + { + QMessageBox::information(this, tr("Custom Pad Configuration Batch Removal"), tr("No files found"), QMessageBox::Ok); + return; + } + + progress_dialog* pdlg = new progress_dialog(tr("Removing all custom pad configurations"), tr("Cancel"), 0, total, this); + pdlg->setWindowTitle(tr("Custom Pad Configuration Batch Removal")); + pdlg->setAutoClose(false); + pdlg->setAutoReset(false); + pdlg->show(); + + u32 removed = 0; + for (const auto& serial : serials) + { + if (pdlg->wasCanceled()) + { + LOG_NOTICE(GENERAL, "Custom Pad Configuration Batch Removal was canceled. %d/%d custom pad configurations cleared", removed, total); + break; + } + QApplication::processEvents(); + + if (RemoveCustomPadConfiguration(serial)) + { + pdlg->SetValue(++removed); + } + } + + pdlg->setLabelText(tr("%0/%1 custom pad configurations cleared").arg(removed).arg(total)); + pdlg->setCancelButtonText(tr("OK")); + QApplication::beep(); + Refresh(true); +} + void game_list_frame::BatchRemoveShaderCaches() { std::set serials; @@ -1302,7 +1433,7 @@ void game_list_frame::BatchRemoveShaderCaches() QApplication::beep(); } -QPixmap game_list_frame::PaintedPixmap(const QImage& img, bool paint_config_icon, const QColor& compatibility_color) +QPixmap game_list_frame::PaintedPixmap(const QImage& img, bool paint_config_icon, bool paint_pad_config_icon, const QColor& compatibility_color) { const int device_pixel_ratio = devicePixelRatio(); const QSize original_size = img.size(); @@ -1318,11 +1449,26 @@ QPixmap game_list_frame::PaintedPixmap(const QImage& img, bool paint_config_icon painter.drawImage(QPoint(0, 0), img); } - if (paint_config_icon && !m_isListLayout) + if (!m_isListLayout && (paint_config_icon || paint_pad_config_icon)) { const int width = original_size.width() * 0.2; const QPoint origin = QPoint(original_size.width() - width, 0); - QImage custom_config_icon(":/Icons/custom_config_2.png"); + QString icon_path; + + if (paint_config_icon && paint_pad_config_icon) + { + icon_path = ":/Icons/combo_config_bordered.png"; + } + else if (paint_config_icon) + { + icon_path = ":/Icons/custom_config_2.png"; + } + else if (paint_pad_config_icon) + { + icon_path = ":/Icons/controllers_2.png"; + } + + QImage custom_config_icon(icon_path); custom_config_icon.setDevicePixelRatio(device_pixel_ratio); painter.drawImage(origin, custom_config_icon.scaled(QSize(width, width) * device_pixel_ratio, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation)); } @@ -1342,7 +1488,7 @@ QPixmap game_list_frame::PaintedPixmap(const QImage& img, bool paint_config_icon return QPixmap::fromImage(image.scaled(m_Icon_Size * device_pixel_ratio, Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation)); } -void game_list_frame::ShowCustomConfigIcon(QTableWidgetItem* item, bool enabled) +void game_list_frame::ShowCustomConfigIcon(QTableWidgetItem* item, config::type type) { auto game = GetGameInfoFromItem(item); if (game == nullptr) @@ -1350,24 +1496,22 @@ void game_list_frame::ShowCustomConfigIcon(QTableWidgetItem* item, bool enabled) return; } - game->hasCustomConfig = enabled; - const QColor color = getGridCompatibilityColor(game->compat.color); - game->pxmap = PaintedPixmap(game->icon, enabled, color); - if (!m_isListLayout) { + const QColor color = getGridCompatibilityColor(game->compat.color); + game->pxmap = PaintedPixmap(game->icon, game->hasCustomConfig, game->hasCustomPadConfig, color); int r = m_xgrid->currentItem()->row(), c = m_xgrid->currentItem()->column(); m_xgrid->addItem(game->pxmap, qstr(game->info.name).simplified(), r, c); m_xgrid->item(r, c)->setData(gui::game_role, QVariant::fromValue(game)); } - else if (enabled) - { + else if (game->hasCustomConfig && game->hasCustomPadConfig) + m_gameList->item(item->row(), gui::column_name)->setIcon(QIcon(":/Icons/combo_config_bordered.png")); + else if (game->hasCustomConfig) m_gameList->item(item->row(), gui::column_name)->setIcon(QIcon(":/Icons/custom_config.png")); - } + else if (game->hasCustomPadConfig) + m_gameList->item(item->row(), gui::column_name)->setIcon(QIcon(":/Icons/controllers.png")); else - { m_gameList->setItem(item->row(), gui::column_name, new custom_table_widget_item(game->info.name)); - } } void game_list_frame::ResizeIcons(const int& sliderPos) @@ -1395,7 +1539,7 @@ void game_list_frame::RepaintIcons(const bool& fromSettings) for (auto& game : m_game_data) { QColor color = getGridCompatibilityColor(game->compat.color); - game->pxmap = PaintedPixmap(game->icon, game->hasCustomConfig, color); + game->pxmap = PaintedPixmap(game->icon, game->hasCustomConfig, game->hasCustomPadConfig, color); } Refresh(); @@ -1560,10 +1704,18 @@ int game_list_frame::PopulateGameList() // Title custom_table_widget_item* title_item = new custom_table_widget_item(title); - if (game->hasCustomConfig) + if (game->hasCustomConfig && game->hasCustomPadConfig) + { + title_item->setIcon(QIcon(":/Icons/combo_config_bordered.png")); + } + else if (game->hasCustomConfig) { title_item->setIcon(QIcon(":/Icons/custom_config.png")); } + else if (game->hasCustomPadConfig) + { + title_item->setIcon(QIcon(":/Icons/controllers.png")); + } // Serial custom_table_widget_item* serial_item = new custom_table_widget_item(game->info.serial); diff --git a/rpcs3/rpcs3qt/game_list_frame.h b/rpcs3/rpcs3qt/game_list_frame.h index 6d415eb5dc..f130f1130f 100644 --- a/rpcs3/rpcs3qt/game_list_frame.h +++ b/rpcs3/rpcs3qt/game_list_frame.h @@ -31,6 +31,15 @@ enum Category Others, }; +namespace config +{ + enum class type + { + emu, + pad + }; +} + namespace category // (see PARAM.SFO in psdevwiki.com) TODO: Disc Categories { // PS3 bootable @@ -171,6 +180,7 @@ struct gui_game_info QImage icon; QPixmap pxmap; bool hasCustomConfig; + bool hasCustomPadConfig; }; typedef std::shared_ptr game_info; @@ -215,6 +225,7 @@ public Q_SLOTS: void BatchRemovePPUCaches(); void BatchRemoveSPUCaches(); void BatchRemoveCustomConfigurations(); + void BatchRemoveCustomPadConfigurations(); void BatchRemoveShaderCaches(); void SetListMode(const bool& isList); void SetSearchText(const QString& text); @@ -234,9 +245,9 @@ protected: void resizeEvent(QResizeEvent *event) override; bool eventFilter(QObject *object, QEvent *event) override; private: - QPixmap PaintedPixmap(const QImage& img, bool paint_config_icon = false, const QColor& color = QColor()); + QPixmap PaintedPixmap(const QImage& img, bool paint_config_icon = false, bool paint_pad_config_icon = false, const QColor& color = QColor()); QColor getGridCompatibilityColor(const QString& string); - void ShowCustomConfigIcon(QTableWidgetItem* item, bool enabled); + void ShowCustomConfigIcon(QTableWidgetItem* item, config::type type); void PopulateGameGrid(int maxCols, const QSize& image_size, const QColor& image_color); bool IsEntryVisible(const game_info& game); void SortGameList(); @@ -244,7 +255,8 @@ private: int PopulateGameList(); bool SearchMatchesApp(const std::string& name, const std::string& serial); - bool RemoveCustomConfiguration(const std::string& base_dir, bool is_interactive = false); + bool RemoveCustomConfiguration(const std::string& title_id, game_info game = nullptr, bool is_interactive = false); + bool RemoveCustomPadConfiguration(const std::string& title_id, game_info game = nullptr, bool is_interactive = false); bool RemoveShadersCache(const std::string& base_dir, bool is_interactive = false); bool RemovePPUCache(const std::string& base_dir, bool is_interactive = false); bool RemoveSPUCache(const std::string& base_dir, bool is_interactive = false); diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 6a1f628fc8..bbb57e8766 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -1247,6 +1247,7 @@ void main_window::CreateConnects() connect(ui->batchRemoveSPUCachesAct, &QAction::triggered, m_gameListFrame, &game_list_frame::BatchRemoveSPUCaches); connect(ui->batchRemoveShaderCachesAct, &QAction::triggered, m_gameListFrame, &game_list_frame::BatchRemoveShaderCaches); connect(ui->batchRemoveCustomConfigurationsAct, &QAction::triggered, m_gameListFrame, &game_list_frame::BatchRemoveCustomConfigurations); + connect(ui->batchRemoveCustomPadConfigurationsAct, &QAction::triggered, m_gameListFrame, &game_list_frame::BatchRemoveCustomPadConfigurations); connect(ui->sysPauseAct, &QAction::triggered, this, &main_window::OnPlayOrPause); connect(ui->sysStopAct, &QAction::triggered, [=]() { Emu.Stop(); }); @@ -1282,20 +1283,19 @@ void main_window::CreateConnects() auto openPadSettings = [this] { - auto resetPadHandlers = [this](int/* result*/) - { - if (Emu.IsStopped()) - { - return; - } - Emu.GetCallbacks().reset_pads(); - }; if (!Emu.IsStopped()) { Emu.GetCallbacks().enable_pads(false); } pad_settings_dialog dlg(this); - connect(&dlg, &QDialog::finished, resetPadHandlers); + connect(&dlg, &QDialog::finished, [this](int/* result*/) + { + if (Emu.IsStopped()) + { + return; + } + Emu.GetCallbacks().reset_pads(Emu.GetTitleID()); + }); dlg.exec(); if (!Emu.IsStopped()) { diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index c3e6967705..4075cdba42 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -141,7 +141,7 @@ 0 0 1058 - 21 + 22 @@ -172,6 +172,7 @@ + @@ -995,6 +996,11 @@ Remove Shader Caches + + + Remove Custom Pad Configurations + + diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.cpp b/rpcs3/rpcs3qt/pad_settings_dialog.cpp index edd7599914..df19f5092d 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.cpp +++ b/rpcs3/rpcs3qt/pad_settings_dialog.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -29,19 +29,13 @@ constexpr auto qstr = QString::fromStdString; inline bool CreateConfigFile(const QString& dir, const QString& name) { - QString input_dir = qstr(fs::get_config_dir()) + "/InputConfigs/"; - if (!QDir().mkdir(input_dir) && !QDir().exists(input_dir)) - { - LOG_ERROR(GENERAL, "Failed to create dir %s", sstr(input_dir)); - return false; - } - if (!QDir().mkdir(dir) && !QDir().exists(dir)) + if (!QDir().mkpath(dir)) { LOG_ERROR(GENERAL, "Failed to create dir %s", sstr(dir)); return false; } - QString filename = dir + name + ".yml"; + const QString filename = dir + name + ".yml"; QFile new_file(filename); if (!new_file.open(QIODevice::WriteOnly)) @@ -54,16 +48,25 @@ inline bool CreateConfigFile(const QString& dir, const QString& name) return true; }; -pad_settings_dialog::pad_settings_dialog(QWidget *parent) +pad_settings_dialog::pad_settings_dialog(QWidget *parent, const GameInfo *game) : QDialog(parent), ui(new Ui::pad_settings_dialog) { ui->setupUi(this); - setWindowTitle(tr("Gamepads Settings")); - // load input config g_cfg_input.from_default(); - g_cfg_input.load(); + + if (game) + { + m_title_id = game->serial; + g_cfg_input.load(game->serial); + setWindowTitle(tr("Gamepads Settings: [%0] %1").arg(qstr(game->serial)).arg(qstr(game->name).simplified())); + } + else + { + g_cfg_input.load(); + setWindowTitle(tr("Gamepads Settings")); + } // Create tab widget for 7 players m_tabs = new QTabWidget; @@ -153,7 +156,7 @@ pad_settings_dialog::pad_settings_dialog(QWidget *parent) QMessageBox::warning(this, tr("Error"), tr("Please choose a non-existing name")); continue; } - if (CreateConfigFile(qstr(PadHandlerBase::get_config_dir(g_cfg_input.player[i]->handler)), friendlyName)) + if (CreateConfigFile(qstr(PadHandlerBase::get_config_dir(g_cfg_input.player[i]->handler, m_title_id)), friendlyName)) { ui->chooseProfile->addItem(friendlyName); ui->chooseProfile->setCurrentText(friendlyName); @@ -839,6 +842,7 @@ void pad_settings_dialog::OnPadButtonClicked(int id) void pad_settings_dialog::OnTabChanged(int index) { + // TODO: Do not save yet! But keep all profile changes until the dialog was saved. // Save old profile SaveProfile(); @@ -898,7 +902,7 @@ void pad_settings_dialog::ChangeInputType() if (!g_cfg_input.player[player]->handler.from_string(handler)) { // Something went wrong - LOG_ERROR(GENERAL, "Failed to convert input string:%s", handler); + LOG_ERROR(GENERAL, "Failed to convert input string: %s", handler); return; } @@ -963,7 +967,7 @@ void pad_settings_dialog::ChangeInputType() } } - QString profile_dir = qstr(PadHandlerBase::get_config_dir(m_handler->m_type)); + QString profile_dir = qstr(PadHandlerBase::get_config_dir(m_handler->m_type, m_title_id)); QStringList profiles = gui::utils::get_dir_entries(QDir(profile_dir), QStringList() << "*.yml"); if (profiles.isEmpty()) @@ -1021,7 +1025,7 @@ void pad_settings_dialog::ChangeProfile() } // Change handler - const std::string cfg_name = PadHandlerBase::get_config_dir(m_handler->m_type) + m_profile + ".yml"; + const std::string cfg_name = PadHandlerBase::get_config_dir(m_handler->m_type, m_title_id) + m_profile + ".yml"; // Adjust to the different pad handlers switch (m_handler->m_type) @@ -1145,7 +1149,7 @@ void pad_settings_dialog::SaveExit() g_cfg_input.player[i]->profile.from_default(); } - g_cfg_input.save(); + g_cfg_input.save(m_title_id); QDialog::accept(); } diff --git a/rpcs3/rpcs3qt/pad_settings_dialog.h b/rpcs3/rpcs3qt/pad_settings_dialog.h index e14b00ad37..0541b7589d 100644 --- a/rpcs3/rpcs3qt/pad_settings_dialog.h +++ b/rpcs3/rpcs3qt/pad_settings_dialog.h @@ -8,6 +8,7 @@ #include #include "Emu/Io/PadHandler.h" +#include "Emu/GameInfo.h" namespace Ui { @@ -85,7 +86,7 @@ class pad_settings_dialog : public QDialog const QString Disconnected_suffix = tr(" (disconnected)"); public: - explicit pad_settings_dialog(QWidget *parent = nullptr); + explicit pad_settings_dialog(QWidget *parent = nullptr, const GameInfo *game = nullptr); ~pad_settings_dialog(); private Q_SLOTS: @@ -100,6 +101,7 @@ private Q_SLOTS: private: Ui::pad_settings_dialog *ui; + std::string m_title_id; // TabWidget QTabWidget* m_tabs;