// SPDX-FileCopyrightText: 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "common/logging/log.h" #include "input_common/main.h" #include "qt_config.h" #include "uisettings.h" const std::array QtConfig::default_buttons = { Qt::Key_C, Qt::Key_X, Qt::Key_V, Qt::Key_Z, Qt::Key_F, Qt::Key_G, Qt::Key_Q, Qt::Key_E, Qt::Key_R, Qt::Key_T, Qt::Key_M, Qt::Key_N, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, Qt::Key_Down, Qt::Key_Q, Qt::Key_E, 0, 0, Qt::Key_Q, Qt::Key_E, }; const std::array QtConfig::default_motions = { Qt::Key_7, Qt::Key_8, }; const std::array, Settings::NativeAnalog::NumAnalogs> QtConfig::default_analogs{{ { Qt::Key_W, Qt::Key_S, Qt::Key_A, Qt::Key_D, }, { Qt::Key_I, Qt::Key_K, Qt::Key_J, Qt::Key_L, }, }}; const std::array QtConfig::default_stick_mod = { Qt::Key_Shift, 0, }; const std::array QtConfig::default_ringcon_analogs{{ Qt::Key_A, Qt::Key_D, }}; QtConfig::QtConfig(const std::string& config_name, const ConfigType config_type) : Config(config_type) { Initialize(config_name); if (config_type != ConfigType::InputProfile) { ReadQtValues(); SaveQtValues(); } } QtConfig::~QtConfig() { if (global) { QtConfig::SaveAllValues(); } } void QtConfig::ReloadAllValues() { Reload(); ReadQtValues(); SaveQtValues(); } void QtConfig::SaveAllValues() { SaveValues(); SaveQtValues(); } void QtConfig::ReadQtValues() { if (global) { ReadUIValues(); } ReadQtControlValues(); } void QtConfig::ReadQtPlayerValues(const std::size_t player_index) { std::string player_prefix; if (type != ConfigType::InputProfile) { player_prefix.append("player_").append(ToString(player_index)).append("_"); } auto& player = Settings::values.players.GetValue()[player_index]; if (IsCustomConfig()) { const auto profile_name = ReadStringSetting(std::string(player_prefix).append("profile_name")); if (profile_name.empty()) { // Use the global input config player = Settings::values.players.GetValue(true)[player_index]; return; } } for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); auto& player_buttons = player.buttons[i]; player_buttons = ReadStringSetting( std::string(player_prefix).append(Settings::NativeButton::mapping[i]), default_param); if (player_buttons.empty()) { player_buttons = default_param; } } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], default_analogs[i][3], default_stick_mod[i], 0.5f); auto& player_analogs = player.analogs[i]; player_analogs = ReadStringSetting( std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), default_param); if (player_analogs.empty()) { player_analogs = default_param; } } for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); auto& player_motions = player.motions[i]; player_motions = ReadStringSetting( std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), default_param); if (player_motions.empty()) { player_motions = default_param; } } } void QtConfig::ReadHidbusValues() { const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); auto& ringcon_analogs = Settings::values.ringcon_analogs; ringcon_analogs = ReadStringSetting(std::string("ring_controller"), default_param); if (ringcon_analogs.empty()) { ringcon_analogs = default_param; } } void QtConfig::ReadDebugControlValues() { for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); auto& debug_pad_buttons = Settings::values.debug_pad_buttons[i]; debug_pad_buttons = ReadStringSetting( std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), default_param); if (debug_pad_buttons.empty()) { debug_pad_buttons = default_param; } } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], default_analogs[i][3], default_stick_mod[i], 0.5f); auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i]; debug_pad_analogs = ReadStringSetting( std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), default_param); if (debug_pad_analogs.empty()) { debug_pad_analogs = default_param; } } } void QtConfig::ReadQtControlValues() { BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); Settings::values.players.SetGlobal(!IsCustomConfig()); for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { ReadQtPlayerValues(p); } if (IsCustomConfig()) { EndGroup(); return; } ReadDebugControlValues(); ReadHidbusValues(); EndGroup(); } void QtConfig::ReadPathValues() { BeginGroup(Settings::TranslateCategory(Settings::Category::Paths)); UISettings::values.roms_path = ReadStringSetting(std::string("romsPath")); UISettings::values.game_dir_deprecated = ReadStringSetting(std::string("gameListRootDir"), std::string(".")); UISettings::values.game_dir_deprecated_deepscan = ReadBooleanSetting(std::string("gameListDeepScan"), std::make_optional(false)); const int gamedirs_size = BeginArray(std::string("gamedirs")); for (int i = 0; i < gamedirs_size; ++i) { SetArrayIndex(i); UISettings::GameDir game_dir; game_dir.path = ReadStringSetting(std::string("path")); game_dir.deep_scan = ReadBooleanSetting(std::string("deep_scan"), std::make_optional(false)); game_dir.expanded = ReadBooleanSetting(std::string("expanded"), std::make_optional(true)); UISettings::values.game_dirs.append(game_dir); } EndArray(); // Create NAND and SD card directories if empty, these are not removable through the UI, // also carries over old game list settings if present if (UISettings::values.game_dirs.empty()) { UISettings::GameDir game_dir; game_dir.path = std::string("SDMC"); game_dir.expanded = true; UISettings::values.game_dirs.append(game_dir); game_dir.path = std::string("UserNAND"); UISettings::values.game_dirs.append(game_dir); game_dir.path = std::string("SysNAND"); UISettings::values.game_dirs.append(game_dir); if (UISettings::values.game_dir_deprecated != std::string(".")) { game_dir.path = UISettings::values.game_dir_deprecated; game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan; UISettings::values.game_dirs.append(game_dir); } } UISettings::values.recent_files = QString::fromStdString(ReadStringSetting(std::string("recentFiles"))) .split(QStringLiteral(", "), Qt::SkipEmptyParts, Qt::CaseSensitive); ReadCategory(Settings::Category::Paths); EndGroup(); } void QtConfig::ReadShortcutValues() { BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts)); for (const auto& [name, group, shortcut] : UISettings::default_hotkeys) { BeginGroup(group); BeginGroup(name); // No longer using ReadSetting for shortcut.second as it inaccurately returns a value of 1 // for WidgetWithChildrenShortcut which is a value of 3. Needed to fix shortcuts the open // a file dialog in windowed mode UISettings::values.shortcuts.push_back( {name, group, {ReadStringSetting(std::string("KeySeq"), shortcut.keyseq), ReadStringSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq), shortcut.context, ReadBooleanSetting(std::string("Repeat"), std::optional(shortcut.repeat))}}); EndGroup(); // name EndGroup(); // group } EndGroup(); } void QtConfig::ReadUIValues() { BeginGroup(Settings::TranslateCategory(Settings::Category::Ui)); UISettings::values.theme = ReadStringSetting( std::string("theme"), std::string(UISettings::themes[static_cast(UISettings::default_theme)].second)); ReadUIGamelistValues(); ReadUILayoutValues(); ReadPathValues(); ReadScreenshotValues(); ReadShortcutValues(); ReadMultiplayerValues(); ReadCategory(Settings::Category::Ui); ReadCategory(Settings::Category::UiGeneral); EndGroup(); } void QtConfig::ReadUIGamelistValues() { BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList)); ReadCategory(Settings::Category::UiGameList); const int favorites_size = BeginArray("favorites"); for (int i = 0; i < favorites_size; i++) { SetArrayIndex(i); UISettings::values.favorited_ids.append( ReadUnsignedIntegerSetting(std::string("program_id"))); } EndArray(); EndGroup(); } void QtConfig::ReadUILayoutValues() { BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList)); ReadCategory(Settings::Category::UiLayout); EndGroup(); } void QtConfig::ReadMultiplayerValues() { BeginGroup(Settings::TranslateCategory(Settings::Category::Multiplayer)); ReadCategory(Settings::Category::Multiplayer); // Read ban list back int size = BeginArray(std::string("username_ban_list")); UISettings::values.multiplayer_ban_list.first.resize(size); for (int i = 0; i < size; ++i) { SetArrayIndex(i); UISettings::values.multiplayer_ban_list.first[i] = ReadStringSetting(std::string("username"), std::string("")); } EndArray(); size = BeginArray(std::string("ip_ban_list")); UISettings::values.multiplayer_ban_list.second.resize(size); for (int i = 0; i < size; ++i) { UISettings::values.multiplayer_ban_list.second[i] = ReadStringSetting("username", std::string("")); } EndArray(); EndGroup(); } void QtConfig::SaveQtValues() { if (global) { LOG_DEBUG(Config, "Saving global Qt configuration values"); SaveUIValues(); } else { LOG_DEBUG(Config, "Saving Qt configuration values"); } SaveQtControlValues(); WriteToIni(); } void QtConfig::SaveQtPlayerValues(const std::size_t player_index) { std::string player_prefix; if (type != ConfigType::InputProfile) { player_prefix = std::string("player_").append(ToString(player_index)).append("_"); } const auto& player = Settings::values.players.GetValue()[player_index]; if (IsCustomConfig() && player.profile_name.empty()) { // No custom profile selected return; } for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); WriteStringSetting(std::string(player_prefix).append(Settings::NativeButton::mapping[i]), player.buttons[i], std::make_optional(default_param)); } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], default_analogs[i][3], default_stick_mod[i], 0.5f); WriteStringSetting(std::string(player_prefix).append(Settings::NativeAnalog::mapping[i]), player.analogs[i], std::make_optional(default_param)); } for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) { const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]); WriteStringSetting(std::string(player_prefix).append(Settings::NativeMotion::mapping[i]), player.motions[i], std::make_optional(default_param)); } } void QtConfig::SaveDebugControlValues() { for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); WriteStringSetting(std::string("debug_pad_").append(Settings::NativeButton::mapping[i]), Settings::values.debug_pad_buttons[i], std::make_optional(default_param)); } for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], default_analogs[i][3], default_stick_mod[i], 0.5f); WriteStringSetting(std::string("debug_pad_").append(Settings::NativeAnalog::mapping[i]), Settings::values.debug_pad_analogs[i], std::make_optional(default_param)); } } void QtConfig::SaveHidbusValues() { const std::string default_param = InputCommon::GenerateAnalogParamFromKeys( 0, 0, default_ringcon_analogs[0], default_ringcon_analogs[1], 0, 0.05f); WriteStringSetting(std::string("ring_controller"), Settings::values.ringcon_analogs, std::make_optional(default_param)); } void QtConfig::SaveQtControlValues() { BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); Settings::values.players.SetGlobal(!IsCustomConfig()); for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) { SaveQtPlayerValues(p); } if (IsCustomConfig()) { EndGroup(); return; } SaveDebugControlValues(); SaveHidbusValues(); EndGroup(); } void QtConfig::SavePathValues() { BeginGroup(Settings::TranslateCategory(Settings::Category::Paths)); WriteCategory(Settings::Category::Paths); WriteStringSetting(std::string("romsPath"), UISettings::values.roms_path); BeginArray(std::string("gamedirs")); for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { SetArrayIndex(i); const auto& game_dir = UISettings::values.game_dirs[i]; WriteStringSetting(std::string("path"), game_dir.path); WriteBooleanSetting(std::string("deep_scan"), game_dir.deep_scan, std::make_optional(false)); WriteBooleanSetting(std::string("expanded"), game_dir.expanded, std::make_optional(true)); } EndArray(); WriteStringSetting(std::string("recentFiles"), UISettings::values.recent_files.join(QStringLiteral(", ")).toStdString()); EndGroup(); } void QtConfig::SaveShortcutValues() { BeginGroup(Settings::TranslateCategory(Settings::Category::Shortcuts)); // Lengths of UISettings::values.shortcuts & default_hotkeys are same. // However, their ordering must also be the same. for (std::size_t i = 0; i < UISettings::default_hotkeys.size(); i++) { const auto& [name, group, shortcut] = UISettings::values.shortcuts[i]; const auto& default_hotkey = UISettings::default_hotkeys[i].shortcut; BeginGroup(group); BeginGroup(name); WriteStringSetting(std::string("KeySeq"), shortcut.keyseq, std::make_optional(default_hotkey.keyseq)); WriteStringSetting(std::string("Controller_KeySeq"), shortcut.controller_keyseq, std::make_optional(default_hotkey.controller_keyseq)); WriteIntegerSetting(std::string("Context"), shortcut.context, std::make_optional(default_hotkey.context)); WriteBooleanSetting(std::string("Repeat"), shortcut.repeat, std::make_optional(default_hotkey.repeat)); EndGroup(); // name EndGroup(); // group } EndGroup(); } void QtConfig::SaveUIValues() { BeginGroup(Settings::TranslateCategory(Settings::Category::Ui)); WriteCategory(Settings::Category::Ui); WriteCategory(Settings::Category::UiGeneral); WriteStringSetting( std::string("theme"), UISettings::values.theme, std::make_optional(std::string( UISettings::themes[static_cast(UISettings::default_theme)].second))); SaveUIGamelistValues(); SaveUILayoutValues(); SavePathValues(); SaveScreenshotValues(); SaveShortcutValues(); SaveMultiplayerValues(); EndGroup(); } void QtConfig::SaveUIGamelistValues() { BeginGroup(Settings::TranslateCategory(Settings::Category::UiGameList)); WriteCategory(Settings::Category::UiGameList); BeginArray(std::string("favorites")); for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) { SetArrayIndex(i); WriteIntegerSetting(std::string("program_id"), UISettings::values.favorited_ids[i]); } EndArray(); // favorites EndGroup(); } void QtConfig::SaveUILayoutValues() { BeginGroup(Settings::TranslateCategory(Settings::Category::UiLayout)); WriteCategory(Settings::Category::UiLayout); EndGroup(); } void QtConfig::SaveMultiplayerValues() { BeginGroup(std::string("Multiplayer")); WriteCategory(Settings::Category::Multiplayer); // Write ban list BeginArray(std::string("username_ban_list")); for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.first.size(); ++i) { SetArrayIndex(static_cast(i)); WriteStringSetting(std::string("username"), UISettings::values.multiplayer_ban_list.first[i]); } EndArray(); // username_ban_list BeginArray(std::string("ip_ban_list")); for (std::size_t i = 0; i < UISettings::values.multiplayer_ban_list.second.size(); ++i) { SetArrayIndex(static_cast(i)); WriteStringSetting(std::string("ip"), UISettings::values.multiplayer_ban_list.second[i]); } EndArray(); // ip_ban_list EndGroup(); } std::vector& QtConfig::FindRelevantList(Settings::Category category) { auto& map = Settings::values.linkage.by_category; if (map.contains(category)) { return Settings::values.linkage.by_category[category]; } return UISettings::values.linkage.by_category[category]; } void QtConfig::ReadQtControlPlayerValues(std::size_t player_index) { BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); ReadPlayerValues(player_index); ReadQtPlayerValues(player_index); EndGroup(); } void QtConfig::SaveQtControlPlayerValues(std::size_t player_index) { BeginGroup(Settings::TranslateCategory(Settings::Category::Controls)); LOG_DEBUG(Config, "Saving players control configuration values"); SavePlayerValues(player_index); SaveQtPlayerValues(player_index); EndGroup(); WriteToIni(); }