diff options
Diffstat (limited to 'src/yuzu/configuration')
24 files changed, 1558 insertions, 593 deletions
| diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 5e0d0e7af..1b2b1b2bb 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -13,17 +13,20 @@  #include "input_common/udp/client.h"  #include "yuzu/configuration/config.h" -Config::Config() { +Config::Config(const std::string& config_file, bool is_global) {      // TODO: Don't hardcode the path; let the frontend decide where to put the config files. -    qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; +    qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + config_file;      FileUtil::CreateFullPath(qt_config_loc);      qt_config =          std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); +    global = is_global;      Reload();  }  Config::~Config() { -    Save(); +    if (global) { +        Save(); +    }  }  const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { @@ -402,16 +405,19 @@ void Config::ApplyDefaultProfileIfInputInvalid() {  void Config::ReadAudioValues() {      qt_config->beginGroup(QStringLiteral("Audio")); -    Settings::values.sink_id = ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto")) -                                   .toString() -                                   .toStdString(); -    Settings::values.enable_audio_stretching = -        ReadSetting(QStringLiteral("enable_audio_stretching"), true).toBool(); -    Settings::values.audio_device_id = -        ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto")) -            .toString() -            .toStdString(); -    Settings::values.volume = ReadSetting(QStringLiteral("volume"), 1).toFloat(); +    if (global) { +        Settings::values.sink_id = +            ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto")) +                .toString() +                .toStdString(); +        Settings::values.audio_device_id = +            ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto")) +                .toString() +                .toStdString(); +    } +    ReadSettingGlobal(Settings::values.enable_audio_stretching, +                      QStringLiteral("enable_audio_stretching"), true); +    ReadSettingGlobal(Settings::values.volume, QStringLiteral("volume"), 1);      qt_config->endGroup();  } @@ -440,6 +446,8 @@ void Config::ReadControlValues() {              .toInt());      Settings::values.udp_pad_index =          static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt()); +    Settings::values.use_docked_mode = +        ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();      qt_config->endGroup();  } @@ -447,7 +455,7 @@ void Config::ReadControlValues() {  void Config::ReadCoreValues() {      qt_config->beginGroup(QStringLiteral("Core")); -    Settings::values.use_multi_core = ReadSetting(QStringLiteral("use_multi_core"), false).toBool(); +    ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), false);      qt_config->endGroup();  } @@ -628,32 +636,28 @@ void Config::ReadPathValues() {  void Config::ReadRendererValues() {      qt_config->beginGroup(QStringLiteral("Renderer")); -    Settings::values.renderer_backend = -        static_cast<Settings::RendererBackend>(ReadSetting(QStringLiteral("backend"), 0).toInt()); -    Settings::values.renderer_debug = ReadSetting(QStringLiteral("debug"), false).toBool(); -    Settings::values.vulkan_device = ReadSetting(QStringLiteral("vulkan_device"), 0).toInt(); -    Settings::values.aspect_ratio = ReadSetting(QStringLiteral("aspect_ratio"), 0).toInt(); -    Settings::values.max_anisotropy = ReadSetting(QStringLiteral("max_anisotropy"), 0).toInt(); -    Settings::values.use_frame_limit = -        ReadSetting(QStringLiteral("use_frame_limit"), true).toBool(); -    Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toUInt(); -    Settings::values.use_disk_shader_cache = -        ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool(); -    const int gpu_accuracy_level = ReadSetting(QStringLiteral("gpu_accuracy"), 0).toInt(); -    Settings::values.gpu_accuracy = static_cast<Settings::GPUAccuracy>(gpu_accuracy_level); -    Settings::values.use_asynchronous_gpu_emulation = -        ReadSetting(QStringLiteral("use_asynchronous_gpu_emulation"), false).toBool(); -    Settings::values.use_vsync = ReadSetting(QStringLiteral("use_vsync"), true).toBool(); -    Settings::values.use_assembly_shaders = -        ReadSetting(QStringLiteral("use_assembly_shaders"), false).toBool(); -    Settings::values.use_fast_gpu_time = -        ReadSetting(QStringLiteral("use_fast_gpu_time"), true).toBool(); -    Settings::values.force_30fps_mode = -        ReadSetting(QStringLiteral("force_30fps_mode"), false).toBool(); - -    Settings::values.bg_red = ReadSetting(QStringLiteral("bg_red"), 0.0).toFloat(); -    Settings::values.bg_green = ReadSetting(QStringLiteral("bg_green"), 0.0).toFloat(); -    Settings::values.bg_blue = ReadSetting(QStringLiteral("bg_blue"), 0.0).toFloat(); +    ReadSettingGlobal(Settings::values.renderer_backend, QStringLiteral("backend"), 0); +    ReadSettingGlobal(Settings::values.renderer_debug, QStringLiteral("debug"), false); +    ReadSettingGlobal(Settings::values.vulkan_device, QStringLiteral("vulkan_device"), 0); +    ReadSettingGlobal(Settings::values.aspect_ratio, QStringLiteral("aspect_ratio"), 0); +    ReadSettingGlobal(Settings::values.max_anisotropy, QStringLiteral("max_anisotropy"), 0); +    ReadSettingGlobal(Settings::values.use_frame_limit, QStringLiteral("use_frame_limit"), true); +    ReadSettingGlobal(Settings::values.frame_limit, QStringLiteral("frame_limit"), 100); +    ReadSettingGlobal(Settings::values.use_disk_shader_cache, +                      QStringLiteral("use_disk_shader_cache"), true); +    ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 0); +    ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation, +                      QStringLiteral("use_asynchronous_gpu_emulation"), false); +    ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true); +    ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"), +                      false); +    ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"), +                      true); +    ReadSettingGlobal(Settings::values.force_30fps_mode, QStringLiteral("force_30fps_mode"), false); + +    ReadSettingGlobal(Settings::values.bg_red, QStringLiteral("bg_red"), 0.0); +    ReadSettingGlobal(Settings::values.bg_green, QStringLiteral("bg_green"), 0.0); +    ReadSettingGlobal(Settings::values.bg_blue, QStringLiteral("bg_blue"), 0.0);      qt_config->endGroup();  } @@ -682,35 +686,45 @@ void Config::ReadShortcutValues() {  void Config::ReadSystemValues() {      qt_config->beginGroup(QStringLiteral("System")); -    Settings::values.use_docked_mode = -        ReadSetting(QStringLiteral("use_docked_mode"), false).toBool(); - -    Settings::values.current_user = std::clamp<int>( -        ReadSetting(QStringLiteral("current_user"), 0).toInt(), 0, Service::Account::MAX_USERS - 1); +    ReadSettingGlobal(Settings::values.current_user, QStringLiteral("current_user"), 0); +    Settings::values.current_user = +        std::clamp<int>(Settings::values.current_user, 0, Service::Account::MAX_USERS - 1); -    Settings::values.language_index = ReadSetting(QStringLiteral("language_index"), 1).toInt(); +    ReadSettingGlobal(Settings::values.language_index, QStringLiteral("language_index"), 1); -    Settings::values.region_index = ReadSetting(QStringLiteral("region_index"), 1).toInt(); +    ReadSettingGlobal(Settings::values.region_index, QStringLiteral("region_index"), 1); -    Settings::values.time_zone_index = ReadSetting(QStringLiteral("time_zone_index"), 0).toInt(); +    ReadSettingGlobal(Settings::values.time_zone_index, QStringLiteral("time_zone_index"), 0); -    const auto rng_seed_enabled = ReadSetting(QStringLiteral("rng_seed_enabled"), false).toBool(); -    if (rng_seed_enabled) { -        Settings::values.rng_seed = ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong(); -    } else { -        Settings::values.rng_seed = std::nullopt; +    bool rng_seed_enabled; +    ReadSettingGlobal(rng_seed_enabled, QStringLiteral("rng_seed_enabled"), false); +    bool rng_seed_global = +        global || qt_config->value(QStringLiteral("rng_seed/use_global"), true).toBool(); +    Settings::values.rng_seed.SetGlobal(rng_seed_global); +    if (global || !rng_seed_global) { +        if (rng_seed_enabled) { +            Settings::values.rng_seed.SetValue( +                ReadSetting(QStringLiteral("rng_seed"), 0).toULongLong()); +        } else { +            Settings::values.rng_seed.SetValue(std::nullopt); +        }      } -    const auto custom_rtc_enabled = -        ReadSetting(QStringLiteral("custom_rtc_enabled"), false).toBool(); -    if (custom_rtc_enabled) { -        Settings::values.custom_rtc = -            std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong()); -    } else { -        Settings::values.custom_rtc = std::nullopt; +    bool custom_rtc_enabled; +    ReadSettingGlobal(custom_rtc_enabled, QStringLiteral("custom_rtc_enabled"), false); +    bool custom_rtc_global = +        global || qt_config->value(QStringLiteral("custom_rtc/use_global"), true).toBool(); +    Settings::values.custom_rtc.SetGlobal(custom_rtc_global); +    if (global || !custom_rtc_global) { +        if (custom_rtc_enabled) { +            Settings::values.custom_rtc.SetValue( +                std::chrono::seconds(ReadSetting(QStringLiteral("custom_rtc"), 0).toULongLong())); +        } else { +            Settings::values.custom_rtc.SetValue(std::nullopt); +        }      } -    Settings::values.sound_index = ReadSetting(QStringLiteral("sound_index"), 1).toInt(); +    ReadSettingGlobal(Settings::values.sound_index, QStringLiteral("sound_index"), 1);      qt_config->endGroup();  } @@ -804,18 +818,20 @@ void Config::ReadWebServiceValues() {  }  void Config::ReadValues() { -    ReadControlValues(); +    if (global) { +        ReadControlValues(); +        ReadDataStorageValues(); +        ReadDebuggingValues(); +        ReadDisabledAddOnValues(); +        ReadServiceValues(); +        ReadUIValues(); +        ReadWebServiceValues(); +        ReadMiscellaneousValues(); +    }      ReadCoreValues();      ReadRendererValues();      ReadAudioValues(); -    ReadDataStorageValues();      ReadSystemValues(); -    ReadMiscellaneousValues(); -    ReadDebuggingValues(); -    ReadWebServiceValues(); -    ReadServiceValues(); -    ReadDisabledAddOnValues(); -    ReadUIValues();  }  void Config::SavePlayerValues() { @@ -902,30 +918,35 @@ void Config::SaveTouchscreenValues() {  }  void Config::SaveValues() { -    SaveControlValues(); +    if (global) { +        SaveControlValues(); +        SaveDataStorageValues(); +        SaveDebuggingValues(); +        SaveDisabledAddOnValues(); +        SaveServiceValues(); +        SaveUIValues(); +        SaveWebServiceValues(); +        SaveMiscellaneousValues(); +    }      SaveCoreValues();      SaveRendererValues();      SaveAudioValues(); -    SaveDataStorageValues();      SaveSystemValues(); -    SaveMiscellaneousValues(); -    SaveDebuggingValues(); -    SaveWebServiceValues(); -    SaveServiceValues(); -    SaveDisabledAddOnValues(); -    SaveUIValues();  }  void Config::SaveAudioValues() {      qt_config->beginGroup(QStringLiteral("Audio")); -    WriteSetting(QStringLiteral("output_engine"), QString::fromStdString(Settings::values.sink_id), -                 QStringLiteral("auto")); -    WriteSetting(QStringLiteral("enable_audio_stretching"), -                 Settings::values.enable_audio_stretching, true); -    WriteSetting(QStringLiteral("output_device"), -                 QString::fromStdString(Settings::values.audio_device_id), QStringLiteral("auto")); -    WriteSetting(QStringLiteral("volume"), Settings::values.volume, 1.0f); +    if (global) { +        WriteSetting(QStringLiteral("output_engine"), +                     QString::fromStdString(Settings::values.sink_id), QStringLiteral("auto")); +        WriteSetting(QStringLiteral("output_device"), +                     QString::fromStdString(Settings::values.audio_device_id), +                     QStringLiteral("auto")); +    } +    WriteSettingGlobal(QStringLiteral("enable_audio_stretching"), +                       Settings::values.enable_audio_stretching, true); +    WriteSettingGlobal(QStringLiteral("volume"), Settings::values.volume, 1.0f);      qt_config->endGroup();  } @@ -948,6 +969,7 @@ void Config::SaveControlValues() {      WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port,                   InputCommon::CemuhookUDP::DEFAULT_PORT);      WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0); +    WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);      qt_config->endGroup();  } @@ -955,7 +977,7 @@ void Config::SaveControlValues() {  void Config::SaveCoreValues() {      qt_config->beginGroup(QStringLiteral("Core")); -    WriteSetting(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false); +    WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false);      qt_config->endGroup();  } @@ -1078,29 +1100,34 @@ void Config::SavePathValues() {  void Config::SaveRendererValues() {      qt_config->beginGroup(QStringLiteral("Renderer")); -    WriteSetting(QStringLiteral("backend"), static_cast<int>(Settings::values.renderer_backend), 0); +    WriteSettingGlobal(QStringLiteral("backend"), +                       static_cast<int>(Settings::values.renderer_backend.GetValue(global)), +                       Settings::values.renderer_backend.UsingGlobal(), 0);      WriteSetting(QStringLiteral("debug"), Settings::values.renderer_debug, false); -    WriteSetting(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0); -    WriteSetting(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0); -    WriteSetting(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0); -    WriteSetting(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); -    WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); -    WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache, -                 true); -    WriteSetting(QStringLiteral("gpu_accuracy"), static_cast<int>(Settings::values.gpu_accuracy), -                 0); -    WriteSetting(QStringLiteral("use_asynchronous_gpu_emulation"), -                 Settings::values.use_asynchronous_gpu_emulation, false); -    WriteSetting(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); -    WriteSetting(QStringLiteral("use_assembly_shaders"), Settings::values.use_assembly_shaders, -                 false); -    WriteSetting(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, true); -    WriteSetting(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, false); +    WriteSettingGlobal(QStringLiteral("vulkan_device"), Settings::values.vulkan_device, 0); +    WriteSettingGlobal(QStringLiteral("aspect_ratio"), Settings::values.aspect_ratio, 0); +    WriteSettingGlobal(QStringLiteral("max_anisotropy"), Settings::values.max_anisotropy, 0); +    WriteSettingGlobal(QStringLiteral("use_frame_limit"), Settings::values.use_frame_limit, true); +    WriteSettingGlobal(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); +    WriteSettingGlobal(QStringLiteral("use_disk_shader_cache"), +                       Settings::values.use_disk_shader_cache, true); +    WriteSettingGlobal(QStringLiteral("gpu_accuracy"), +                       static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)), +                       Settings::values.gpu_accuracy.UsingGlobal(), 0); +    WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"), +                       Settings::values.use_asynchronous_gpu_emulation, false); +    WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true); +    WriteSettingGlobal(QStringLiteral("use_assembly_shaders"), +                       Settings::values.use_assembly_shaders, false); +    WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time, +                       true); +    WriteSettingGlobal(QStringLiteral("force_30fps_mode"), Settings::values.force_30fps_mode, +                       false);      // Cast to double because Qt's written float values are not human-readable -    WriteSetting(QStringLiteral("bg_red"), static_cast<double>(Settings::values.bg_red), 0.0); -    WriteSetting(QStringLiteral("bg_green"), static_cast<double>(Settings::values.bg_green), 0.0); -    WriteSetting(QStringLiteral("bg_blue"), static_cast<double>(Settings::values.bg_blue), 0.0); +    WriteSettingGlobal(QStringLiteral("bg_red"), Settings::values.bg_red, 0.0); +    WriteSettingGlobal(QStringLiteral("bg_green"), Settings::values.bg_green, 0.0); +    WriteSettingGlobal(QStringLiteral("bg_blue"), Settings::values.bg_blue, 0.0);      qt_config->endGroup();  } @@ -1128,23 +1155,28 @@ void Config::SaveShortcutValues() {  void Config::SaveSystemValues() {      qt_config->beginGroup(QStringLiteral("System")); -    WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);      WriteSetting(QStringLiteral("current_user"), Settings::values.current_user, 0); -    WriteSetting(QStringLiteral("language_index"), Settings::values.language_index, 1); -    WriteSetting(QStringLiteral("region_index"), Settings::values.region_index, 1); -    WriteSetting(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0); - -    WriteSetting(QStringLiteral("rng_seed_enabled"), Settings::values.rng_seed.has_value(), false); -    WriteSetting(QStringLiteral("rng_seed"), Settings::values.rng_seed.value_or(0), 0); - -    WriteSetting(QStringLiteral("custom_rtc_enabled"), Settings::values.custom_rtc.has_value(), -                 false); -    WriteSetting(QStringLiteral("custom_rtc"), -                 QVariant::fromValue<long long>( -                     Settings::values.custom_rtc.value_or(std::chrono::seconds{}).count()), -                 0); - -    WriteSetting(QStringLiteral("sound_index"), Settings::values.sound_index, 1); +    WriteSettingGlobal(QStringLiteral("language_index"), Settings::values.language_index, 1); +    WriteSettingGlobal(QStringLiteral("region_index"), Settings::values.region_index, 1); +    WriteSettingGlobal(QStringLiteral("time_zone_index"), Settings::values.time_zone_index, 0); + +    WriteSettingGlobal(QStringLiteral("rng_seed_enabled"), +                       Settings::values.rng_seed.GetValue(global).has_value(), +                       Settings::values.rng_seed.UsingGlobal(), false); +    WriteSettingGlobal(QStringLiteral("rng_seed"), +                       Settings::values.rng_seed.GetValue(global).value_or(0), +                       Settings::values.rng_seed.UsingGlobal(), 0); + +    WriteSettingGlobal(QStringLiteral("custom_rtc_enabled"), +                       Settings::values.custom_rtc.GetValue(global).has_value(), +                       Settings::values.custom_rtc.UsingGlobal(), false); +    WriteSettingGlobal( +        QStringLiteral("custom_rtc"), +        QVariant::fromValue<long long>( +            Settings::values.custom_rtc.GetValue(global).value_or(std::chrono::seconds{}).count()), +        Settings::values.custom_rtc.UsingGlobal(), 0); + +    WriteSettingGlobal(QStringLiteral("sound_index"), Settings::values.sound_index, 1);      qt_config->endGroup();  } @@ -1236,6 +1268,34 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value)      return result;  } +template <typename Type> +void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name) { +    const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); +    setting.SetGlobal(use_global); +    if (global || !use_global) { +        setting.SetValue(ReadSetting(name).value<Type>()); +    } +} + +template <typename Type> +void Config::ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name, +                               const QVariant& default_value) { +    const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); +    setting.SetGlobal(use_global); +    if (global || !use_global) { +        setting.SetValue(ReadSetting(name, default_value).value<Type>()); +    } +} + +template <typename Type> +void Config::ReadSettingGlobal(Type& setting, const QString& name, +                               const QVariant& default_value) const { +    const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); +    if (global || !use_global) { +        setting = ReadSetting(name, default_value).value<Type>(); +    } +} +  void Config::WriteSetting(const QString& name, const QVariant& value) {      qt_config->setValue(name, value);  } @@ -1246,6 +1306,40 @@ void Config::WriteSetting(const QString& name, const QVariant& value,      qt_config->setValue(name, value);  } +template <typename Type> +void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting) { +    if (!global) { +        qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal()); +    } +    if (global || !setting.UsingGlobal()) { +        qt_config->setValue(name, setting.GetValue(global)); +    } +} + +template <typename Type> +void Config::WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting, +                                const QVariant& default_value) { +    if (!global) { +        qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal()); +    } +    if (global || !setting.UsingGlobal()) { +        qt_config->setValue(name + QStringLiteral("/default"), +                            setting.GetValue(global) == default_value.value<Type>()); +        qt_config->setValue(name, setting.GetValue(global)); +    } +} + +void Config::WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global, +                                const QVariant& default_value) { +    if (!global) { +        qt_config->setValue(name + QStringLiteral("/use_global"), use_global); +    } +    if (global || !use_global) { +        qt_config->setValue(name + QStringLiteral("/default"), value == default_value); +        qt_config->setValue(name, value); +    } +} +  void Config::Reload() {      ReadValues();      // To apply default value changes diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index 09316382c..681f0bca5 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h @@ -7,6 +7,7 @@  #include <array>  #include <memory>  #include <string> +#include <QMetaType>  #include <QVariant>  #include "core/settings.h"  #include "yuzu/uisettings.h" @@ -15,7 +16,7 @@ class QSettings;  class Config {  public: -    Config(); +    explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true);      ~Config();      void Reload(); @@ -82,9 +83,33 @@ private:      QVariant ReadSetting(const QString& name) const;      QVariant ReadSetting(const QString& name, const QVariant& default_value) const; +    // Templated ReadSettingGlobal functions will also look for the use_global setting and set +    // both the value and the global state properly +    template <typename Type> +    void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name); +    template <typename Type> +    void ReadSettingGlobal(Settings::Setting<Type>& setting, const QString& name, +                           const QVariant& default_value); +    template <typename Type> +    void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const; +    // Templated WriteSettingGlobal functions will also write the global state if needed and will +    // skip writing the actual setting if it defers to the global value      void WriteSetting(const QString& name, const QVariant& value);      void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); +    template <typename Type> +    void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting); +    template <typename Type> +    void WriteSettingGlobal(const QString& name, const Settings::Setting<Type>& setting, +                            const QVariant& default_value); +    void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global, +                            const QVariant& default_value);      std::unique_ptr<QSettings> qt_config;      std::string qt_config_loc; + +    bool global;  }; + +// These metatype declarations cannot be in core/settings.h because core is devoid of QT +Q_DECLARE_METATYPE(Settings::RendererBackend); +Q_DECLARE_METATYPE(Settings::GPUAccuracy); diff --git a/src/yuzu/configuration/configuration_shared.cpp b/src/yuzu/configuration/configuration_shared.cpp new file mode 100644 index 000000000..bb47c3933 --- /dev/null +++ b/src/yuzu/configuration/configuration_shared.cpp @@ -0,0 +1,76 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <QCheckBox> +#include <QComboBox> +#include "core/settings.h" +#include "yuzu/configuration/configuration_shared.h" +#include "yuzu/configuration/configure_per_game.h" + +void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<bool>* setting, +                                              const QCheckBox* checkbox) { +    if (checkbox->checkState() == Qt::PartiallyChecked) { +        setting->SetGlobal(true); +    } else { +        setting->SetGlobal(false); +        setting->SetValue(checkbox->checkState() == Qt::Checked); +    } +} + +void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<int>* setting, +                                              const QComboBox* combobox) { +    if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { +        setting->SetGlobal(true); +    } else { +        setting->SetGlobal(false); +        setting->SetValue(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET); +    } +} + +void ConfigurationShared::ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting, +                                              const QComboBox* combobox) { +    if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { +        setting->SetGlobal(true); +    } else { +        setting->SetGlobal(false); +        setting->SetValue(static_cast<Settings::RendererBackend>( +            combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET)); +    } +} + +void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox, +                                            const Settings::Setting<bool>* setting) { +    if (setting->UsingGlobal()) { +        checkbox->setCheckState(Qt::PartiallyChecked); +    } else { +        checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked); +    } +} + +void ConfigurationShared::SetPerGameSetting(QComboBox* combobox, +                                            const Settings::Setting<int>* setting) { +    combobox->setCurrentIndex(setting->UsingGlobal() +                                  ? ConfigurationShared::USE_GLOBAL_INDEX +                                  : setting->GetValue() + ConfigurationShared::USE_GLOBAL_OFFSET); +} + +void ConfigurationShared::SetPerGameSetting( +    QComboBox* combobox, const Settings::Setting<Settings::RendererBackend>* setting) { +    combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX +                                                     : static_cast<int>(setting->GetValue()) + +                                                           ConfigurationShared::USE_GLOBAL_OFFSET); +} + +void ConfigurationShared::SetPerGameSetting( +    QComboBox* combobox, const Settings::Setting<Settings::GPUAccuracy>* setting) { +    combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX +                                                     : static_cast<int>(setting->GetValue()) + +                                                           ConfigurationShared::USE_GLOBAL_OFFSET); +} + +void ConfigurationShared::InsertGlobalItem(QComboBox* combobox) { +    const QString use_global_text = ConfigurePerGame::tr("Use global configuration"); +    combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text); +    combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX); +} diff --git a/src/yuzu/configuration/configuration_shared.h b/src/yuzu/configuration/configuration_shared.h new file mode 100644 index 000000000..b11b1b950 --- /dev/null +++ b/src/yuzu/configuration/configuration_shared.h @@ -0,0 +1,36 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <QCheckBox> +#include <QComboBox> +#include <QString> +#include "core/settings.h" + +namespace ConfigurationShared { + +constexpr int USE_GLOBAL_INDEX = 0; +constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1; +constexpr int USE_GLOBAL_OFFSET = 2; + +// Global-aware apply and set functions + +void ApplyPerGameSetting(Settings::Setting<bool>* setting, const QCheckBox* checkbox); +void ApplyPerGameSetting(Settings::Setting<int>* setting, const QComboBox* combobox); +void ApplyPerGameSetting(Settings::Setting<Settings::RendererBackend>* setting, +                         const QComboBox* combobox); +void ApplyPerGameSetting(Settings::Setting<Settings::GPUAccuracy>* setting, +                         const QComboBox* combobox); + +void SetPerGameSetting(QCheckBox* checkbox, const Settings::Setting<bool>* setting); +void SetPerGameSetting(QComboBox* combobox, const Settings::Setting<int>* setting); +void SetPerGameSetting(QComboBox* combobox, +                       const Settings::Setting<Settings::RendererBackend>* setting); +void SetPerGameSetting(QComboBox* combobox, +                       const Settings::Setting<Settings::GPUAccuracy>* setting); + +void InsertGlobalItem(QComboBox* combobox); + +} // namespace ConfigurationShared diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index f370c690f..cc021beec 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp @@ -11,6 +11,7 @@  #include "core/core.h"  #include "core/settings.h"  #include "ui_configure_audio.h" +#include "yuzu/configuration/configuration_shared.h"  #include "yuzu/configuration/configure_audio.h"  ConfigureAudio::ConfigureAudio(QWidget* parent) @@ -24,6 +25,11 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)      connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,              &ConfigureAudio::UpdateAudioDevices); +    ui->volume_label->setVisible(Settings::configuring_global); +    ui->volume_combo_box->setVisible(!Settings::configuring_global); + +    SetupPerGameUI(); +      SetConfiguration();      const bool is_powered_on = Core::System::GetInstance().IsPoweredOn(); @@ -41,8 +47,22 @@ void ConfigureAudio::SetConfiguration() {      SetAudioDeviceFromDeviceID(); -    ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching); -    ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum()); +    ui->volume_slider->setValue(Settings::values.volume.GetValue() * ui->volume_slider->maximum()); + +    if (Settings::configuring_global) { +        ui->toggle_audio_stretching->setChecked( +            Settings::values.enable_audio_stretching.GetValue()); +    } else { +        ConfigurationShared::SetPerGameSetting(ui->toggle_audio_stretching, +                                               &Settings::values.enable_audio_stretching); +        if (Settings::values.volume.UsingGlobal()) { +            ui->volume_combo_box->setCurrentIndex(0); +            ui->volume_slider->setEnabled(false); +        } else { +            ui->volume_combo_box->setCurrentIndex(1); +            ui->volume_slider->setEnabled(true); +        } +    }      SetVolumeIndicatorText(ui->volume_slider->sliderPosition());  } @@ -80,15 +100,36 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) {  }  void ConfigureAudio::ApplyConfiguration() { -    Settings::values.sink_id = -        ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) -            .toStdString(); -    Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked(); -    Settings::values.audio_device_id = -        ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex()) -            .toStdString(); -    Settings::values.volume = -        static_cast<float>(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum(); +    if (Settings::configuring_global) { +        Settings::values.sink_id = +            ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) +                .toStdString(); +        Settings::values.audio_device_id = +            ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex()) +                .toStdString(); + +        // Guard if during game and set to game-specific value +        if (Settings::values.enable_audio_stretching.UsingGlobal()) { +            Settings::values.enable_audio_stretching.SetValue( +                ui->toggle_audio_stretching->isChecked()); +        } +        if (Settings::values.volume.UsingGlobal()) { +            Settings::values.volume.SetValue( +                static_cast<float>(ui->volume_slider->sliderPosition()) / +                ui->volume_slider->maximum()); +        } +    } else { +        ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching, +                                                 ui->toggle_audio_stretching); +        if (ui->volume_combo_box->currentIndex() == 0) { +            Settings::values.volume.SetGlobal(true); +        } else { +            Settings::values.volume.SetGlobal(false); +            Settings::values.volume.SetValue( +                static_cast<float>(ui->volume_slider->sliderPosition()) / +                ui->volume_slider->maximum()); +        } +    }  }  void ConfigureAudio::changeEvent(QEvent* event) { @@ -122,3 +163,22 @@ void ConfigureAudio::RetranslateUI() {      ui->retranslateUi(this);      SetVolumeIndicatorText(ui->volume_slider->sliderPosition());  } + +void ConfigureAudio::SetupPerGameUI() { +    if (Settings::configuring_global) { +        ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal()); +        ui->toggle_audio_stretching->setEnabled( +            Settings::values.enable_audio_stretching.UsingGlobal()); + +        return; +    } + +    ui->toggle_audio_stretching->setTristate(true); +    connect(ui->volume_combo_box, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), +            this, [this](int index) { ui->volume_slider->setEnabled(index == 1); }); + +    ui->output_sink_combo_box->setVisible(false); +    ui->output_sink_label->setVisible(false); +    ui->audio_device_combo_box->setVisible(false); +    ui->audio_device_label->setVisible(false); +} diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h index ea83bd72d..d84f4a682 100644 --- a/src/yuzu/configuration/configure_audio.h +++ b/src/yuzu/configuration/configure_audio.h @@ -34,5 +34,7 @@ private:      void SetAudioDeviceFromDeviceID();      void SetVolumeIndicatorText(int percentage); +    void SetupPerGameUI(); +      std::unique_ptr<Ui::ConfigureAudio> ui;  }; diff --git a/src/yuzu/configuration/configure_audio.ui b/src/yuzu/configuration/configure_audio.ui index a098b9acc..862ccb988 100644 --- a/src/yuzu/configuration/configure_audio.ui +++ b/src/yuzu/configuration/configure_audio.ui @@ -6,8 +6,8 @@     <rect>      <x>0</x>      <y>0</y> -    <width>188</width> -    <height>246</height> +    <width>367</width> +    <height>368</height>     </rect>    </property>    <layout class="QVBoxLayout"> @@ -18,9 +18,9 @@       </property>       <layout class="QVBoxLayout">        <item> -       <layout class="QHBoxLayout"> +       <layout class="QHBoxLayout" name="_3">          <item> -         <widget class="QLabel" name="label_1"> +         <widget class="QLabel" name="output_sink_label">            <property name="text">             <string>Output Engine:</string>            </property> @@ -31,20 +31,20 @@          </item>         </layout>        </item> -       <item> -         <widget class="QCheckBox" name="toggle_audio_stretching"> -           <property name="toolTip"> -             <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string> -           </property> -           <property name="text"> -             <string>Enable audio stretching</string> -           </property> -         </widget> -       </item>        <item> -       <layout class="QHBoxLayout"> +       <widget class="QCheckBox" name="toggle_audio_stretching"> +        <property name="toolTip"> +         <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string> +        </property> +        <property name="text"> +         <string>Enable audio stretching</string> +        </property> +       </widget> +      </item> +      <item> +       <layout class="QHBoxLayout" name="_2">          <item> -         <widget class="QLabel" name="label_2"> +         <widget class="QLabel" name="audio_device_label">            <property name="text">             <string>Audio Device:</string>            </property> @@ -61,7 +61,21 @@           <number>0</number>          </property>          <item> -         <widget class="QLabel" name="label_3"> +         <widget class="QComboBox" name="volume_combo_box"> +          <item> +           <property name="text"> +            <string>Use global volume</string> +           </property> +          </item> +          <item> +           <property name="text"> +            <string>Set volume:</string> +           </property> +          </item> +         </widget> +        </item> +        <item> +         <widget class="QLabel" name="volume_label">            <property name="text">             <string>Volume:</string>            </property> @@ -74,7 +88,7 @@            </property>            <property name="sizeHint" stdset="0">             <size> -            <width>40</width> +            <width>30</width>              <height>20</height>             </size>            </property> diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index df4473b46..5918e9972 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -14,6 +14,8 @@  ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)      : QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) { +    Settings::configuring_global = true; +      ui->setupUi(this);      ui->hotkeysTab->Populate(registry);      setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 74b2ad537..1fb62d1cf 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp @@ -7,17 +7,21 @@  #include "core/core.h"  #include "core/settings.h"  #include "ui_configure_general.h" +#include "yuzu/configuration/configuration_shared.h"  #include "yuzu/configuration/configure_general.h"  #include "yuzu/uisettings.h"  ConfigureGeneral::ConfigureGeneral(QWidget* parent)      : QWidget(parent), ui(new Ui::ConfigureGeneral) { -      ui->setupUi(this); +    SetupPerGameUI(); +      SetConfiguration(); -    connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled); +    connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, [this]() { +        ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked); +    });  }  ConfigureGeneral::~ConfigureGeneral() = default; @@ -26,27 +30,56 @@ void ConfigureGeneral::SetConfiguration() {      const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();      ui->use_multi_core->setEnabled(runtime_lock); -    ui->use_multi_core->setChecked(Settings::values.use_multi_core); +    ui->use_multi_core->setChecked(Settings::values.use_multi_core.GetValue());      ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing);      ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot);      ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background);      ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse); -    ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); -    ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); -    ui->frame_limit->setValue(Settings::values.frame_limit); +    ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue()); +    ui->frame_limit->setValue(Settings::values.frame_limit.GetValue()); + +    if (!Settings::configuring_global) { +        if (Settings::values.use_multi_core.UsingGlobal()) { +            ui->use_multi_core->setCheckState(Qt::PartiallyChecked); +        } +        if (Settings::values.use_frame_limit.UsingGlobal()) { +            ui->toggle_frame_limit->setCheckState(Qt::PartiallyChecked); +        } +    } + +    ui->frame_limit->setEnabled(ui->toggle_frame_limit->checkState() == Qt::Checked && +                                ui->toggle_frame_limit->isEnabled());  }  void ConfigureGeneral::ApplyConfiguration() { -    UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); -    UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); -    UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); -    UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); - -    Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); -    Settings::values.frame_limit = ui->frame_limit->value(); -    Settings::values.use_multi_core = ui->use_multi_core->isChecked(); +    if (Settings::configuring_global) { +        UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); +        UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); +        UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); +        UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); + +        // Guard if during game and set to game-specific value +        if (Settings::values.use_frame_limit.UsingGlobal()) { +            Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() == +                                                      Qt::Checked); +            Settings::values.frame_limit.SetValue(ui->frame_limit->value()); +            Settings::values.use_multi_core.SetValue(ui->use_multi_core->isChecked()); +        } +    } else { +        ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_multi_core, +                                                 ui->use_multi_core); + +        bool global_frame_limit = ui->toggle_frame_limit->checkState() == Qt::PartiallyChecked; +        Settings::values.use_frame_limit.SetGlobal(global_frame_limit); +        Settings::values.frame_limit.SetGlobal(global_frame_limit); +        if (!global_frame_limit) { +            Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() == +                                                      Qt::Checked); +            Settings::values.frame_limit.SetValue(ui->frame_limit->value()); +        } +    }  }  void ConfigureGeneral::changeEvent(QEvent* event) { @@ -60,3 +93,20 @@ void ConfigureGeneral::changeEvent(QEvent* event) {  void ConfigureGeneral::RetranslateUI() {      ui->retranslateUi(this);  } + +void ConfigureGeneral::SetupPerGameUI() { +    if (Settings::configuring_global) { +        ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal()); +        ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal()); + +        return; +    } + +    ui->toggle_check_exit->setVisible(false); +    ui->toggle_user_on_boot->setVisible(false); +    ui->toggle_background_pause->setVisible(false); +    ui->toggle_hide_mouse->setVisible(false); + +    ui->toggle_frame_limit->setTristate(true); +    ui->use_multi_core->setTristate(true); +} diff --git a/src/yuzu/configuration/configure_general.h b/src/yuzu/configuration/configure_general.h index ef05ce065..9c785c22e 100644 --- a/src/yuzu/configuration/configure_general.h +++ b/src/yuzu/configuration/configure_general.h @@ -28,5 +28,7 @@ private:      void SetConfiguration(); +    void SetupPerGameUI(); +      std::unique_ptr<Ui::ConfigureGeneral> ui;  }; diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 304625cd7..cb4706bd6 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -13,6 +13,7 @@  #include "core/core.h"  #include "core/settings.h"  #include "ui_configure_graphics.h" +#include "yuzu/configuration/configuration_shared.h"  #include "yuzu/configuration/configure_graphics.h"  #ifdef HAS_VULKAN @@ -21,16 +22,18 @@  ConfigureGraphics::ConfigureGraphics(QWidget* parent)      : QWidget(parent), ui(new Ui::ConfigureGraphics) { -    vulkan_device = Settings::values.vulkan_device; +    vulkan_device = Settings::values.vulkan_device.GetValue();      RetrieveVulkanDevices();      ui->setupUi(this); +    SetupPerGameUI(); +      SetConfiguration(); -    connect(ui->api, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, +    connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this,              [this] { UpdateDeviceComboBox(); }); -    connect(ui->device, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, +    connect(ui->device, qOverload<int>(&QComboBox::activated), this,              [this](int device) { UpdateDeviceSelection(device); });      connect(ui->bg_button, &QPushButton::clicked, this, [this] { @@ -40,6 +43,9 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)          }          UpdateBackgroundColorButton(new_bg_color);      }); + +    ui->bg_label->setVisible(Settings::configuring_global); +    ui->bg_combobox->setVisible(!Settings::configuring_global);  }  void ConfigureGraphics::UpdateDeviceSelection(int device) { @@ -57,27 +63,95 @@ void ConfigureGraphics::SetConfiguration() {      const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();      ui->api->setEnabled(runtime_lock); -    ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend)); -    ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio); -    ui->use_disk_shader_cache->setEnabled(runtime_lock); -    ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache);      ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock); -    ui->use_asynchronous_gpu_emulation->setChecked(Settings::values.use_asynchronous_gpu_emulation); -    UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, -                                                 Settings::values.bg_blue)); +    ui->use_disk_shader_cache->setEnabled(runtime_lock); + +    if (Settings::configuring_global) { +        ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue())); +        ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue()); +        ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); +        ui->use_asynchronous_gpu_emulation->setChecked( +            Settings::values.use_asynchronous_gpu_emulation.GetValue()); +    } else { +        ConfigurationShared::SetPerGameSetting(ui->use_disk_shader_cache, +                                               &Settings::values.use_disk_shader_cache); +        ConfigurationShared::SetPerGameSetting(ui->use_asynchronous_gpu_emulation, +                                               &Settings::values.use_asynchronous_gpu_emulation); + +        ConfigurationShared::SetPerGameSetting(ui->api, &Settings::values.renderer_backend); +        ConfigurationShared::SetPerGameSetting(ui->aspect_ratio_combobox, +                                               &Settings::values.aspect_ratio); + +        ui->bg_combobox->setCurrentIndex(Settings::values.bg_red.UsingGlobal() ? 0 : 1); +        ui->bg_button->setEnabled(!Settings::values.bg_red.UsingGlobal()); +    } + +    UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red.GetValue(), +                                                 Settings::values.bg_green.GetValue(), +                                                 Settings::values.bg_blue.GetValue()));      UpdateDeviceComboBox();  }  void ConfigureGraphics::ApplyConfiguration() { -    Settings::values.renderer_backend = GetCurrentGraphicsBackend(); -    Settings::values.vulkan_device = vulkan_device; -    Settings::values.aspect_ratio = ui->aspect_ratio_combobox->currentIndex(); -    Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); -    Settings::values.use_asynchronous_gpu_emulation = -        ui->use_asynchronous_gpu_emulation->isChecked(); -    Settings::values.bg_red = static_cast<float>(bg_color.redF()); -    Settings::values.bg_green = static_cast<float>(bg_color.greenF()); -    Settings::values.bg_blue = static_cast<float>(bg_color.blueF()); +    if (Settings::configuring_global) { +        // Guard if during game and set to game-specific value +        if (Settings::values.renderer_backend.UsingGlobal()) { +            Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend()); +        } +        if (Settings::values.vulkan_device.UsingGlobal()) { +            Settings::values.vulkan_device.SetValue(vulkan_device); +        } +        if (Settings::values.aspect_ratio.UsingGlobal()) { +            Settings::values.aspect_ratio.SetValue(ui->aspect_ratio_combobox->currentIndex()); +        } +        if (Settings::values.use_disk_shader_cache.UsingGlobal()) { +            Settings::values.use_disk_shader_cache.SetValue(ui->use_disk_shader_cache->isChecked()); +        } +        if (Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()) { +            Settings::values.use_asynchronous_gpu_emulation.SetValue( +                ui->use_asynchronous_gpu_emulation->isChecked()); +        } +        if (Settings::values.bg_red.UsingGlobal()) { +            Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF())); +            Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF())); +            Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF())); +        } +    } else { +        if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { +            Settings::values.renderer_backend.SetGlobal(true); +            Settings::values.vulkan_device.SetGlobal(true); +        } else { +            Settings::values.renderer_backend.SetGlobal(false); +            Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend()); +            if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) { +                Settings::values.vulkan_device.SetGlobal(false); +                Settings::values.vulkan_device.SetValue(vulkan_device); +            } else { +                Settings::values.vulkan_device.SetGlobal(true); +            } +        } + +        ConfigurationShared::ApplyPerGameSetting(&Settings::values.aspect_ratio, +                                                 ui->aspect_ratio_combobox); + +        ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache, +                                                 ui->use_disk_shader_cache); +        ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation, +                                                 ui->use_asynchronous_gpu_emulation); + +        if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { +            Settings::values.bg_red.SetGlobal(true); +            Settings::values.bg_green.SetGlobal(true); +            Settings::values.bg_blue.SetGlobal(true); +        } else { +            Settings::values.bg_red.SetGlobal(false); +            Settings::values.bg_green.SetGlobal(false); +            Settings::values.bg_blue.SetGlobal(false); +            Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF())); +            Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF())); +            Settings::values.bg_blue.SetValue(static_cast<float>(bg_color.blueF())); +        } +    }  }  void ConfigureGraphics::changeEvent(QEvent* event) { @@ -106,19 +180,27 @@ void ConfigureGraphics::UpdateDeviceComboBox() {      ui->device->clear();      bool enabled = false; + +    if (!Settings::configuring_global && +        ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { +        vulkan_device = Settings::values.vulkan_device.GetValue(); +    }      switch (GetCurrentGraphicsBackend()) {      case Settings::RendererBackend::OpenGL:          ui->device->addItem(tr("OpenGL Graphics Device"));          enabled = false;          break;      case Settings::RendererBackend::Vulkan: -        for (const auto device : vulkan_devices) { +        for (const auto& device : vulkan_devices) {              ui->device->addItem(device);          }          ui->device->setCurrentIndex(vulkan_device);          enabled = !vulkan_devices.empty();          break;      } +    // If in per-game config and use global is selected, don't enable. +    enabled &= !(!Settings::configuring_global && +                 ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX);      ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn());  } @@ -132,5 +214,37 @@ void ConfigureGraphics::RetrieveVulkanDevices() {  }  Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { -    return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); +    if (Settings::configuring_global) { +        return static_cast<Settings::RendererBackend>(ui->api->currentIndex()); +    } + +    if (ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { +        Settings::values.renderer_backend.SetGlobal(true); +        return Settings::values.renderer_backend.GetValue(); +    } +    Settings::values.renderer_backend.SetGlobal(false); +    return static_cast<Settings::RendererBackend>(ui->api->currentIndex() - +                                                  ConfigurationShared::USE_GLOBAL_OFFSET); +} + +void ConfigureGraphics::SetupPerGameUI() { +    if (Settings::configuring_global) { +        ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal()); +        ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal()); +        ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal()); +        ui->use_asynchronous_gpu_emulation->setEnabled( +            Settings::values.use_asynchronous_gpu_emulation.UsingGlobal()); +        ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal()); +        ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal()); + +        return; +    } + +    connect(ui->bg_combobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, +            [this](int index) { ui->bg_button->setEnabled(index == 1); }); + +    ui->use_disk_shader_cache->setTristate(true); +    ui->use_asynchronous_gpu_emulation->setTristate(true); +    ConfigurationShared::InsertGlobalItem(ui->aspect_ratio_combobox); +    ConfigurationShared::InsertGlobalItem(ui->api);  } diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index 7e0596d9c..24f01c739 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h @@ -35,6 +35,8 @@ private:      void RetrieveVulkanDevices(); +    void SetupPerGameUI(); +      Settings::RendererBackend GetCurrentGraphicsBackend() const;      std::unique_ptr<Ui::ConfigureGraphics> ui; diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 6e75447a5..62418fc14 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -122,6 +122,29 @@          <item>           <layout class="QHBoxLayout" name="horizontalLayout_3">            <item> +           <widget class="QComboBox" name="bg_combobox"> +            <property name="currentText"> +             <string>Use global background color</string> +            </property> +            <property name="currentIndex"> +             <number>0</number> +            </property> +            <property name="maxVisibleItems"> +             <number>10</number> +            </property> +            <item> +             <property name="text"> +              <string>Use global background color</string> +             </property> +            </item> +            <item> +             <property name="text"> +              <string>Set background color:</string> +             </property> +            </item> +           </widget> +          </item> +          <item>             <widget class="QLabel" name="bg_label">              <property name="text">               <string>Background Color:</string> @@ -129,6 +152,19 @@             </widget>            </item>            <item> +           <spacer name="horizontalSpacer"> +            <property name="orientation"> +             <enum>Qt::Horizontal</enum> +            </property> +            <property name="sizeHint" stdset="0"> +             <size> +              <width>40</width> +              <height>20</height> +             </size> +            </property> +           </spacer> +          </item> +          <item>             <widget class="QPushButton" name="bg_button">              <property name="maximumSize">               <size> diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp index be5006ad3..7c0fa7ec5 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.cpp +++ b/src/yuzu/configuration/configure_graphics_advanced.cpp @@ -5,6 +5,7 @@  #include "core/core.h"  #include "core/settings.h"  #include "ui_configure_graphics_advanced.h" +#include "yuzu/configuration/configuration_shared.h"  #include "yuzu/configuration/configure_graphics_advanced.h"  ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent) @@ -12,6 +13,8 @@ ConfigureGraphicsAdvanced::ConfigureGraphicsAdvanced(QWidget* parent)      ui->setupUi(this); +    SetupPerGameUI(); +      SetConfiguration();  } @@ -19,26 +22,81 @@ ConfigureGraphicsAdvanced::~ConfigureGraphicsAdvanced() = default;  void ConfigureGraphicsAdvanced::SetConfiguration() {      const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn(); -    ui->gpu_accuracy->setCurrentIndex(static_cast<int>(Settings::values.gpu_accuracy));      ui->use_vsync->setEnabled(runtime_lock); -    ui->use_vsync->setChecked(Settings::values.use_vsync);      ui->use_assembly_shaders->setEnabled(runtime_lock); -    ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders); -    ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time);      ui->force_30fps_mode->setEnabled(runtime_lock); -    ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);      ui->anisotropic_filtering_combobox->setEnabled(runtime_lock); -    ui->anisotropic_filtering_combobox->setCurrentIndex(Settings::values.max_anisotropy); + +    if (Settings::configuring_global) { +        ui->gpu_accuracy->setCurrentIndex( +            static_cast<int>(Settings::values.gpu_accuracy.GetValue())); +        ui->use_vsync->setChecked(Settings::values.use_vsync.GetValue()); +        ui->use_assembly_shaders->setChecked(Settings::values.use_assembly_shaders.GetValue()); +        ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue()); +        ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode.GetValue()); +        ui->anisotropic_filtering_combobox->setCurrentIndex( +            Settings::values.max_anisotropy.GetValue()); +    } else { +        ConfigurationShared::SetPerGameSetting(ui->gpu_accuracy, &Settings::values.gpu_accuracy); +        ConfigurationShared::SetPerGameSetting(ui->use_vsync, &Settings::values.use_vsync); +        ConfigurationShared::SetPerGameSetting(ui->use_assembly_shaders, +                                               &Settings::values.use_assembly_shaders); +        ConfigurationShared::SetPerGameSetting(ui->use_fast_gpu_time, +                                               &Settings::values.use_fast_gpu_time); +        ConfigurationShared::SetPerGameSetting(ui->force_30fps_mode, +                                               &Settings::values.force_30fps_mode); +        ConfigurationShared::SetPerGameSetting(ui->anisotropic_filtering_combobox, +                                               &Settings::values.max_anisotropy); +    }  }  void ConfigureGraphicsAdvanced::ApplyConfiguration() { -    auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(ui->gpu_accuracy->currentIndex()); -    Settings::values.gpu_accuracy = gpu_accuracy; -    Settings::values.use_vsync = ui->use_vsync->isChecked(); -    Settings::values.use_assembly_shaders = ui->use_assembly_shaders->isChecked(); -    Settings::values.use_fast_gpu_time = ui->use_fast_gpu_time->isChecked(); -    Settings::values.force_30fps_mode = ui->force_30fps_mode->isChecked(); -    Settings::values.max_anisotropy = ui->anisotropic_filtering_combobox->currentIndex(); +    // Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots) +    const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>( +        ui->gpu_accuracy->currentIndex() - +        ((Settings::configuring_global) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET)); + +    if (Settings::configuring_global) { +        // Must guard in case of a during-game configuration when set to be game-specific. +        if (Settings::values.gpu_accuracy.UsingGlobal()) { +            Settings::values.gpu_accuracy.SetValue(gpu_accuracy); +        } +        if (Settings::values.use_vsync.UsingGlobal()) { +            Settings::values.use_vsync.SetValue(ui->use_vsync->isChecked()); +        } +        if (Settings::values.use_assembly_shaders.UsingGlobal()) { +            Settings::values.use_assembly_shaders.SetValue(ui->use_assembly_shaders->isChecked()); +        } +        if (Settings::values.use_fast_gpu_time.UsingGlobal()) { +            Settings::values.use_fast_gpu_time.SetValue(ui->use_fast_gpu_time->isChecked()); +        } +        if (Settings::values.force_30fps_mode.UsingGlobal()) { +            Settings::values.force_30fps_mode.SetValue(ui->force_30fps_mode->isChecked()); +        } +        if (Settings::values.max_anisotropy.UsingGlobal()) { +            Settings::values.max_anisotropy.SetValue( +                ui->anisotropic_filtering_combobox->currentIndex()); +        } +    } else { +        ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, +                                                 ui->anisotropic_filtering_combobox); +        ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync, ui->use_vsync); +        ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_assembly_shaders, +                                                 ui->use_assembly_shaders); +        ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_fast_gpu_time, +                                                 ui->use_fast_gpu_time); +        ConfigurationShared::ApplyPerGameSetting(&Settings::values.force_30fps_mode, +                                                 ui->force_30fps_mode); +        ConfigurationShared::ApplyPerGameSetting(&Settings::values.max_anisotropy, +                                                 ui->anisotropic_filtering_combobox); + +        if (ui->gpu_accuracy->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { +            Settings::values.gpu_accuracy.SetGlobal(true); +        } else { +            Settings::values.gpu_accuracy.SetGlobal(false); +            Settings::values.gpu_accuracy.SetValue(gpu_accuracy); +        } +    }  }  void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) { @@ -52,3 +110,25 @@ void ConfigureGraphicsAdvanced::changeEvent(QEvent* event) {  void ConfigureGraphicsAdvanced::RetranslateUI() {      ui->retranslateUi(this);  } + +void ConfigureGraphicsAdvanced::SetupPerGameUI() { +    // Disable if not global (only happens during game) +    if (Settings::configuring_global) { +        ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal()); +        ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal()); +        ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal()); +        ui->use_fast_gpu_time->setEnabled(Settings::values.use_fast_gpu_time.UsingGlobal()); +        ui->force_30fps_mode->setEnabled(Settings::values.force_30fps_mode.UsingGlobal()); +        ui->anisotropic_filtering_combobox->setEnabled( +            Settings::values.max_anisotropy.UsingGlobal()); + +        return; +    } + +    ConfigurationShared::InsertGlobalItem(ui->gpu_accuracy); +    ui->use_vsync->setTristate(true); +    ui->use_assembly_shaders->setTristate(true); +    ui->use_fast_gpu_time->setTristate(true); +    ui->force_30fps_mode->setTristate(true); +    ConfigurationShared::InsertGlobalItem(ui->anisotropic_filtering_combobox); +} diff --git a/src/yuzu/configuration/configure_graphics_advanced.h b/src/yuzu/configuration/configure_graphics_advanced.h index bbc9d4355..c043588ff 100644 --- a/src/yuzu/configuration/configure_graphics_advanced.h +++ b/src/yuzu/configuration/configure_graphics_advanced.h @@ -26,5 +26,7 @@ private:      void SetConfiguration(); +    void SetupPerGameUI(); +      std::unique_ptr<Ui::ConfigureGraphicsAdvanced> ui;  }; diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp new file mode 100644 index 000000000..1e49f0787 --- /dev/null +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -0,0 +1,140 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <memory> +#include <utility> + +#include <QCheckBox> +#include <QHeaderView> +#include <QMenu> +#include <QStandardItemModel> +#include <QString> +#include <QTimer> +#include <QTreeView> + +#include "common/common_paths.h" +#include "common/file_util.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/patch_manager.h" +#include "core/file_sys/xts_archive.h" +#include "core/loader/loader.h" +#include "ui_configure_per_game.h" +#include "yuzu/configuration/config.h" +#include "yuzu/configuration/configure_input.h" +#include "yuzu/configuration/configure_per_game.h" +#include "yuzu/uisettings.h" +#include "yuzu/util/util.h" + +ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id) +    : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) { +    game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false); + +    Settings::configuring_global = false; + +    ui->setupUi(this); +    setFocusPolicy(Qt::ClickFocus); +    setWindowTitle(tr("Properties")); + +    ui->addonsTab->SetTitleId(title_id); + +    scene = new QGraphicsScene; +    ui->icon_view->setScene(scene); + +    LoadConfiguration(); +} + +ConfigurePerGame::~ConfigurePerGame() = default; + +void ConfigurePerGame::ApplyConfiguration() { +    ui->addonsTab->ApplyConfiguration(); +    ui->generalTab->ApplyConfiguration(); +    ui->systemTab->ApplyConfiguration(); +    ui->graphicsTab->ApplyConfiguration(); +    ui->graphicsAdvancedTab->ApplyConfiguration(); +    ui->audioTab->ApplyConfiguration(); + +    Settings::Apply(); +    Settings::LogSettings(); + +    game_config->Save(); +} + +void ConfigurePerGame::changeEvent(QEvent* event) { +    if (event->type() == QEvent::LanguageChange) { +        RetranslateUI(); +    } + +    QDialog::changeEvent(event); +} + +void ConfigurePerGame::RetranslateUI() { +    ui->retranslateUi(this); +} + +void ConfigurePerGame::LoadFromFile(FileSys::VirtualFile file) { +    this->file = std::move(file); +    LoadConfiguration(); +} + +void ConfigurePerGame::LoadConfiguration() { +    if (file == nullptr) { +        return; +    } + +    ui->addonsTab->LoadFromFile(file); + +    ui->display_title_id->setText( +        QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper()); + +    FileSys::PatchManager pm{title_id}; +    const auto control = pm.GetControlMetadata(); +    const auto loader = Loader::GetLoader(file); + +    if (control.first != nullptr) { +        ui->display_version->setText(QString::fromStdString(control.first->GetVersionString())); +        ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName())); +        ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName())); +    } else { +        std::string title; +        if (loader->ReadTitle(title) == Loader::ResultStatus::Success) +            ui->display_name->setText(QString::fromStdString(title)); + +        FileSys::NACP nacp; +        if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success) +            ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName())); + +        ui->display_version->setText(QStringLiteral("1.0.0")); +    } + +    if (control.second != nullptr) { +        scene->clear(); + +        QPixmap map; +        const auto bytes = control.second->ReadAllBytes(); +        map.loadFromData(bytes.data(), static_cast<u32>(bytes.size())); + +        scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(), +                                    Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); +    } else { +        std::vector<u8> bytes; +        if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) { +            scene->clear(); + +            QPixmap map; +            map.loadFromData(bytes.data(), static_cast<u32>(bytes.size())); + +            scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(), +                                        Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); +        } +    } + +    ui->display_filename->setText(QString::fromStdString(file->GetName())); + +    ui->display_format->setText( +        QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))); + +    const auto valueText = ReadableByteSize(file->GetSize()); +    ui->display_size->setText(valueText); +} diff --git a/src/yuzu/configuration/configure_per_game.h b/src/yuzu/configuration/configure_per_game.h new file mode 100644 index 000000000..5f9a08cef --- /dev/null +++ b/src/yuzu/configuration/configure_per_game.h @@ -0,0 +1,51 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <vector> + +#include <QDialog> +#include <QList> + +#include "core/file_sys/vfs_types.h" +#include "yuzu/configuration/config.h" + +class QGraphicsScene; +class QStandardItem; +class QStandardItemModel; +class QTreeView; +class QVBoxLayout; + +namespace Ui { +class ConfigurePerGame; +} + +class ConfigurePerGame : public QDialog { +    Q_OBJECT + +public: +    explicit ConfigurePerGame(QWidget* parent, u64 title_id); +    ~ConfigurePerGame() override; + +    /// Save all button configurations to settings file +    void ApplyConfiguration(); + +    void LoadFromFile(FileSys::VirtualFile file); + +private: +    void changeEvent(QEvent* event) override; +    void RetranslateUI(); + +    void LoadConfiguration(); + +    std::unique_ptr<Ui::ConfigurePerGame> ui; +    FileSys::VirtualFile file; +    u64 title_id; + +    QGraphicsScene* scene; + +    std::unique_ptr<Config> game_config; +}; diff --git a/src/yuzu/configuration/configure_per_game.ui b/src/yuzu/configuration/configure_per_game.ui new file mode 100644 index 000000000..d2057c4ab --- /dev/null +++ b/src/yuzu/configuration/configure_per_game.ui @@ -0,0 +1,350 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigurePerGame</class> + <widget class="QDialog" name="ConfigurePerGame"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>800</width> +    <height>600</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Dialog</string> +  </property> +  <layout class="QVBoxLayout" name="verticalLayout_3"> +   <item> +    <layout class="QHBoxLayout" name="horizontalLayout"> +     <item> +      <widget class="QGroupBox" name="groupBox"> +       <property name="sizePolicy"> +        <sizepolicy hsizetype="Maximum" vsizetype="Preferred"> +         <horstretch>0</horstretch> +         <verstretch>0</verstretch> +        </sizepolicy> +       </property> +       <property name="title"> +        <string>Info</string> +       </property> +       <layout class="QVBoxLayout" name="verticalLayout"> +        <item alignment="Qt::AlignHCenter"> +         <widget class="QGraphicsView" name="icon_view"> +          <property name="sizePolicy"> +           <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> +            <horstretch>0</horstretch> +            <verstretch>0</verstretch> +           </sizepolicy> +          </property> +          <property name="minimumSize"> +           <size> +            <width>256</width> +            <height>256</height> +           </size> +          </property> +          <property name="maximumSize"> +           <size> +            <width>256</width> +            <height>256</height> +           </size> +          </property> +          <property name="verticalScrollBarPolicy"> +           <enum>Qt::ScrollBarAlwaysOff</enum> +          </property> +          <property name="horizontalScrollBarPolicy"> +           <enum>Qt::ScrollBarAlwaysOff</enum> +          </property> +          <property name="interactive"> +           <bool>false</bool> +          </property> +         </widget> +        </item> +        <item> +         <layout class="QGridLayout" name="gridLayout_2"> +          <item row="6" column="1"> +           <widget class="QLineEdit" name="display_size"> +            <property name="enabled"> +             <bool>true</bool> +            </property> +            <property name="readOnly"> +             <bool>true</bool> +            </property> +           </widget> +          </item> +          <item row="3" column="1"> +           <widget class="QLineEdit" name="display_version"> +            <property name="enabled"> +             <bool>true</bool> +            </property> +            <property name="readOnly"> +             <bool>true</bool> +            </property> +           </widget> +          </item> +          <item row="1" column="0"> +           <widget class="QLabel" name="label"> +            <property name="text"> +             <string>Name</string> +            </property> +           </widget> +          </item> +          <item row="4" column="0"> +           <widget class="QLabel" name="label_4"> +            <property name="text"> +             <string>Title ID</string> +            </property> +           </widget> +          </item> +          <item row="4" column="1"> +           <widget class="QLineEdit" name="display_title_id"> +            <property name="enabled"> +             <bool>true</bool> +            </property> +            <property name="readOnly"> +             <bool>true</bool> +            </property> +           </widget> +          </item> +          <item row="7" column="1"> +           <widget class="QLineEdit" name="display_filename"> +            <property name="enabled"> +             <bool>true</bool> +            </property> +            <property name="readOnly"> +             <bool>true</bool> +            </property> +           </widget> +          </item> +          <item row="5" column="1"> +           <widget class="QLineEdit" name="display_format"> +            <property name="enabled"> +             <bool>true</bool> +            </property> +            <property name="readOnly"> +             <bool>true</bool> +            </property> +           </widget> +          </item> +          <item row="7" column="0"> +           <widget class="QLabel" name="label_7"> +            <property name="text"> +             <string>Filename</string> +            </property> +           </widget> +          </item> +          <item row="1" column="1"> +           <widget class="QLineEdit" name="display_name"> +            <property name="enabled"> +             <bool>true</bool> +            </property> +            <property name="readOnly"> +             <bool>true</bool> +            </property> +           </widget> +          </item> +          <item row="2" column="1"> +           <widget class="QLineEdit" name="display_developer"> +            <property name="enabled"> +             <bool>true</bool> +            </property> +            <property name="readOnly"> +             <bool>true</bool> +            </property> +           </widget> +          </item> +          <item row="5" column="0"> +           <widget class="QLabel" name="label_5"> +            <property name="text"> +             <string>Format</string> +            </property> +           </widget> +          </item> +          <item row="3" column="0"> +           <widget class="QLabel" name="label_3"> +            <property name="text"> +             <string>Version</string> +            </property> +           </widget> +          </item> +          <item row="6" column="0"> +           <widget class="QLabel" name="label_6"> +            <property name="text"> +             <string>Size</string> +            </property> +           </widget> +          </item> +          <item row="2" column="0"> +           <widget class="QLabel" name="label_2"> +            <property name="text"> +             <string>Developer</string> +            </property> +           </widget> +          </item> +         </layout> +        </item> +        <item> +         <spacer name="verticalSpacer"> +          <property name="orientation"> +           <enum>Qt::Vertical</enum> +          </property> +          <property name="sizeHint" stdset="0"> +           <size> +            <width>20</width> +            <height>40</height> +           </size> +          </property> +         </spacer> +        </item> +       </layout> +      </widget> +     </item> +     <item> +      <layout class="QVBoxLayout" name="VerticalLayout"> +       <item> +        <layout class="QVBoxLayout" name="verticalLayout_2"/> +       </item> +       <item> +        <widget class="QTabWidget" name="tabWidget"> +         <property name="enabled"> +          <bool>true</bool> +         </property> +         <property name="currentIndex"> +          <number>0</number> +         </property> +         <property name="usesScrollButtons"> +          <bool>true</bool> +         </property> +         <property name="documentMode"> +          <bool>false</bool> +         </property> +         <property name="tabsClosable"> +          <bool>false</bool> +         </property> +         <widget class="ConfigurePerGameAddons" name="addonsTab"> +          <attribute name="title"> +           <string>Add-Ons</string> +          </attribute> +         </widget> +         <widget class="ConfigureGeneral" name="generalTab"> +          <attribute name="title"> +           <string>General</string> +          </attribute> +         </widget> +         <widget class="ConfigureSystem" name="systemTab"> +          <attribute name="title"> +           <string>System</string> +          </attribute> +         </widget> +         <widget class="ConfigureGraphics" name="graphicsTab"> +          <attribute name="title"> +           <string>Graphics</string> +          </attribute> +         </widget> +         <widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab"> +          <attribute name="title"> +           <string>Adv. Graphics</string> +          </attribute> +         </widget> +         <widget class="ConfigureAudio" name="audioTab"> +          <attribute name="title"> +           <string>Audio</string> +          </attribute> +         </widget> +        </widget> +       </item> +      </layout> +     </item> +    </layout> +   </item> +   <item> +    <widget class="QDialogButtonBox" name="buttonBox"> +     <property name="sizePolicy"> +      <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> +       <horstretch>0</horstretch> +       <verstretch>0</verstretch> +      </sizepolicy> +     </property> +     <property name="orientation"> +      <enum>Qt::Horizontal</enum> +     </property> +     <property name="standardButtons"> +      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> +     </property> +    </widget> +   </item> +  </layout> + </widget> + <customwidgets> +  <customwidget> +   <class>ConfigureGeneral</class> +   <extends>QWidget</extends> +   <header>configuration/configure_general.h</header> +   <container>1</container> +  </customwidget> +  <customwidget> +   <class>ConfigureSystem</class> +   <extends>QWidget</extends> +   <header>configuration/configure_system.h</header> +   <container>1</container> +  </customwidget> +  <customwidget> +   <class>ConfigureAudio</class> +   <extends>QWidget</extends> +   <header>configuration/configure_audio.h</header> +   <container>1</container> +  </customwidget> +  <customwidget> +   <class>ConfigureGraphics</class> +   <extends>QWidget</extends> +   <header>configuration/configure_graphics.h</header> +   <container>1</container> +  </customwidget> +  <customwidget> +   <class>ConfigureGraphicsAdvanced</class> +   <extends>QWidget</extends> +   <header>configuration/configure_graphics_advanced.h</header> +   <container>1</container> +  </customwidget> +  <customwidget> +   <class>ConfigurePerGameAddons</class> +   <extends>QWidget</extends> +   <header>configuration/configure_per_game_addons.h</header> +   <container>1</container> +  </customwidget> + </customwidgets> + <resources/> + <connections> +  <connection> +   <sender>buttonBox</sender> +   <signal>accepted()</signal> +   <receiver>ConfigurePerGame</receiver> +   <slot>accept()</slot> +   <hints> +    <hint type="sourcelabel"> +     <x>248</x> +     <y>254</y> +    </hint> +    <hint type="destinationlabel"> +     <x>157</x> +     <y>274</y> +    </hint> +   </hints> +  </connection> +  <connection> +   <sender>buttonBox</sender> +   <signal>rejected()</signal> +   <receiver>ConfigurePerGame</receiver> +   <slot>reject()</slot> +   <hints> +    <hint type="sourcelabel"> +     <x>316</x> +     <y>260</y> +    </hint> +    <hint type="destinationlabel"> +     <x>286</x> +     <y>274</y> +    </hint> +   </hints> +  </connection> + </connections> +</ui> diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp index d7f259f12..478d5d3a1 100644 --- a/src/yuzu/configuration/configure_per_general.cpp +++ b/src/yuzu/configuration/configure_per_game_addons.cpp @@ -15,23 +15,20 @@  #include "common/common_paths.h"  #include "common/file_util.h" -#include "core/file_sys/control_metadata.h" +#include "core/core.h"  #include "core/file_sys/patch_manager.h"  #include "core/file_sys/xts_archive.h"  #include "core/loader/loader.h" -#include "ui_configure_per_general.h" +#include "ui_configure_per_game_addons.h"  #include "yuzu/configuration/config.h"  #include "yuzu/configuration/configure_input.h" -#include "yuzu/configuration/configure_per_general.h" +#include "yuzu/configuration/configure_per_game_addons.h"  #include "yuzu/uisettings.h"  #include "yuzu/util/util.h" -ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id) -    : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGameGeneral>()), title_id(title_id) { - +ConfigurePerGameAddons::ConfigurePerGameAddons(QWidget* parent) +    : QWidget(parent), ui(new Ui::ConfigurePerGameAddons) {      ui->setupUi(this); -    setFocusPolicy(Qt::ClickFocus); -    setWindowTitle(tr("Properties"));      layout = new QVBoxLayout;      tree_view = new QTreeView; @@ -52,7 +49,7 @@ ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id)      item_model->setHeaderData(1, Qt::Horizontal, tr("Version"));      // We must register all custom types with the Qt Automoc system so that we are able to use it -    // with signals/slots. In this case, QList falls under the umbrells of custom types. +    // with signals/slots. In this case, QList falls under the umbrella of custom types.      qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>");      layout->setContentsMargins(0, 0, 0, 0); @@ -61,18 +58,15 @@ ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id)      ui->scrollArea->setLayout(layout); -    scene = new QGraphicsScene; -    ui->icon_view->setScene(scene); +    ui->scrollArea->setEnabled(!Core::System::GetInstance().IsPoweredOn());      connect(item_model, &QStandardItemModel::itemChanged,              [] { UISettings::values.is_game_list_reload_pending.exchange(true); }); - -    LoadConfiguration();  } -ConfigurePerGameGeneral::~ConfigurePerGameGeneral() = default; +ConfigurePerGameAddons::~ConfigurePerGameAddons() = default; -void ConfigurePerGameGeneral::ApplyConfiguration() { +void ConfigurePerGameAddons::ApplyConfiguration() {      std::vector<std::string> disabled_addons;      for (const auto& item : list_items) { @@ -92,72 +86,35 @@ void ConfigurePerGameGeneral::ApplyConfiguration() {      Settings::values.disabled_addons[title_id] = disabled_addons;  } -void ConfigurePerGameGeneral::changeEvent(QEvent* event) { +void ConfigurePerGameAddons::LoadFromFile(FileSys::VirtualFile file) { +    this->file = std::move(file); +    LoadConfiguration(); +} + +void ConfigurePerGameAddons::SetTitleId(u64 id) { +    this->title_id = id; +} + +void ConfigurePerGameAddons::changeEvent(QEvent* event) {      if (event->type() == QEvent::LanguageChange) {          RetranslateUI();      } -    QDialog::changeEvent(event); +    QWidget::changeEvent(event);  } -void ConfigurePerGameGeneral::RetranslateUI() { +void ConfigurePerGameAddons::RetranslateUI() {      ui->retranslateUi(this);  } -void ConfigurePerGameGeneral::LoadFromFile(FileSys::VirtualFile file) { -    this->file = std::move(file); -    LoadConfiguration(); -} - -void ConfigurePerGameGeneral::LoadConfiguration() { +void ConfigurePerGameAddons::LoadConfiguration() {      if (file == nullptr) {          return;      } -    ui->display_title_id->setText(QString::fromStdString(fmt::format("{:016X}", title_id))); -      FileSys::PatchManager pm{title_id}; -    const auto control = pm.GetControlMetadata();      const auto loader = Loader::GetLoader(file); -    if (control.first != nullptr) { -        ui->display_version->setText(QString::fromStdString(control.first->GetVersionString())); -        ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName())); -        ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName())); -    } else { -        std::string title; -        if (loader->ReadTitle(title) == Loader::ResultStatus::Success) -            ui->display_name->setText(QString::fromStdString(title)); - -        FileSys::NACP nacp; -        if (loader->ReadControlData(nacp) == Loader::ResultStatus::Success) -            ui->display_developer->setText(QString::fromStdString(nacp.GetDeveloperName())); - -        ui->display_version->setText(QStringLiteral("1.0.0")); -    } - -    if (control.second != nullptr) { -        scene->clear(); - -        QPixmap map; -        const auto bytes = control.second->ReadAllBytes(); -        map.loadFromData(bytes.data(), static_cast<u32>(bytes.size())); - -        scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(), -                                    Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); -    } else { -        std::vector<u8> bytes; -        if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) { -            scene->clear(); - -            QPixmap map; -            map.loadFromData(bytes.data(), static_cast<u32>(bytes.size())); - -            scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(), -                                        Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); -        } -    } -      FileSys::VirtualFile update_raw;      loader->ReadUpdateRaw(update_raw); @@ -182,12 +139,4 @@ void ConfigurePerGameGeneral::LoadConfiguration() {      }      tree_view->setColumnWidth(0, 5 * tree_view->width() / 16); - -    ui->display_filename->setText(QString::fromStdString(file->GetName())); - -    ui->display_format->setText( -        QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))); - -    const auto valueText = ReadableByteSize(file->GetSize()); -    ui->display_size->setText(valueText);  } diff --git a/src/yuzu/configuration/configure_per_general.h b/src/yuzu/configuration/configure_per_game_addons.h index a3b2cdeff..a00ec3539 100644 --- a/src/yuzu/configuration/configure_per_general.h +++ b/src/yuzu/configuration/configure_per_game_addons.h @@ -1,4 +1,4 @@ -// Copyright 2016 Citra Emulator Project +// Copyright 2016 Citra Emulator Project  // Licensed under GPLv2 or any later version  // Refer to the license.txt file included. @@ -7,7 +7,6 @@  #include <memory>  #include <vector> -#include <QDialog>  #include <QList>  #include "core/file_sys/vfs_types.h" @@ -19,35 +18,36 @@ class QTreeView;  class QVBoxLayout;  namespace Ui { -class ConfigurePerGameGeneral; +class ConfigurePerGameAddons;  } -class ConfigurePerGameGeneral : public QDialog { +class ConfigurePerGameAddons : public QWidget {      Q_OBJECT  public: -    explicit ConfigurePerGameGeneral(QWidget* parent, u64 title_id); -    ~ConfigurePerGameGeneral() override; +    explicit ConfigurePerGameAddons(QWidget* parent = nullptr); +    ~ConfigurePerGameAddons() override;      /// Save all button configurations to settings file      void ApplyConfiguration();      void LoadFromFile(FileSys::VirtualFile file); +    void SetTitleId(u64 id); +  private:      void changeEvent(QEvent* event) override;      void RetranslateUI();      void LoadConfiguration(); -    std::unique_ptr<Ui::ConfigurePerGameGeneral> ui; +    std::unique_ptr<Ui::ConfigurePerGameAddons> ui;      FileSys::VirtualFile file;      u64 title_id;      QVBoxLayout* layout;      QTreeView* tree_view;      QStandardItemModel* item_model; -    QGraphicsScene* scene;      std::vector<QList<QStandardItem*>> list_items;  }; diff --git a/src/yuzu/configuration/configure_per_game_addons.ui b/src/yuzu/configuration/configure_per_game_addons.ui new file mode 100644 index 000000000..aefdebfcd --- /dev/null +++ b/src/yuzu/configuration/configure_per_game_addons.ui @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ConfigurePerGameAddons</class> + <widget class="QWidget" name="ConfigurePerGameAddons"> +  <property name="geometry"> +   <rect> +    <x>0</x> +    <y>0</y> +    <width>400</width> +    <height>300</height> +   </rect> +  </property> +  <property name="windowTitle"> +   <string>Form</string> +  </property> +  <layout class="QGridLayout" name="gridLayout"> +   <item row="0" column="0"> +    <widget class="QScrollArea" name="scrollArea"> +     <property name="widgetResizable"> +      <bool>true</bool> +     </property> +     <widget class="QWidget" name="scrollAreaWidgetContents"> +      <property name="geometry"> +       <rect> +        <x>0</x> +        <y>0</y> +        <width>380</width> +        <height>280</height> +       </rect> +      </property> +     </widget> +    </widget> +   </item> +  </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/yuzu/configuration/configure_per_general.ui b/src/yuzu/configuration/configure_per_general.ui deleted file mode 100644 index 8fdd96fa4..000000000 --- a/src/yuzu/configuration/configure_per_general.ui +++ /dev/null @@ -1,276 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>ConfigurePerGameGeneral</class> - <widget class="QDialog" name="ConfigurePerGameGeneral"> -  <property name="geometry"> -   <rect> -    <x>0</x> -    <y>0</y> -    <width>400</width> -    <height>520</height> -   </rect> -  </property> -  <property name="windowTitle"> -   <string>ConfigurePerGameGeneral</string> -  </property> -  <layout class="QHBoxLayout" name="HorizontalLayout"> -   <item> -    <layout class="QVBoxLayout" name="VerticalLayout"> -     <item> -      <widget class="QGroupBox" name="GeneralGroupBox"> -       <property name="title"> -        <string>Info</string> -       </property> -       <layout class="QHBoxLayout" name="GeneralHorizontalLayout"> -        <item> -         <layout class="QGridLayout" name="gridLayout_2"> -          <item row="6" column="1" colspan="2"> -           <widget class="QLineEdit" name="display_filename"> -            <property name="enabled"> -             <bool>true</bool> -            </property> -            <property name="readOnly"> -             <bool>true</bool> -            </property> -           </widget> -          </item> -          <item row="0" column="1"> -           <widget class="QLineEdit" name="display_name"> -            <property name="enabled"> -             <bool>true</bool> -            </property> -            <property name="readOnly"> -             <bool>true</bool> -            </property> -           </widget> -          </item> -          <item row="1" column="0"> -           <widget class="QLabel" name="label_2"> -            <property name="text"> -             <string>Developer</string> -            </property> -           </widget> -          </item> -          <item row="5" column="1" colspan="2"> -           <widget class="QLineEdit" name="display_size"> -            <property name="enabled"> -             <bool>true</bool> -            </property> -            <property name="readOnly"> -             <bool>true</bool> -            </property> -           </widget> -          </item> -          <item row="0" column="0"> -           <widget class="QLabel" name="label"> -            <property name="text"> -             <string>Name</string> -            </property> -           </widget> -          </item> -          <item row="6" column="0"> -           <widget class="QLabel" name="label_7"> -            <property name="text"> -             <string>Filename</string> -            </property> -           </widget> -          </item> -          <item row="2" column="0"> -           <widget class="QLabel" name="label_3"> -            <property name="text"> -             <string>Version</string> -            </property> -           </widget> -          </item> -          <item row="4" column="0"> -           <widget class="QLabel" name="label_5"> -            <property name="text"> -             <string>Format</string> -            </property> -           </widget> -          </item> -          <item row="2" column="1"> -           <widget class="QLineEdit" name="display_version"> -            <property name="enabled"> -             <bool>true</bool> -            </property> -            <property name="readOnly"> -             <bool>true</bool> -            </property> -           </widget> -          </item> -          <item row="4" column="1"> -           <widget class="QLineEdit" name="display_format"> -            <property name="enabled"> -             <bool>true</bool> -            </property> -            <property name="readOnly"> -             <bool>true</bool> -            </property> -           </widget> -          </item> -          <item row="5" column="0"> -           <widget class="QLabel" name="label_6"> -            <property name="text"> -             <string>Size</string> -            </property> -           </widget> -          </item> -          <item row="1" column="1"> -           <widget class="QLineEdit" name="display_developer"> -            <property name="enabled"> -             <bool>true</bool> -            </property> -            <property name="readOnly"> -             <bool>true</bool> -            </property> -           </widget> -          </item> -          <item row="3" column="0"> -           <widget class="QLabel" name="label_4"> -            <property name="text"> -             <string>Title ID</string> -            </property> -           </widget> -          </item> -          <item row="3" column="1"> -           <widget class="QLineEdit" name="display_title_id"> -            <property name="enabled"> -             <bool>true</bool> -            </property> -            <property name="readOnly"> -             <bool>true</bool> -            </property> -           </widget> -          </item> -          <item row="0" column="2" rowspan="5"> -           <widget class="QGraphicsView" name="icon_view"> -            <property name="sizePolicy"> -             <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> -              <horstretch>0</horstretch> -              <verstretch>0</verstretch> -             </sizepolicy> -            </property> -            <property name="minimumSize"> -             <size> -              <width>128</width> -              <height>128</height> -             </size> -            </property> -            <property name="maximumSize"> -             <size> -              <width>128</width> -              <height>128</height> -             </size> -            </property> -            <property name="verticalScrollBarPolicy"> -             <enum>Qt::ScrollBarAlwaysOff</enum> -            </property> -            <property name="horizontalScrollBarPolicy"> -             <enum>Qt::ScrollBarAlwaysOff</enum> -            </property> -            <property name="sizeAdjustPolicy"> -             <enum>QAbstractScrollArea::AdjustToContents</enum> -            </property> -            <property name="interactive"> -             <bool>false</bool> -            </property> -           </widget> -          </item> -         </layout> -        </item> -       </layout> -      </widget> -     </item> -     <item> -      <widget class="QGroupBox" name="PerformanceGroupBox"> -       <property name="title"> -        <string>Add-Ons</string> -       </property> -       <layout class="QHBoxLayout" name="PerformanceHorizontalLayout"> -        <item> -         <widget class="QScrollArea" name="scrollArea"> -          <property name="widgetResizable"> -           <bool>true</bool> -          </property> -          <widget class="QWidget" name="scrollAreaWidgetContents"> -           <property name="geometry"> -            <rect> -             <x>0</x> -             <y>0</y> -             <width>350</width> -             <height>169</height> -            </rect> -           </property> -          </widget> -         </widget> -        </item> -        <item> -         <layout class="QVBoxLayout" name="PerformanceVerticalLayout"/> -        </item> -       </layout> -      </widget> -     </item> -     <item> -      <spacer name="verticalSpacer"> -       <property name="orientation"> -        <enum>Qt::Vertical</enum> -       </property> -       <property name="sizeType"> -        <enum>QSizePolicy::Fixed</enum> -       </property> -       <property name="sizeHint" stdset="0"> -        <size> -         <width>20</width> -         <height>40</height> -        </size> -       </property> -      </spacer> -     </item> -     <item> -      <widget class="QDialogButtonBox" name="buttonBox"> -       <property name="standardButtons"> -        <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> -       </property> -      </widget> -     </item> -    </layout> -   </item> -  </layout> - </widget> - <resources/> - <connections> -  <connection> -   <sender>buttonBox</sender> -   <signal>accepted()</signal> -   <receiver>ConfigurePerGameGeneral</receiver> -   <slot>accept()</slot> -   <hints> -    <hint type="sourcelabel"> -     <x>269</x> -     <y>567</y> -    </hint> -    <hint type="destinationlabel"> -     <x>269</x> -     <y>294</y> -    </hint> -   </hints> -  </connection> -  <connection> -   <sender>buttonBox</sender> -   <signal>rejected()</signal> -   <receiver>ConfigurePerGameGeneral</receiver> -   <slot>reject()</slot> -   <hints> -    <hint type="sourcelabel"> -     <x>269</x> -     <y>567</y> -    </hint> -    <hint type="destinationlabel"> -     <x>269</x> -     <y>294</y> -    </hint> -   </hints> -  </connection> - </connections> -</ui> diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 10315e7a6..68e02738b 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -14,6 +14,7 @@  #include "core/core.h"  #include "core/settings.h"  #include "ui_configure_system.h" +#include "yuzu/configuration/configuration_shared.h"  #include "yuzu/configuration/configure_system.h"  ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) { @@ -21,20 +22,25 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::      connect(ui->button_regenerate_console_id, &QPushButton::clicked, this,              &ConfigureSystem::RefreshConsoleID); -    connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) { -        ui->rng_seed_edit->setEnabled(checked); -        if (!checked) { +    connect(ui->rng_seed_checkbox, &QCheckBox::stateChanged, this, [this](int state) { +        ui->rng_seed_edit->setEnabled(state == Qt::Checked); +        if (state != Qt::Checked) {              ui->rng_seed_edit->setText(QStringLiteral("00000000"));          }      }); -    connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](bool checked) { -        ui->custom_rtc_edit->setEnabled(checked); -        if (!checked) { +    connect(ui->custom_rtc_checkbox, &QCheckBox::stateChanged, this, [this](int state) { +        ui->custom_rtc_edit->setEnabled(state == Qt::Checked); +        if (state != Qt::Checked) {              ui->custom_rtc_edit->setDateTime(QDateTime::currentDateTime());          }      }); +    ui->label_console_id->setVisible(Settings::configuring_global); +    ui->button_regenerate_console_id->setVisible(Settings::configuring_global); + +    SetupPerGameUI(); +      SetConfiguration();  } @@ -54,26 +60,58 @@ void ConfigureSystem::RetranslateUI() {  void ConfigureSystem::SetConfiguration() {      enabled = !Core::System::GetInstance().IsPoweredOn(); +    const auto rng_seed = +        QStringLiteral("%1") +            .arg(Settings::values.rng_seed.GetValue().value_or(0), 8, 16, QLatin1Char{'0'}) +            .toUpper(); +    const auto rtc_time = Settings::values.custom_rtc.GetValue().value_or( +        std::chrono::seconds(QDateTime::currentSecsSinceEpoch())); -    ui->combo_language->setCurrentIndex(Settings::values.language_index); -    ui->combo_region->setCurrentIndex(Settings::values.region_index); -    ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index); -    ui->combo_sound->setCurrentIndex(Settings::values.sound_index); - -    ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.has_value()); -    ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.has_value()); - -    const auto rng_seed = QStringLiteral("%1") -                              .arg(Settings::values.rng_seed.value_or(0), 8, 16, QLatin1Char{'0'}) -                              .toUpper(); -    ui->rng_seed_edit->setText(rng_seed); - -    ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.has_value()); -    ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.has_value()); +    if (Settings::configuring_global) { +        ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue()); +        ui->combo_region->setCurrentIndex(Settings::values.region_index.GetValue()); +        ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index.GetValue()); +        ui->combo_sound->setCurrentIndex(Settings::values.sound_index.GetValue()); + +        ui->rng_seed_checkbox->setChecked(Settings::values.rng_seed.GetValue().has_value()); +        ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value() && +                                      Settings::values.rng_seed.UsingGlobal()); +        ui->rng_seed_edit->setText(rng_seed); + +        ui->custom_rtc_checkbox->setChecked(Settings::values.custom_rtc.GetValue().has_value()); +        ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.GetValue().has_value() && +                                        Settings::values.rng_seed.UsingGlobal()); +        ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count())); +    } else { +        ConfigurationShared::SetPerGameSetting(ui->combo_language, +                                               &Settings::values.language_index); +        ConfigurationShared::SetPerGameSetting(ui->combo_region, &Settings::values.region_index); +        ConfigurationShared::SetPerGameSetting(ui->combo_time_zone, +                                               &Settings::values.time_zone_index); +        ConfigurationShared::SetPerGameSetting(ui->combo_sound, &Settings::values.sound_index); + +        if (Settings::values.rng_seed.UsingGlobal()) { +            ui->rng_seed_checkbox->setCheckState(Qt::PartiallyChecked); +        } else { +            ui->rng_seed_checkbox->setCheckState( +                Settings::values.rng_seed.GetValue().has_value() ? Qt::Checked : Qt::Unchecked); +            ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.GetValue().has_value()); +            if (Settings::values.rng_seed.GetValue().has_value()) { +                ui->rng_seed_edit->setText(rng_seed); +            } +        } -    const auto rtc_time = Settings::values.custom_rtc.value_or( -        std::chrono::seconds(QDateTime::currentSecsSinceEpoch())); -    ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count())); +        if (Settings::values.custom_rtc.UsingGlobal()) { +            ui->custom_rtc_checkbox->setCheckState(Qt::PartiallyChecked); +        } else { +            ui->custom_rtc_checkbox->setCheckState( +                Settings::values.custom_rtc.GetValue().has_value() ? Qt::Checked : Qt::Unchecked); +            ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.GetValue().has_value()); +            if (Settings::values.custom_rtc.GetValue().has_value()) { +                ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count())); +            } +        } +    }  }  void ConfigureSystem::ReadSystemSettings() {} @@ -83,22 +121,78 @@ void ConfigureSystem::ApplyConfiguration() {          return;      } -    Settings::values.language_index = ui->combo_language->currentIndex(); -    Settings::values.region_index = ui->combo_region->currentIndex(); -    Settings::values.time_zone_index = ui->combo_time_zone->currentIndex(); -    Settings::values.sound_index = ui->combo_sound->currentIndex(); +    if (Settings::configuring_global) { +        // Guard if during game and set to game-specific value +        if (Settings::values.language_index.UsingGlobal()) { +            Settings::values.language_index.SetValue(ui->combo_language->currentIndex()); +        } +        if (Settings::values.region_index.UsingGlobal()) { +            Settings::values.region_index.SetValue(ui->combo_region->currentIndex()); +        } +        if (Settings::values.time_zone_index.UsingGlobal()) { +            Settings::values.time_zone_index.SetValue(ui->combo_time_zone->currentIndex()); +        } +        if (Settings::values.sound_index.UsingGlobal()) { +            Settings::values.sound_index.SetValue(ui->combo_sound->currentIndex()); +        } + +        if (Settings::values.rng_seed.UsingGlobal()) { +            if (ui->rng_seed_checkbox->isChecked()) { +                Settings::values.rng_seed.SetValue( +                    ui->rng_seed_edit->text().toULongLong(nullptr, 16)); +            } else { +                Settings::values.rng_seed.SetValue(std::nullopt); +            } +        } -    if (ui->rng_seed_checkbox->isChecked()) { -        Settings::values.rng_seed = ui->rng_seed_edit->text().toULongLong(nullptr, 16); +        if (Settings::values.custom_rtc.UsingGlobal()) { +            if (ui->custom_rtc_checkbox->isChecked()) { +                Settings::values.custom_rtc.SetValue( +                    std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch())); +            } else { +                Settings::values.custom_rtc.SetValue(std::nullopt); +            } +        }      } else { -        Settings::values.rng_seed = std::nullopt; -    } +        ConfigurationShared::ApplyPerGameSetting(&Settings::values.language_index, +                                                 ui->combo_language); +        ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_index, ui->combo_region); +        ConfigurationShared::ApplyPerGameSetting(&Settings::values.time_zone_index, +                                                 ui->combo_time_zone); +        ConfigurationShared::ApplyPerGameSetting(&Settings::values.sound_index, ui->combo_sound); + +        switch (ui->rng_seed_checkbox->checkState()) { +        case Qt::Checked: +            Settings::values.rng_seed.SetGlobal(false); +            Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toULongLong(nullptr, 16)); +            break; +        case Qt::Unchecked: +            Settings::values.rng_seed.SetGlobal(false); +            Settings::values.rng_seed.SetValue(std::nullopt); +            break; +        case Qt::PartiallyChecked: +            Settings::values.rng_seed.SetGlobal(false); +            Settings::values.rng_seed.SetValue(std::nullopt); +            Settings::values.rng_seed.SetGlobal(true); +            break; +        } -    if (ui->custom_rtc_checkbox->isChecked()) { -        Settings::values.custom_rtc = -            std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()); -    } else { -        Settings::values.custom_rtc = std::nullopt; +        switch (ui->custom_rtc_checkbox->checkState()) { +        case Qt::Checked: +            Settings::values.custom_rtc.SetGlobal(false); +            Settings::values.custom_rtc.SetValue( +                std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch())); +            break; +        case Qt::Unchecked: +            Settings::values.custom_rtc.SetGlobal(false); +            Settings::values.custom_rtc.SetValue(std::nullopt); +            break; +        case Qt::PartiallyChecked: +            Settings::values.custom_rtc.SetGlobal(false); +            Settings::values.custom_rtc.SetValue(std::nullopt); +            Settings::values.custom_rtc.SetGlobal(true); +            break; +        }      }      Settings::Apply(); @@ -120,3 +214,25 @@ void ConfigureSystem::RefreshConsoleID() {      ui->label_console_id->setText(          tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper()));  } + +void ConfigureSystem::SetupPerGameUI() { +    if (Settings::configuring_global) { +        ui->combo_language->setEnabled(Settings::values.language_index.UsingGlobal()); +        ui->combo_region->setEnabled(Settings::values.region_index.UsingGlobal()); +        ui->combo_time_zone->setEnabled(Settings::values.time_zone_index.UsingGlobal()); +        ui->combo_sound->setEnabled(Settings::values.sound_index.UsingGlobal()); +        ui->rng_seed_checkbox->setEnabled(Settings::values.rng_seed.UsingGlobal()); +        ui->rng_seed_edit->setEnabled(Settings::values.rng_seed.UsingGlobal()); +        ui->custom_rtc_checkbox->setEnabled(Settings::values.custom_rtc.UsingGlobal()); +        ui->custom_rtc_edit->setEnabled(Settings::values.custom_rtc.UsingGlobal()); + +        return; +    } + +    ConfigurationShared::InsertGlobalItem(ui->combo_language); +    ConfigurationShared::InsertGlobalItem(ui->combo_region); +    ConfigurationShared::InsertGlobalItem(ui->combo_time_zone); +    ConfigurationShared::InsertGlobalItem(ui->combo_sound); +    ui->rng_seed_checkbox->setTristate(true); +    ui->custom_rtc_checkbox->setTristate(true); +} diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h index 26d42d5c5..f317ef8b5 100644 --- a/src/yuzu/configuration/configure_system.h +++ b/src/yuzu/configuration/configure_system.h @@ -32,6 +32,8 @@ private:      void RefreshConsoleID(); +    void SetupPerGameUI(); +      std::unique_ptr<Ui::ConfigureSystem> ui;      bool enabled = false; | 
