From 48ee112cebfebf74ecb0013bd2ac557d808fa48f Mon Sep 17 00:00:00 2001 From: GPUCode <47210458+GPUCode@users.noreply.github.com> Date: Thu, 8 Dec 2022 13:27:25 +0200 Subject: [PATCH] Add per game configuration options (#6187) * common: Move settings to common from core. - Removes a dependency on core and input_common from common. * code: Wrap settings values * Port from yuzu to allow per game settings * citra_qt: Initial per-game settings dialog * citra_qt: Use new API for read/save of config values * citra_qt: Per game audio settings * citra_qt: Per game graphics settings * citra_qt: Per game system settings * citra_qt: Per game general settings * citra_qt: Document and run clang format * citra_qt: Make icon smaller and centered * citra_qt: Remove version number * Not sure how to extract that, can always add it back later * citra_qt: Wrap UISettings * citra_qt: Fix unthottled fps setting * citra_qt: Remove margin in emulation tab * citra_qt: Implement some suggestions * Bring back speed switch hotkey * Allow configuration when game is running * Rename/adjust UI stuff * citra_qt: Fix build with separate windows * citra_qt: Address feedback * citra_qt: Log per-game settings before launching games * citra_qt: Add shader cache options * Also fix android build * citra_qt: Add DLC menu option * citra_qt: Run clang-format * citra_qt: Adjust for time offset * citra_qt: Implement suggestions * Run clang-format Co-authored-by: bunnei --- .../utils/EmulationMenuSettings.java | 2 +- src/android/app/src/main/jni/config.cpp | 12 +- .../src/main/jni/emu_window/emu_window.cpp | 2 +- .../app/src/main/jni/game_settings.cpp | 2 +- src/android/app/src/main/jni/id_cache.cpp | 4 +- src/android/app/src/main/jni/native.cpp | 4 +- src/audio_core/dsp_interface.cpp | 7 +- src/audio_core/dsp_interface.h | 2 +- src/citra/citra.cpp | 10 +- src/citra/config.cpp | 18 +- src/citra/emu_window/emu_window_sdl2.cpp | 2 +- src/citra_qt/CMakeLists.txt | 5 + src/citra_qt/bootmanager.cpp | 2 +- src/citra_qt/configuration/config.cpp | 812 ++++++++++-------- src/citra_qt/configuration/config.h | 76 +- .../configuration/configuration_shared.cpp | 95 ++ .../configuration/configuration_shared.h | 98 +++ .../configuration/configure_audio.cpp | 126 ++- src/citra_qt/configuration/configure_audio.h | 7 + src/citra_qt/configuration/configure_audio.ui | 220 +++-- .../configuration/configure_camera.cpp | 2 +- .../configuration/configure_debug.cpp | 18 +- .../configuration/configure_dialog.cpp | 4 +- .../configuration/configure_enhancements.cpp | 39 +- .../configuration/configure_enhancements.h | 3 +- .../configuration/configure_general.cpp | 162 ++-- .../configuration/configure_general.h | 2 + .../configuration/configure_general.ui | 346 ++++---- .../configuration/configure_graphics.cpp | 83 +- .../configuration/configure_graphics.h | 12 + .../configuration/configure_hotkeys.cpp | 2 +- src/citra_qt/configuration/configure_input.h | 2 +- .../configuration/configure_motion_touch.h | 2 +- .../configuration/configure_per_game.cpp | 128 +++ .../configuration/configure_per_game.h | 66 ++ .../configuration/configure_per_game.ui | 264 ++++++ .../configuration/configure_storage.cpp | 8 +- .../configuration/configure_system.cpp | 120 ++- src/citra_qt/configuration/configure_system.h | 7 + .../configuration/configure_system.ui | 114 ++- .../configure_touch_from_button.h | 2 +- src/citra_qt/configuration/configure_ui.cpp | 14 +- src/citra_qt/configuration/configure_web.cpp | 2 +- src/citra_qt/debugger/console.cpp | 4 +- src/citra_qt/debugger/lle_service_modules.cpp | 2 +- src/citra_qt/debugger/wait_tree.cpp | 2 +- src/citra_qt/dumping/dumping_dialog.cpp | 2 +- src/citra_qt/game_list.cpp | 66 +- src/citra_qt/game_list.h | 3 + src/citra_qt/game_list_p.h | 24 +- src/citra_qt/main.cpp | 241 ++++-- src/citra_qt/main.h | 3 + src/citra_qt/main.ui | 35 +- src/citra_qt/uisettings.h | 51 +- src/common/CMakeLists.txt | 2 + src/{core => common}/settings.cpp | 156 ++-- src/common/settings.h | 538 ++++++++++++ src/core/CMakeLists.txt | 2 - src/core/arm/exclusive_monitor.cpp | 2 +- src/core/cheats/gateway_cheat.h | 2 + src/core/core.cpp | 18 +- src/core/dumping/ffmpeg_backend.cpp | 2 +- src/core/file_sys/archive_sdmc.cpp | 2 +- src/core/file_sys/archive_sdmcwriteonly.cpp | 2 +- src/core/frontend/emu_window.cpp | 61 +- src/core/frontend/framebuffer_layout.cpp | 82 +- src/core/frontend/framebuffer_layout.h | 2 +- src/core/hle/kernel/memory.cpp | 4 +- src/core/hle/kernel/shared_page.cpp | 12 +- src/core/hle/service/apt/apt.cpp | 2 +- src/core/hle/service/cam/cam.cpp | 2 +- src/core/hle/service/cfg/cfg.cpp | 8 +- src/core/hle/service/fs/fs_user.cpp | 6 +- src/core/hle/service/hid/hid.cpp | 7 +- src/core/hle/service/hid/hid.h | 2 +- src/core/hle/service/ir/extra_hid.cpp | 2 +- src/core/hle/service/ir/ir_rst.cpp | 2 +- src/core/hle/service/mic_u.cpp | 6 +- src/core/hle/service/ptm/ptm.cpp | 4 +- src/core/memory.cpp | 4 +- src/core/movie.cpp | 6 +- src/core/perf_stats.cpp | 11 +- src/core/settings.h | 268 ------ src/core/telemetry_session.cpp | 44 +- src/input_common/gcadapter/gc_poller.h | 2 +- src/input_common/sdl/sdl_impl.h | 2 +- src/input_common/touch_from_button.cpp | 2 +- src/input_common/udp/udp.cpp | 2 +- .../rasterizer_cache/rasterizer_cache.cpp | 6 +- .../renderer_opengl/gl_shader_disk_cache.cpp | 2 +- .../renderer_opengl/renderer_opengl.cpp | 91 +- src/video_core/video_core.cpp | 8 +- 92 files changed, 3171 insertions(+), 1546 deletions(-) create mode 100644 src/citra_qt/configuration/configuration_shared.cpp create mode 100644 src/citra_qt/configuration/configuration_shared.h create mode 100644 src/citra_qt/configuration/configure_per_game.cpp create mode 100644 src/citra_qt/configuration/configure_per_game.h create mode 100644 src/citra_qt/configuration/configure_per_game.ui rename src/{core => common}/settings.cpp (54%) create mode 100644 src/common/settings.h delete mode 100644 src/core/settings.h diff --git a/src/android/app/src/main/java/org/citra/citra_emu/utils/EmulationMenuSettings.java b/src/android/app/src/main/java/org/citra/citra_emu/utils/EmulationMenuSettings.java index 9664f8464..a24749083 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/utils/EmulationMenuSettings.java +++ b/src/android/app/src/main/java/org/citra/citra_emu/utils/EmulationMenuSettings.java @@ -8,7 +8,7 @@ import org.citra.citra_emu.CitraApplication; public class EmulationMenuSettings { private static SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(CitraApplication.getAppContext()); - // These must match what is defined in src/core/settings.h + // These must match what is defined in src/common/settings.h public static final int LayoutOption_Default = 0; public static final int LayoutOption_SingleScreen = 1; public static final int LayoutOption_LargeScreen = 2; diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp index 746c26e09..e511b438e 100644 --- a/src/android/app/src/main/jni/config.cpp +++ b/src/android/app/src/main/jni/config.cpp @@ -11,10 +11,10 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/param_package.h" +#include "common/settings.h" #include "core/core.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/service.h" -#include "core/settings.h" #include "input_common/main.h" #include "input_common/udp/client.h" #include "jni/camera/ndk_camera.h" @@ -139,9 +139,9 @@ void Config::ReadValues() { Settings::values.factor_3d = static_cast(sdl2_config->GetInteger("Renderer", "factor_3d", 0)); std::string default_shader = "none (builtin)"; - if (Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph) + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph) default_shader = "dubois (builtin)"; - else if (Settings::values.render_3d == Settings::StereoRenderOption::Interlaced) + else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Interlaced) default_shader = "horizontal (builtin)"; Settings::values.pp_shader_name = sdl2_config->GetString("Renderer", "pp_shader_name", default_shader); @@ -186,9 +186,9 @@ void Config::ReadValues() { sdl2_config->GetBoolean("Utility", "preload_textures", false); // Audio - Settings::values.enable_dsp_lle = sdl2_config->GetBoolean("Audio", "enable_dsp_lle", false); - Settings::values.enable_dsp_lle_multithread = - sdl2_config->GetBoolean("Audio", "enable_dsp_lle_multithread", false); + Settings::values.audio_emulation = + static_cast(sdl2_config->GetInteger( + "Audio", "audio_emulation", static_cast(Settings::AudioEmulation::HLE))); Settings::values.sink_id = sdl2_config->GetString("Audio", "output_engine", "auto"); Settings::values.enable_audio_stretching = sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp index 86faef554..c2cc91ef5 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.cpp +++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp @@ -11,7 +11,7 @@ #include #include "common/logging/log.h" -#include "core/settings.h" +#include "common/settings.h" #include "input_common/main.h" #include "jni/emu_window/emu_window.h" #include "jni/id_cache.h" diff --git a/src/android/app/src/main/jni/game_settings.cpp b/src/android/app/src/main/jni/game_settings.cpp index 3feb42704..d8382b0e1 100644 --- a/src/android/app/src/main/jni/game_settings.cpp +++ b/src/android/app/src/main/jni/game_settings.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/settings.h" +#include "common/settings.h" namespace GameSettings { diff --git a/src/android/app/src/main/jni/id_cache.cpp b/src/android/app/src/main/jni/id_cache.cpp index 60c091912..b1b372923 100644 --- a/src/android/app/src/main/jni/id_cache.cpp +++ b/src/android/app/src/main/jni/id_cache.cpp @@ -6,7 +6,7 @@ #include "common/logging/backend.h" #include "common/logging/filter.h" #include "common/logging/log.h" -#include "core/settings.h" +#include "common/settings.h" #include "jni/applets/mii_selector.h" #include "jni/applets/swkbd.h" #include "jni/camera/still_image_camera.h" @@ -156,7 +156,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) { // Initialize Logger Log::Filter log_filter; - log_filter.ParseFilterString(Settings::values.log_filter); + log_filter.ParseFilterString(Settings::values.log_filter.GetValue()); Log::SetGlobalFilter(log_filter); Log::AddBackend(std::make_unique()); FileUtil::CreateFullPath(FileUtil::GetUserPath(FileUtil::UserPath::LogDir)); diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 04c38305e..78907ea4a 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -17,6 +17,7 @@ #include "common/microprofile.h" #include "common/scm_rev.h" #include "common/scope_exit.h" +#include "common/settings.h" #include "common/string_util.h" #include "core/core.h" #include "core/frontend/applets/default_applets.h" @@ -26,7 +27,6 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/nfc/nfc.h" #include "core/savestate.h" -#include "core/settings.h" #include "jni/android_common/android_common.h" #include "jni/applets/mii_selector.h" #include "jni/applets/swkbd.h" @@ -238,7 +238,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) { } } else { // Ensure no audio bleeds out while game is paused - const float volume = Settings::values.volume; + const float volume = Settings::values.volume.GetValue(); SCOPE_EXIT({ Settings::values.volume = volume; }); Settings::values.volume = 0; diff --git a/src/audio_core/dsp_interface.cpp b/src/audio_core/dsp_interface.cpp index 74ab37f54..560ed7a3c 100644 --- a/src/audio_core/dsp_interface.cpp +++ b/src/audio_core/dsp_interface.cpp @@ -7,17 +7,18 @@ #include "audio_core/sink.h" #include "audio_core/sink_details.h" #include "common/assert.h" +#include "common/settings.h" #include "core/core.h" #include "core/dumping/backend.h" -#include "core/settings.h" namespace AudioCore { DspInterface::DspInterface() = default; DspInterface::~DspInterface() = default; -void DspInterface::SetSink(const std::string& sink_id, const std::string& audio_device) { - sink = CreateSinkFromID(Settings::values.sink_id, Settings::values.audio_device_id); +void DspInterface::SetSink(std::string_view sink_id, std::string_view audio_device) { + sink = CreateSinkFromID(Settings::values.sink_id.GetValue(), + Settings::values.audio_device_id.GetValue()); sink->SetCallback( [this](s16* buffer, std::size_t num_frames) { OutputCallback(buffer, num_frames); }); time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate()); diff --git a/src/audio_core/dsp_interface.h b/src/audio_core/dsp_interface.h index 066499a41..ba951b5d1 100644 --- a/src/audio_core/dsp_interface.h +++ b/src/audio_core/dsp_interface.h @@ -94,7 +94,7 @@ public: virtual void UnloadComponent() = 0; /// Select the sink to use based on sink id. - void SetSink(const std::string& sink_id, const std::string& audio_device); + void SetSink(std::string_view sink_id, std::string_view audio_device); /// Get the current sink Sink& GetSink(); /// Enable/Disable audio stretching. diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 217a3eb1d..acd3092cc 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -22,6 +22,7 @@ #include "common/logging/log.h" #include "common/scm_rev.h" #include "common/scope_exit.h" +#include "common/settings.h" #include "common/string_util.h" #include "core/core.h" #include "core/dumping/backend.h" @@ -34,7 +35,6 @@ #include "core/hle/service/cfg/cfg.h" #include "core/loader/loader.h" #include "core/movie.h" -#include "core/settings.h" #include "input_common/main.h" #include "network/network.h" #include "video_core/renderer_base.h" @@ -174,7 +174,7 @@ static void OnStatusMessageReceived(const Network::StatusMessageEntry& msg) { static void InitializeLogging() { Log::Filter log_filter(Log::Level::Debug); - log_filter.ParseFilterString(Settings::values.log_filter); + log_filter.ParseFilterString(Settings::values.log_filter.GetValue()); Log::SetGlobalFilter(log_filter); Log::AddBackend(std::make_unique()); @@ -192,8 +192,8 @@ int main(int argc, char** argv) { Common::DetachedTasks detached_tasks; Config config; int option_index = 0; - bool use_gdbstub = Settings::values.use_gdbstub; - u32 gdb_port = static_cast(Settings::values.gdbstub_port); + bool use_gdbstub = Settings::values.use_gdbstub.GetValue(); + u32 gdb_port = static_cast(Settings::values.gdbstub_port.GetValue()); std::string movie_record; std::string movie_record_author; std::string movie_play; @@ -363,7 +363,7 @@ int main(int argc, char** argv) { EmuWindow_SDL2::InitializeSDL2(); const auto emu_window{std::make_unique(fullscreen, false)}; - const bool use_secondary_window{Settings::values.layout_option == + const bool use_secondary_window{Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows}; const auto secondary_window = use_secondary_window ? std::make_unique(false, true) : nullptr; diff --git a/src/citra/config.cpp b/src/citra/config.cpp index eaa5aeb5a..fb8559c8f 100644 --- a/src/citra/config.cpp +++ b/src/citra/config.cpp @@ -13,9 +13,9 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/param_package.h" +#include "common/settings.h" #include "core/frontend/mic.h" #include "core/hle/service/service.h" -#include "core/settings.h" #include "input_common/main.h" #include "input_common/udp/client.h" #include "network/network_settings.h" @@ -128,10 +128,6 @@ void Config::ReadValues() { sdl2_config->GetBoolean("Renderer", "use_disk_shader_cache", true); Settings::values.frame_limit = static_cast(sdl2_config->GetInteger("Renderer", "frame_limit", 100)); - Settings::values.use_frame_limit_alternate = - sdl2_config->GetBoolean("Renderer", "use_frame_limit_alternate", false); - Settings::values.frame_limit_alternate = - static_cast(sdl2_config->GetInteger("Renderer", "frame_limit_alternate", 200)); Settings::values.use_vsync_new = static_cast(sdl2_config->GetInteger("Renderer", "use_vsync_new", 1)); Settings::values.texture_filter_name = @@ -144,10 +140,11 @@ void Config::ReadValues() { Settings::values.factor_3d = static_cast(sdl2_config->GetInteger("Renderer", "factor_3d", 0)); std::string default_shader = "none (builtin)"; - if (Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph) + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph) default_shader = "dubois (builtin)"; - else if (Settings::values.render_3d == Settings::StereoRenderOption::Interlaced || - Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced) + else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Interlaced || + Settings::values.render_3d.GetValue() == + Settings::StereoRenderOption::ReverseInterlaced) default_shader = "horizontal (builtin)"; Settings::values.pp_shader_name = sdl2_config->GetString("Renderer", "pp_shader_name", default_shader); @@ -188,9 +185,8 @@ void Config::ReadValues() { sdl2_config->GetBoolean("Utility", "preload_textures", false); // Audio - Settings::values.enable_dsp_lle = sdl2_config->GetBoolean("Audio", "enable_dsp_lle", false); - Settings::values.enable_dsp_lle_multithread = - sdl2_config->GetBoolean("Audio", "enable_dsp_lle_multithread", false); + Settings::values.audio_emulation = static_cast( + sdl2_config->GetInteger("Audio", "audio_emulation", 0)); Settings::values.sink_id = sdl2_config->GetString("Audio", "output_engine", "auto"); Settings::values.enable_audio_stretching = sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true); diff --git a/src/citra/emu_window/emu_window_sdl2.cpp b/src/citra/emu_window/emu_window_sdl2.cpp index 461638156..9e7b7924f 100644 --- a/src/citra/emu_window/emu_window_sdl2.cpp +++ b/src/citra/emu_window/emu_window_sdl2.cpp @@ -12,9 +12,9 @@ #include "citra/emu_window/emu_window_sdl2.h" #include "common/logging/log.h" #include "common/scm_rev.h" +#include "common/settings.h" #include "core/3ds.h" #include "core/core.h" -#include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" #include "input_common/motion_emu.h" diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index 413c3a078..29f8292d8 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt @@ -38,6 +38,8 @@ add_executable(citra-qt configuration/config.cpp configuration/config.h configuration/configure.ui + configuration/configuration_shared.cpp + configuration/configuration_shared.h configuration/configure_audio.cpp configuration/configure_audio.h configuration/configure_audio.ui @@ -67,6 +69,9 @@ add_executable(citra-qt configuration/configure_motion_touch.cpp configuration/configure_motion_touch.h configuration/configure_motion_touch.ui + configuration/configure_per_game.cpp + configuration/configure_per_game.h + configuration/configure_per_game.ui configuration/configure_storage.cpp configuration/configure_storage.h configuration/configure_storage.ui diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index e09c4d4d5..ef89f890b 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -15,11 +15,11 @@ #include "citra_qt/main.h" #include "common/microprofile.h" #include "common/scm_rev.h" +#include "common/settings.h" #include "core/3ds.h" #include "core/core.h" #include "core/frontend/scope_acquire_context.h" #include "core/perf_stats.h" -#include "core/settings.h" #include "input_common/keyboard.h" #include "input_common/main.h" #include "input_common/motion_emu.h" diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp index a453341a7..7b8323593 100644 --- a/src/citra_qt/configuration/config.cpp +++ b/src/citra_qt/configuration/config.cpp @@ -17,17 +17,15 @@ #include "network/network.h" #include "network/network_settings.h" -Config::Config() { - // 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"; - FileUtil::CreateFullPath(qt_config_loc); - qt_config = - std::make_unique(QString::fromStdString(qt_config_loc), QSettings::IniFormat); - Reload(); +Config::Config(const std::string& config_name, ConfigType config_type) : type{config_type} { + global = config_type == ConfigType::GlobalConfig; + Initialize(config_name); } Config::~Config() { - Save(); + if (global) { + Save(); + } } const std::array Config::default_buttons = { @@ -85,46 +83,176 @@ const std::array default_hotkeys{ {QStringLiteral("Toggle Texture Dumping"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+D"), Qt::ApplicationShortcut}}}}; // clang-format on +void Config::Initialize(const std::string& config_name) { + const std::string fs_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir); + const std::string config_file = fmt::format("{}.ini", config_name); + + switch (type) { + case ConfigType::GlobalConfig: + qt_config_loc = fmt::format("{}/{}", fs_config_loc, config_file); + break; + case ConfigType::PerGameConfig: + qt_config_loc = fmt::format("{}/custom/{}", fs_config_loc, config_file); + break; + } + + FileUtil::CreateFullPath(qt_config_loc); + qt_config = + std::make_unique(QString::fromStdString(qt_config_loc), QSettings::IniFormat); + Reload(); +} + +/* {Read,Write}BasicSetting and WriteGlobalSetting templates must be defined here before their + * usages later in this file. This allows explicit definition of some types that don't work + * nicely with the general version. + */ + +// Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant, nor +// can it implicitly convert a QVariant back to a {std::,Q}string +template <> +void Config::ReadBasicSetting(Settings::Setting& setting) { + const QString name = QString::fromStdString(setting.GetLabel()); + const auto default_value = QString::fromStdString(setting.GetDefault()); + if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { + setting.SetValue(default_value.toStdString()); + } else { + setting.SetValue(qt_config->value(name, default_value).toString().toStdString()); + } +} + +template +void Config::ReadBasicSetting(Settings::Setting& setting) { + const QString name = QString::fromStdString(setting.GetLabel()); + const Type default_value = setting.GetDefault(); + if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) { + setting.SetValue(default_value); + } else { + QVariant value{}; + if constexpr (std::is_enum_v) { + using TypeU = std::underlying_type_t; + value = qt_config->value(name, static_cast(default_value)); + setting.SetValue(static_cast(value.value())); + } else { + value = qt_config->value(name, QVariant::fromValue(default_value)); + setting.SetValue(value.value()); + } + } +} + +template +void Config::ReadGlobalSetting(Settings::SwitchableSetting& setting) { + QString name = QString::fromStdString(setting.GetLabel()); + const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); + setting.SetGlobal(use_global); + if (global || !use_global) { + QVariant value{}; + if constexpr (std::is_enum_v) { + using TypeU = std::underlying_type_t; + value = QVariant::fromValue(static_cast(setting.GetDefault())); + setting.SetValue(static_cast(ReadSetting(name, value).value())); + } else { + value = QVariant::fromValue(setting.GetDefault()); + setting.SetValue(ReadSetting(name, value).value()); + } + } +} + +template <> +void Config::ReadGlobalSetting(Settings::SwitchableSetting& setting) { + QString name = QString::fromStdString(setting.GetLabel()); + const bool use_global = qt_config->value(name + QStringLiteral("/use_global"), true).toBool(); + setting.SetGlobal(use_global); + if (global || !use_global) { + const QString default_value = QString::fromStdString(setting.GetDefault()); + setting.SetValue( + ReadSetting(name, QVariant::fromValue(default_value)).toString().toStdString()); + } +} + +// Explicit std::string definition: Qt can't implicitly convert a std::string to a QVariant +template <> +void Config::WriteBasicSetting(const Settings::Setting& setting) { + const QString name = QString::fromStdString(setting.GetLabel()); + const std::string& value = setting.GetValue(); + qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); + qt_config->setValue(name, QString::fromStdString(value)); +} + +template +void Config::WriteBasicSetting(const Settings::Setting& setting) { + const QString name = QString::fromStdString(setting.GetLabel()); + const Type value = setting.GetValue(); + qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); + if constexpr (std::is_enum_v) { + qt_config->setValue(name, static_cast>(value)); + } else { + qt_config->setValue(name, QVariant::fromValue(value)); + } +} + +template +void Config::WriteGlobalSetting(const Settings::SwitchableSetting& setting) { + const QString name = QString::fromStdString(setting.GetLabel()); + const Type& value = setting.GetValue(global); + if (!global) { + qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal()); + } + if (global || !setting.UsingGlobal()) { + qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); + if constexpr (std::is_enum_v) { + qt_config->setValue(name, static_cast>(value)); + } else { + qt_config->setValue(name, QVariant::fromValue(value)); + } + } +} + +template <> +void Config::WriteGlobalSetting(const Settings::SwitchableSetting& setting) { + const QString name = QString::fromStdString(setting.GetLabel()); + const std::string& value = setting.GetValue(global); + if (!global) { + qt_config->setValue(name + QStringLiteral("/use_global"), setting.UsingGlobal()); + } + if (global || !setting.UsingGlobal()) { + qt_config->setValue(name + QStringLiteral("/default"), value == setting.GetDefault()); + qt_config->setValue(name, QString::fromStdString(value)); + } +} + void Config::ReadValues() { - ReadControlValues(); + if (global) { + ReadControlValues(); + ReadCameraValues(); + ReadDataStorageValues(); + ReadMiscellaneousValues(); + ReadDebuggingValues(); + ReadWebServiceValues(); + ReadVideoDumpingValues(); + ReadUtilityValues(); + } + + ReadUIValues(); ReadCoreValues(); ReadRendererValues(); ReadLayoutValues(); ReadAudioValues(); - ReadCameraValues(); - ReadDataStorageValues(); ReadSystemValues(); - ReadMiscellaneousValues(); - ReadDebuggingValues(); - ReadWebServiceValues(); - ReadVideoDumpingValues(); - ReadUIValues(); - ReadUtilityValues(); } void Config::ReadAudioValues() { qt_config->beginGroup(QStringLiteral("Audio")); - Settings::values.enable_dsp_lle = ReadSetting(QStringLiteral("enable_dsp_lle"), false).toBool(); - Settings::values.enable_dsp_lle_multithread = - ReadSetting(QStringLiteral("enable_dsp_lle_multithread"), false).toBool(); - 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(); - Settings::values.mic_input_type = static_cast( - ReadSetting(QStringLiteral("mic_input_type"), 0).toInt()); - Settings::values.mic_input_device = - ReadSetting(QStringLiteral("mic_input_device"), - QString::fromUtf8(Frontend::Mic::default_device_name)) - .toString() - .toStdString(); + ReadGlobalSetting(Settings::values.audio_emulation); + ReadGlobalSetting(Settings::values.enable_audio_stretching); + ReadGlobalSetting(Settings::values.volume); + + if (global) { + ReadBasicSetting(Settings::values.sink_id); + ReadBasicSetting(Settings::values.audio_device_id); + ReadBasicSetting(Settings::values.mic_input_device); + ReadBasicSetting(Settings::values.mic_input_type); + } qt_config->endGroup(); } @@ -281,11 +409,9 @@ void Config::ReadControlValues() { void Config::ReadUtilityValues() { qt_config->beginGroup(QStringLiteral("Utility")); - Settings::values.dump_textures = ReadSetting(QStringLiteral("dump_textures"), false).toBool(); - Settings::values.custom_textures = - ReadSetting(QStringLiteral("custom_textures"), false).toBool(); - Settings::values.preload_textures = - ReadSetting(QStringLiteral("preload_textures"), false).toBool(); + ReadBasicSetting(Settings::values.dump_textures); + ReadBasicSetting(Settings::values.custom_textures); + ReadBasicSetting(Settings::values.preload_textures); qt_config->endGroup(); } @@ -293,9 +419,11 @@ void Config::ReadUtilityValues() { void Config::ReadCoreValues() { qt_config->beginGroup(QStringLiteral("Core")); - Settings::values.use_cpu_jit = ReadSetting(QStringLiteral("use_cpu_jit"), true).toBool(); - Settings::values.cpu_clock_percentage = - ReadSetting(QStringLiteral("cpu_clock_percentage"), 100).toInt(); + ReadGlobalSetting(Settings::values.cpu_clock_percentage); + + if (global) { + ReadBasicSetting(Settings::values.use_cpu_jit); + } qt_config->endGroup(); } @@ -303,10 +431,9 @@ void Config::ReadCoreValues() { void Config::ReadDataStorageValues() { qt_config->beginGroup(QStringLiteral("Data Storage")); - Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool(); + ReadBasicSetting(Settings::values.use_virtual_sd); + ReadBasicSetting(Settings::values.use_custom_storage); - Settings::values.use_custom_storage = - ReadSetting(QStringLiteral("use_custom_storage"), false).toBool(); const std::string nand_dir = ReadSetting(QStringLiteral("nand_directory"), QStringLiteral("")).toString().toStdString(); const std::string sdmc_dir = @@ -326,8 +453,8 @@ void Config::ReadDebuggingValues() { // Intentionally not using the QT default setting as this is intended to be changed in the ini Settings::values.record_frame_times = qt_config->value(QStringLiteral("record_frame_times"), false).toBool(); - Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool(); - Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt(); + ReadBasicSetting(Settings::values.use_gdbstub); + ReadBasicSetting(Settings::values.gdbstub_port); qt_config->beginGroup(QStringLiteral("LLE")); for (const auto& service_module : Service::service_module_map) { @@ -335,45 +462,38 @@ void Config::ReadDebuggingValues() { Settings::values.lle_modules.emplace(service_module.name, use_lle); } qt_config->endGroup(); - qt_config->endGroup(); } void Config::ReadLayoutValues() { qt_config->beginGroup(QStringLiteral("Layout")); - Settings::values.render_3d = static_cast( - ReadSetting(QStringLiteral("render_3d"), 0).toInt()); - Settings::values.factor_3d = ReadSetting(QStringLiteral("factor_3d"), 0).toInt(); - Settings::values.mono_render_left_eye = - ReadSetting(QStringLiteral("mono_render_left_eye"), true).toBool(); + ReadGlobalSetting(Settings::values.render_3d); + ReadGlobalSetting(Settings::values.factor_3d); Settings::values.pp_shader_name = - ReadSetting(QStringLiteral("pp_shader_name"), - (Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph) - ? QStringLiteral("dubois (builtin)") - : QStringLiteral("none (builtin)")) + ReadSetting(QStringLiteral("pp_shader_name"), (Settings::values.render_3d.GetValue() == + Settings::StereoRenderOption::Anaglyph) + ? QStringLiteral("dubois (builtin)") + : QStringLiteral("none (builtin)")) .toString() .toStdString(); - Settings::values.filter_mode = ReadSetting(QStringLiteral("filter_mode"), true).toBool(); - Settings::values.layout_option = - static_cast(ReadSetting(QStringLiteral("layout_option")).toInt()); - Settings::values.swap_screen = ReadSetting(QStringLiteral("swap_screen"), false).toBool(); - Settings::values.upright_screen = ReadSetting(QStringLiteral("upright_screen"), false).toBool(); - Settings::values.custom_layout = ReadSetting(QStringLiteral("custom_layout"), false).toBool(); - Settings::values.custom_top_left = ReadSetting(QStringLiteral("custom_top_left"), 0).toInt(); - Settings::values.custom_top_top = ReadSetting(QStringLiteral("custom_top_top"), 0).toInt(); - Settings::values.custom_top_right = - ReadSetting(QStringLiteral("custom_top_right"), 400).toInt(); - Settings::values.custom_top_bottom = - ReadSetting(QStringLiteral("custom_top_bottom"), 240).toInt(); - Settings::values.custom_bottom_left = - ReadSetting(QStringLiteral("custom_bottom_left"), 40).toInt(); - Settings::values.custom_bottom_top = - ReadSetting(QStringLiteral("custom_bottom_top"), 240).toInt(); - Settings::values.custom_bottom_right = - ReadSetting(QStringLiteral("custom_bottom_right"), 360).toInt(); - Settings::values.custom_bottom_bottom = - ReadSetting(QStringLiteral("custom_bottom_bottom"), 480).toInt(); + ReadGlobalSetting(Settings::values.filter_mode); + ReadGlobalSetting(Settings::values.layout_option); + ReadGlobalSetting(Settings::values.swap_screen); + ReadGlobalSetting(Settings::values.upright_screen); + + if (global) { + ReadBasicSetting(Settings::values.mono_render_left_eye); + ReadBasicSetting(Settings::values.custom_layout); + ReadBasicSetting(Settings::values.custom_top_left); + ReadBasicSetting(Settings::values.custom_top_top); + ReadBasicSetting(Settings::values.custom_top_right); + ReadBasicSetting(Settings::values.custom_top_bottom); + ReadBasicSetting(Settings::values.custom_bottom_left); + ReadBasicSetting(Settings::values.custom_bottom_top); + ReadBasicSetting(Settings::values.custom_bottom_right); + ReadBasicSetting(Settings::values.custom_bottom_bottom); + } qt_config->endGroup(); } @@ -381,10 +501,7 @@ void Config::ReadLayoutValues() { void Config::ReadMiscellaneousValues() { qt_config->beginGroup(QStringLiteral("Miscellaneous")); - Settings::values.log_filter = - ReadSetting(QStringLiteral("log_filter"), QStringLiteral("*:Info")) - .toString() - .toStdString(); + ReadBasicSetting(Settings::values.log_filter); qt_config->endGroup(); } @@ -434,46 +551,49 @@ void Config::ReadMultiplayerValues() { void Config::ReadPathValues() { qt_config->beginGroup(QStringLiteral("Paths")); - UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString(); - UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString(); - UISettings::values.movie_record_path = - ReadSetting(QStringLiteral("movieRecordPath")).toString(); - UISettings::values.movie_playback_path = - ReadSetting(QStringLiteral("moviePlaybackPath")).toString(); - UISettings::values.screenshot_path = ReadSetting(QStringLiteral("screenshotPath")).toString(); - UISettings::values.video_dumping_path = - ReadSetting(QStringLiteral("videoDumpingPath")).toString(); - UISettings::values.game_dir_deprecated = - ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString(); - UISettings::values.game_dir_deprecated_deepscan = - ReadSetting(QStringLiteral("gameListDeepScan"), false).toBool(); - int size = qt_config->beginReadArray(QStringLiteral("gamedirs")); - for (int i = 0; i < size; ++i) { - qt_config->setArrayIndex(i); - UISettings::GameDir game_dir; - game_dir.path = ReadSetting(QStringLiteral("path")).toString(); - game_dir.deep_scan = ReadSetting(QStringLiteral("deep_scan"), false).toBool(); - game_dir.expanded = ReadSetting(QStringLiteral("expanded"), true).toBool(); - UISettings::values.game_dirs.append(game_dir); - } - qt_config->endArray(); - // create NAND and SD card directories if empty, these are not removable through the UI, - // also carries over old game list settings if present - if (UISettings::values.game_dirs.isEmpty()) { - UISettings::GameDir game_dir; - game_dir.path = QStringLiteral("INSTALLED"); - game_dir.expanded = true; - UISettings::values.game_dirs.append(game_dir); - game_dir.path = QStringLiteral("SYSTEM"); - UISettings::values.game_dirs.append(game_dir); - if (UISettings::values.game_dir_deprecated != QStringLiteral(".")) { - game_dir.path = UISettings::values.game_dir_deprecated; - game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan; + ReadGlobalSetting(UISettings::values.screenshot_path); + + if (global) { + UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString(); + UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString(); + UISettings::values.movie_record_path = + ReadSetting(QStringLiteral("movieRecordPath")).toString(); + UISettings::values.movie_playback_path = + ReadSetting(QStringLiteral("moviePlaybackPath")).toString(); + UISettings::values.video_dumping_path = + ReadSetting(QStringLiteral("videoDumpingPath")).toString(); + UISettings::values.game_dir_deprecated = + ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString(); + UISettings::values.game_dir_deprecated_deepscan = + ReadSetting(QStringLiteral("gameListDeepScan"), false).toBool(); + int size = qt_config->beginReadArray(QStringLiteral("gamedirs")); + for (int i = 0; i < size; ++i) { + qt_config->setArrayIndex(i); + UISettings::GameDir game_dir; + game_dir.path = ReadSetting(QStringLiteral("path")).toString(); + game_dir.deep_scan = ReadSetting(QStringLiteral("deep_scan"), false).toBool(); + game_dir.expanded = ReadSetting(QStringLiteral("expanded"), true).toBool(); UISettings::values.game_dirs.append(game_dir); } + qt_config->endArray(); + // create NAND and SD card directories if empty, these are not removable through the UI, + // also carries over old game list settings if present + if (UISettings::values.game_dirs.isEmpty()) { + UISettings::GameDir game_dir; + game_dir.path = QStringLiteral("INSTALLED"); + game_dir.expanded = true; + UISettings::values.game_dirs.append(game_dir); + game_dir.path = QStringLiteral("SYSTEM"); + UISettings::values.game_dirs.append(game_dir); + if (UISettings::values.game_dir_deprecated != QStringLiteral(".")) { + game_dir.path = UISettings::values.game_dir_deprecated; + game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan; + UISettings::values.game_dirs.append(game_dir); + } + } + UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList(); + UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString(); } - UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList(); - UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString(); qt_config->endGroup(); } @@ -481,38 +601,29 @@ void Config::ReadPathValues() { void Config::ReadRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); - Settings::values.use_hw_renderer = - ReadSetting(QStringLiteral("use_hw_renderer"), true).toBool(); - Settings::values.use_hw_shader = ReadSetting(QStringLiteral("use_hw_shader"), true).toBool(); + ReadGlobalSetting(Settings::values.use_hw_renderer); + ReadGlobalSetting(Settings::values.use_hw_shader); #ifdef __APPLE__ // Hardware shader is broken on macos with Intel GPUs thanks to poor drivers. // We still want to provide this option for test/development purposes, but disable it by // default. - Settings::values.separable_shader = - ReadSetting(QStringLiteral("separable_shader"), false).toBool(); + ReadGlobalSetting(Settings::values.separable_shader); #endif - Settings::values.shaders_accurate_mul = - ReadSetting(QStringLiteral("shaders_accurate_mul"), true).toBool(); - Settings::values.use_shader_jit = ReadSetting(QStringLiteral("use_shader_jit"), true).toBool(); - Settings::values.use_disk_shader_cache = - ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool(); - Settings::values.use_vsync_new = ReadSetting(QStringLiteral("use_vsync_new"), true).toBool(); - Settings::values.resolution_factor = - static_cast(ReadSetting(QStringLiteral("resolution_factor"), 1).toInt()); - Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt(); - Settings::values.use_frame_limit_alternate = - ReadSetting(QStringLiteral("use_frame_limit_alternate"), false).toBool(); - Settings::values.frame_limit_alternate = - ReadSetting(QStringLiteral("frame_limit_alternate"), 200).toInt(); + ReadGlobalSetting(Settings::values.shaders_accurate_mul); + ReadGlobalSetting(Settings::values.use_disk_shader_cache); + ReadGlobalSetting(Settings::values.use_vsync_new); + ReadGlobalSetting(Settings::values.resolution_factor); + ReadGlobalSetting(Settings::values.frame_limit); - 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(); + ReadGlobalSetting(Settings::values.bg_red); + ReadGlobalSetting(Settings::values.bg_green); + ReadGlobalSetting(Settings::values.bg_blue); - Settings::values.texture_filter_name = - ReadSetting(QStringLiteral("texture_filter_name"), QStringLiteral("none")) - .toString() - .toStdString(); + ReadGlobalSetting(Settings::values.texture_filter_name); + + if (global) { + ReadBasicSetting(Settings::values.use_shader_jit); + } qt_config->endGroup(); } @@ -539,16 +650,14 @@ void Config::ReadShortcutValues() { void Config::ReadSystemValues() { qt_config->beginGroup(QStringLiteral("System")); - Settings::values.is_new_3ds = ReadSetting(QStringLiteral("is_new_3ds"), true).toBool(); - Settings::values.region_value = - ReadSetting(QStringLiteral("region_value"), Settings::REGION_VALUE_AUTO_SELECT).toInt(); - Settings::values.init_clock = static_cast( - ReadSetting(QStringLiteral("init_clock"), static_cast(Settings::InitClock::SystemTime)) - .toInt()); - Settings::values.init_time = - ReadSetting(QStringLiteral("init_time"), 946681277ULL).toULongLong(); - Settings::values.init_time_offset = - ReadSetting(QStringLiteral("init_time_offset"), 0LL).toLongLong(); + ReadGlobalSetting(Settings::values.is_new_3ds); + ReadGlobalSetting(Settings::values.region_value); + + if (global) { + ReadBasicSetting(Settings::values.init_clock); + ReadBasicSetting(Settings::values.init_time); + ReadBasicSetting(Settings::values.init_time_offset); + } qt_config->endGroup(); } @@ -599,39 +708,33 @@ void Config::ReadVideoDumpingValues() { void Config::ReadUIValues() { qt_config->beginGroup(QStringLiteral("UI")); - UISettings::values.theme = - ReadSetting(QStringLiteral("theme"), QString::fromUtf8(UISettings::themes[0].second)) - .toString(); - UISettings::values.enable_discord_presence = - ReadSetting(QStringLiteral("enable_discord_presence"), true).toBool(); - UISettings::values.screenshot_resolution_factor = - static_cast(ReadSetting(QStringLiteral("screenshot_resolution_factor"), 0).toUInt()); - - ReadUpdaterValues(); - ReadUILayoutValues(); - ReadUIGameListValues(); ReadPathValues(); - ReadShortcutValues(); - ReadMultiplayerValues(); - UISettings::values.single_window_mode = - ReadSetting(QStringLiteral("singleWindowMode"), true).toBool(); - UISettings::values.fullscreen = ReadSetting(QStringLiteral("fullscreen"), false).toBool(); - UISettings::values.display_titlebar = - ReadSetting(QStringLiteral("displayTitleBars"), true).toBool(); - UISettings::values.show_filter_bar = - ReadSetting(QStringLiteral("showFilterBar"), true).toBool(); - UISettings::values.show_status_bar = - ReadSetting(QStringLiteral("showStatusBar"), true).toBool(); - UISettings::values.confirm_before_closing = - ReadSetting(QStringLiteral("confirmClose"), true).toBool(); - UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool(); - UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt(); - UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool(); - UISettings::values.pause_when_in_background = - ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool(); - UISettings::values.hide_mouse = - ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool(); + if (global) { + UISettings::values.theme = + ReadSetting(QStringLiteral("theme"), QString::fromUtf8(UISettings::themes[0].second)) + .toString(); + ReadBasicSetting(UISettings::values.enable_discord_presence); + ReadBasicSetting(UISettings::values.screenshot_resolution_factor); + + ReadUpdaterValues(); + ReadUILayoutValues(); + ReadUIGameListValues(); + ReadShortcutValues(); + ReadMultiplayerValues(); + + ReadBasicSetting(UISettings::values.single_window_mode); + ReadBasicSetting(UISettings::values.fullscreen); + ReadBasicSetting(UISettings::values.display_titlebar); + ReadBasicSetting(UISettings::values.show_filter_bar); + ReadBasicSetting(UISettings::values.show_status_bar); + ReadBasicSetting(UISettings::values.confirm_before_closing); + ReadBasicSetting(UISettings::values.first_start); + ReadBasicSetting(UISettings::values.callout_flags); + ReadBasicSetting(UISettings::values.show_console); + ReadBasicSetting(UISettings::values.pause_when_in_background); + ReadBasicSetting(UISettings::values.hide_mouse); + } qt_config->endGroup(); } @@ -639,36 +742,11 @@ void Config::ReadUIValues() { void Config::ReadUIGameListValues() { qt_config->beginGroup(QStringLiteral("GameList")); - auto icon_size = UISettings::GameListIconSize{ - ReadSetting(QStringLiteral("iconSize"), - static_cast(UISettings::GameListIconSize::LargeIcon)) - .toInt()}; - if (icon_size < UISettings::GameListIconSize::NoIcon || - icon_size > UISettings::GameListIconSize::LargeIcon) { - icon_size = UISettings::GameListIconSize::LargeIcon; - } - UISettings::values.game_list_icon_size = icon_size; - - UISettings::GameListText row_1 = UISettings::GameListText{ - ReadSetting(QStringLiteral("row1"), static_cast(UISettings::GameListText::TitleName)) - .toInt()}; - if (row_1 <= UISettings::GameListText::NoText || row_1 >= UISettings::GameListText::ListEnd) { - row_1 = UISettings::GameListText::TitleName; - } - UISettings::values.game_list_row_1 = row_1; - - UISettings::GameListText row_2 = UISettings::GameListText{ - ReadSetting(QStringLiteral("row2"), static_cast(UISettings::GameListText::FileName)) - .toInt()}; - if (row_2 < UISettings::GameListText::NoText || row_2 >= UISettings::GameListText::ListEnd) { - row_2 = UISettings::GameListText::FileName; - } - UISettings::values.game_list_row_2 = row_2; - - UISettings::values.game_list_hide_no_icon = - ReadSetting(QStringLiteral("hideNoIcon"), false).toBool(); - UISettings::values.game_list_single_line_mode = - ReadSetting(QStringLiteral("singleLineMode"), false).toBool(); + ReadBasicSetting(UISettings::values.game_list_icon_size); + ReadBasicSetting(UISettings::values.game_list_row_1); + ReadBasicSetting(UISettings::values.game_list_row_2); + ReadBasicSetting(UISettings::values.game_list_hide_no_icon); + ReadBasicSetting(UISettings::values.game_list_single_line_mode); qt_config->endGroup(); } @@ -684,8 +762,7 @@ void Config::ReadUILayoutValues() { ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray(); UISettings::values.microprofile_geometry = ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray(); - UISettings::values.microprofile_visible = - ReadSetting(QStringLiteral("microProfileDialogVisible"), false).toBool(); + ReadBasicSetting(UISettings::values.microprofile_visible); qt_config->endGroup(); } @@ -693,10 +770,8 @@ void Config::ReadUILayoutValues() { void Config::ReadUpdaterValues() { qt_config->beginGroup(QStringLiteral("Updater")); - UISettings::values.check_for_update_on_start = - ReadSetting(QStringLiteral("check_for_update_on_start"), true).toBool(); - UISettings::values.update_on_close = - ReadSetting(QStringLiteral("update_on_close"), false).toBool(); + ReadBasicSetting(UISettings::values.check_for_update_on_start); + ReadBasicSetting(UISettings::values.update_on_close); qt_config->endGroup(); } @@ -719,40 +794,38 @@ void Config::ReadWebServiceValues() { } void Config::SaveValues() { - SaveControlValues(); + if (global) { + SaveControlValues(); + SaveCameraValues(); + SaveDataStorageValues(); + SaveMiscellaneousValues(); + SaveDebuggingValues(); + SaveWebServiceValues(); + SaveVideoDumpingValues(); + SaveUtilityValues(); + } + + SaveUIValues(); SaveCoreValues(); SaveRendererValues(); SaveLayoutValues(); SaveAudioValues(); - SaveCameraValues(); - SaveDataStorageValues(); SaveSystemValues(); - SaveMiscellaneousValues(); - SaveDebuggingValues(); - SaveWebServiceValues(); - SaveVideoDumpingValues(); - SaveUIValues(); - SaveUtilityValues(); } void Config::SaveAudioValues() { qt_config->beginGroup(QStringLiteral("Audio")); - WriteSetting(QStringLiteral("enable_dsp_lle"), Settings::values.enable_dsp_lle, false); - WriteSetting(QStringLiteral("enable_dsp_lle_multithread"), - Settings::values.enable_dsp_lle_multithread, false); - 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); - WriteSetting(QStringLiteral("mic_input_device"), - QString::fromStdString(Settings::values.mic_input_device), - QString::fromUtf8(Frontend::Mic::default_device_name)); - WriteSetting(QStringLiteral("mic_input_type"), - static_cast(Settings::values.mic_input_type), 0); + WriteGlobalSetting(Settings::values.audio_emulation); + WriteGlobalSetting(Settings::values.enable_audio_stretching); + WriteGlobalSetting(Settings::values.volume); + + if (global) { + WriteBasicSetting(Settings::values.sink_id); + WriteBasicSetting(Settings::values.audio_device_id); + WriteBasicSetting(Settings::values.mic_input_device); + WriteBasicSetting(Settings::values.mic_input_type); + } qt_config->endGroup(); } @@ -849,9 +922,9 @@ void Config::SaveControlValues() { void Config::SaveUtilityValues() { qt_config->beginGroup(QStringLiteral("Utility")); - WriteSetting(QStringLiteral("dump_textures"), Settings::values.dump_textures, false); - WriteSetting(QStringLiteral("custom_textures"), Settings::values.custom_textures, false); - WriteSetting(QStringLiteral("preload_textures"), Settings::values.preload_textures, false); + WriteBasicSetting(Settings::values.dump_textures); + WriteBasicSetting(Settings::values.custom_textures); + WriteBasicSetting(Settings::values.preload_textures); qt_config->endGroup(); } @@ -859,9 +932,11 @@ void Config::SaveUtilityValues() { void Config::SaveCoreValues() { qt_config->beginGroup(QStringLiteral("Core")); - WriteSetting(QStringLiteral("use_cpu_jit"), Settings::values.use_cpu_jit, true); - WriteSetting(QStringLiteral("cpu_clock_percentage"), Settings::values.cpu_clock_percentage, - 100); + WriteGlobalSetting(Settings::values.cpu_clock_percentage); + + if (global) { + WriteBasicSetting(Settings::values.use_cpu_jit); + } qt_config->endGroup(); } @@ -869,8 +944,8 @@ void Config::SaveCoreValues() { void Config::SaveDataStorageValues() { qt_config->beginGroup(QStringLiteral("Data Storage")); - WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true); - WriteSetting(QStringLiteral("use_custom_storage"), Settings::values.use_custom_storage, false); + WriteBasicSetting(Settings::values.use_virtual_sd); + WriteBasicSetting(Settings::values.use_custom_storage); WriteSetting(QStringLiteral("nand_directory"), QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)), QStringLiteral("")); @@ -886,8 +961,8 @@ void Config::SaveDebuggingValues() { // Intentionally not using the QT default setting as this is intended to be changed in the ini qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times); - WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false); - WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689); + WriteBasicSetting(Settings::values.use_gdbstub); + WriteBasicSetting(Settings::values.gdbstub_port); qt_config->beginGroup(QStringLiteral("LLE")); for (const auto& service_module : Settings::values.lle_modules) { @@ -901,29 +976,30 @@ void Config::SaveDebuggingValues() { void Config::SaveLayoutValues() { qt_config->beginGroup(QStringLiteral("Layout")); - WriteSetting(QStringLiteral("render_3d"), static_cast(Settings::values.render_3d), 0); - WriteSetting(QStringLiteral("factor_3d"), Settings::values.factor_3d.load(), 0); - WriteSetting(QStringLiteral("mono_render_left_eye"), Settings::values.mono_render_left_eye, - false); + WriteGlobalSetting(Settings::values.render_3d); + WriteGlobalSetting(Settings::values.factor_3d); WriteSetting(QStringLiteral("pp_shader_name"), - QString::fromStdString(Settings::values.pp_shader_name), - (Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph) + QString::fromStdString(Settings::values.pp_shader_name.GetValue()), + (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph) ? QStringLiteral("dubois (builtin)") : QStringLiteral("none (builtin)")); - WriteSetting(QStringLiteral("filter_mode"), Settings::values.filter_mode, true); - WriteSetting(QStringLiteral("layout_option"), static_cast(Settings::values.layout_option)); - WriteSetting(QStringLiteral("swap_screen"), Settings::values.swap_screen, false); - WriteSetting(QStringLiteral("upright_screen"), Settings::values.upright_screen, false); - WriteSetting(QStringLiteral("custom_layout"), Settings::values.custom_layout, false); - WriteSetting(QStringLiteral("custom_top_left"), Settings::values.custom_top_left, 0); - WriteSetting(QStringLiteral("custom_top_top"), Settings::values.custom_top_top, 0); - WriteSetting(QStringLiteral("custom_top_right"), Settings::values.custom_top_right, 400); - WriteSetting(QStringLiteral("custom_top_bottom"), Settings::values.custom_top_bottom, 240); - WriteSetting(QStringLiteral("custom_bottom_left"), Settings::values.custom_bottom_left, 40); - WriteSetting(QStringLiteral("custom_bottom_top"), Settings::values.custom_bottom_top, 240); - WriteSetting(QStringLiteral("custom_bottom_right"), Settings::values.custom_bottom_right, 360); - WriteSetting(QStringLiteral("custom_bottom_bottom"), Settings::values.custom_bottom_bottom, - 480); + WriteGlobalSetting(Settings::values.filter_mode); + WriteGlobalSetting(Settings::values.layout_option); + WriteGlobalSetting(Settings::values.swap_screen); + WriteGlobalSetting(Settings::values.upright_screen); + + if (global) { + WriteBasicSetting(Settings::values.mono_render_left_eye); + WriteBasicSetting(Settings::values.custom_layout); + WriteBasicSetting(Settings::values.custom_top_left); + WriteBasicSetting(Settings::values.custom_top_top); + WriteBasicSetting(Settings::values.custom_top_right); + WriteBasicSetting(Settings::values.custom_top_bottom); + WriteBasicSetting(Settings::values.custom_bottom_left); + WriteBasicSetting(Settings::values.custom_bottom_top); + WriteBasicSetting(Settings::values.custom_bottom_right); + WriteBasicSetting(Settings::values.custom_bottom_bottom); + } qt_config->endGroup(); } @@ -931,8 +1007,7 @@ void Config::SaveLayoutValues() { void Config::SaveMiscellaneousValues() { qt_config->beginGroup(QStringLiteral("Miscellaneous")); - WriteSetting(QStringLiteral("log_filter"), QString::fromStdString(Settings::values.log_filter), - QStringLiteral("*:Info")); + WriteBasicSetting(Settings::values.log_filter); qt_config->endGroup(); } @@ -974,23 +1049,25 @@ void Config::SaveMultiplayerValues() { void Config::SavePathValues() { qt_config->beginGroup(QStringLiteral("Paths")); - WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path); - WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path); - WriteSetting(QStringLiteral("movieRecordPath"), UISettings::values.movie_record_path); - WriteSetting(QStringLiteral("moviePlaybackPath"), UISettings::values.movie_playback_path); - WriteSetting(QStringLiteral("screenshotPath"), UISettings::values.screenshot_path); - WriteSetting(QStringLiteral("videoDumpingPath"), UISettings::values.video_dumping_path); - qt_config->beginWriteArray(QStringLiteral("gamedirs")); - for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { - qt_config->setArrayIndex(i); - const auto& game_dir = UISettings::values.game_dirs[i]; - WriteSetting(QStringLiteral("path"), game_dir.path); - WriteSetting(QStringLiteral("deep_scan"), game_dir.deep_scan, false); - WriteSetting(QStringLiteral("expanded"), game_dir.expanded, true); + WriteGlobalSetting(UISettings::values.screenshot_path); + if (global) { + WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path); + WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path); + WriteSetting(QStringLiteral("movieRecordPath"), UISettings::values.movie_record_path); + WriteSetting(QStringLiteral("moviePlaybackPath"), UISettings::values.movie_playback_path); + WriteSetting(QStringLiteral("videoDumpingPath"), UISettings::values.video_dumping_path); + qt_config->beginWriteArray(QStringLiteral("gamedirs")); + for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) { + qt_config->setArrayIndex(i); + const auto& game_dir = UISettings::values.game_dirs[i]; + WriteSetting(QStringLiteral("path"), game_dir.path); + WriteSetting(QStringLiteral("deep_scan"), game_dir.deep_scan, false); + WriteSetting(QStringLiteral("expanded"), game_dir.expanded, true); + } + qt_config->endArray(); + WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files); + WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{}); } - qt_config->endArray(); - WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files); - WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{}); qt_config->endGroup(); } @@ -998,34 +1075,29 @@ void Config::SavePathValues() { void Config::SaveRendererValues() { qt_config->beginGroup(QStringLiteral("Renderer")); - WriteSetting(QStringLiteral("use_hw_renderer"), Settings::values.use_hw_renderer, true); - WriteSetting(QStringLiteral("use_hw_shader"), Settings::values.use_hw_shader, true); + WriteGlobalSetting(Settings::values.use_hw_renderer); + WriteGlobalSetting(Settings::values.use_hw_shader); #ifdef __APPLE__ // Hardware shader is broken on macos thanks to poor drivers. // TODO: enable this for none Intel GPUs - WriteSetting(QStringLiteral("use_separable_shader"), Settings::values.separable_shader, false); + WriteGlobalSetting(Settings::values.separable_shader); #endif - WriteSetting(QStringLiteral("shaders_accurate_mul"), Settings::values.shaders_accurate_mul, - true); - WriteSetting(QStringLiteral("use_shader_jit"), Settings::values.use_shader_jit, true); - WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache, - true); - WriteSetting(QStringLiteral("use_vsync_new"), Settings::values.use_vsync_new, true); - WriteSetting(QStringLiteral("resolution_factor"), Settings::values.resolution_factor, 1); - WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100); - WriteSetting(QStringLiteral("use_frame_limit_alternate"), - Settings::values.use_frame_limit_alternate, false); - WriteSetting(QStringLiteral("frame_limit_alternate"), Settings::values.frame_limit_alternate, - 200); + WriteGlobalSetting(Settings::values.shaders_accurate_mul); + WriteGlobalSetting(Settings::values.use_disk_shader_cache); + WriteGlobalSetting(Settings::values.use_vsync_new); + WriteGlobalSetting(Settings::values.resolution_factor); + WriteGlobalSetting(Settings::values.frame_limit); - // Cast to double because Qt's written float values are not human-readable - WriteSetting(QStringLiteral("bg_red"), static_cast(Settings::values.bg_red), 0.0); - WriteSetting(QStringLiteral("bg_green"), static_cast(Settings::values.bg_green), 0.0); - WriteSetting(QStringLiteral("bg_blue"), static_cast(Settings::values.bg_blue), 0.0); + WriteGlobalSetting(Settings::values.bg_red); + WriteGlobalSetting(Settings::values.bg_green); + WriteGlobalSetting(Settings::values.bg_blue); - WriteSetting(QStringLiteral("texture_filter_name"), - QString::fromStdString(Settings::values.texture_filter_name), - QStringLiteral("none")); + WriteGlobalSetting(Settings::values.texture_filter_name); + + if (global) { + WriteSetting(QStringLiteral("use_shader_jit"), Settings::values.use_shader_jit.GetValue(), + true); + } qt_config->endGroup(); } @@ -1052,15 +1124,14 @@ void Config::SaveShortcutValues() { void Config::SaveSystemValues() { qt_config->beginGroup(QStringLiteral("System")); - WriteSetting(QStringLiteral("is_new_3ds"), Settings::values.is_new_3ds, true); - WriteSetting(QStringLiteral("region_value"), Settings::values.region_value, - Settings::REGION_VALUE_AUTO_SELECT); - WriteSetting(QStringLiteral("init_clock"), static_cast(Settings::values.init_clock), - static_cast(Settings::InitClock::SystemTime)); - WriteSetting(QStringLiteral("init_time"), - static_cast(Settings::values.init_time), 946681277ULL); - WriteSetting(QStringLiteral("init_time_offset"), - static_cast(Settings::values.init_time_offset), 0LL); + WriteGlobalSetting(Settings::values.is_new_3ds); + WriteGlobalSetting(Settings::values.region_value); + + if (global) { + WriteBasicSetting(Settings::values.init_clock); + WriteBasicSetting(Settings::values.init_time); + WriteBasicSetting(Settings::values.init_time_offset); + } qt_config->endGroup(); } @@ -1095,32 +1166,32 @@ void Config::SaveVideoDumpingValues() { void Config::SaveUIValues() { qt_config->beginGroup(QStringLiteral("UI")); - WriteSetting(QStringLiteral("theme"), UISettings::values.theme, - QString::fromUtf8(UISettings::themes[0].second)); - WriteSetting(QStringLiteral("enable_discord_presence"), - UISettings::values.enable_discord_presence, true); - WriteSetting(QStringLiteral("screenshot_resolution_factor"), - UISettings::values.screenshot_resolution_factor, 0); - - SaveUpdaterValues(); - SaveUILayoutValues(); - SaveUIGameListValues(); SavePathValues(); - SaveShortcutValues(); - SaveMultiplayerValues(); - WriteSetting(QStringLiteral("singleWindowMode"), UISettings::values.single_window_mode, true); - WriteSetting(QStringLiteral("fullscreen"), UISettings::values.fullscreen, false); - WriteSetting(QStringLiteral("displayTitleBars"), UISettings::values.display_titlebar, true); - WriteSetting(QStringLiteral("showFilterBar"), UISettings::values.show_filter_bar, true); - WriteSetting(QStringLiteral("showStatusBar"), UISettings::values.show_status_bar, true); - WriteSetting(QStringLiteral("confirmClose"), UISettings::values.confirm_before_closing, true); - WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true); - WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0); - WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false); - WriteSetting(QStringLiteral("pauseWhenInBackground"), - UISettings::values.pause_when_in_background, false); - WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false); + if (global) { + WriteSetting(QStringLiteral("theme"), UISettings::values.theme, + QString::fromUtf8(UISettings::themes[0].second)); + WriteBasicSetting(UISettings::values.enable_discord_presence); + WriteBasicSetting(UISettings::values.screenshot_resolution_factor); + + SaveUpdaterValues(); + SaveUILayoutValues(); + SaveUIGameListValues(); + SaveShortcutValues(); + SaveMultiplayerValues(); + + WriteBasicSetting(UISettings::values.single_window_mode); + WriteBasicSetting(UISettings::values.fullscreen); + WriteBasicSetting(UISettings::values.display_titlebar); + WriteBasicSetting(UISettings::values.show_filter_bar); + WriteBasicSetting(UISettings::values.show_status_bar); + WriteBasicSetting(UISettings::values.confirm_before_closing); + WriteBasicSetting(UISettings::values.first_start); + WriteBasicSetting(UISettings::values.callout_flags); + WriteBasicSetting(UISettings::values.show_console); + WriteBasicSetting(UISettings::values.pause_when_in_background); + WriteBasicSetting(UISettings::values.hide_mouse); + } qt_config->endGroup(); } @@ -1128,13 +1199,11 @@ void Config::SaveUIValues() { void Config::SaveUIGameListValues() { qt_config->beginGroup(QStringLiteral("GameList")); - WriteSetting(QStringLiteral("iconSize"), - static_cast(UISettings::values.game_list_icon_size), 2); - WriteSetting(QStringLiteral("row1"), static_cast(UISettings::values.game_list_row_1), 2); - WriteSetting(QStringLiteral("row2"), static_cast(UISettings::values.game_list_row_2), 0); - WriteSetting(QStringLiteral("hideNoIcon"), UISettings::values.game_list_hide_no_icon, false); - WriteSetting(QStringLiteral("singleLineMode"), UISettings::values.game_list_single_line_mode, - false); + WriteBasicSetting(UISettings::values.game_list_icon_size); + WriteBasicSetting(UISettings::values.game_list_row_1); + WriteBasicSetting(UISettings::values.game_list_row_2); + WriteBasicSetting(UISettings::values.game_list_hide_no_icon); + WriteBasicSetting(UISettings::values.game_list_single_line_mode); qt_config->endGroup(); } @@ -1148,8 +1217,7 @@ void Config::SaveUILayoutValues() { WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state); WriteSetting(QStringLiteral("microProfileDialogGeometry"), UISettings::values.microprofile_geometry); - WriteSetting(QStringLiteral("microProfileDialogVisible"), - UISettings::values.microprofile_visible, false); + WriteBasicSetting(UISettings::values.microprofile_visible); qt_config->endGroup(); } @@ -1157,9 +1225,8 @@ void Config::SaveUILayoutValues() { void Config::SaveUpdaterValues() { qt_config->beginGroup(QStringLiteral("Updater")); - WriteSetting(QStringLiteral("check_for_update_on_start"), - UISettings::values.check_for_update_on_start, true); - WriteSetting(QStringLiteral("update_on_close"), UISettings::values.update_on_close, false); + WriteBasicSetting(UISettings::values.check_for_update_on_start); + WriteBasicSetting(UISettings::values.update_on_close); qt_config->endGroup(); } @@ -1193,6 +1260,15 @@ QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) return result; } +template +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(); + } +} + void Config::WriteSetting(const QString& name, const QVariant& value) { qt_config->setValue(name, value); } @@ -1203,11 +1279,21 @@ void Config::WriteSetting(const QString& name, const QVariant& value, qt_config->setValue(name, value); } +void Config::WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value, + bool use_global) { + 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 SaveValues(); - Settings::Apply(); } void Config::Save() { diff --git a/src/citra_qt/configuration/config.h b/src/citra_qt/configuration/config.h index 8b1cf8193..8a8659de4 100644 --- a/src/citra_qt/configuration/config.h +++ b/src/citra_qt/configuration/config.h @@ -8,13 +8,16 @@ #include #include #include -#include "core/settings.h" +#include "common/settings.h" class QSettings; class Config { public: - Config(); + enum class ConfigType : u32 { GlobalConfig, PerGameConfig }; + + explicit Config(const std::string& config_name = "qt-config", + ConfigType config_type = ConfigType::GlobalConfig); ~Config(); void Reload(); @@ -24,6 +27,8 @@ public: static const std::array, Settings::NativeAnalog::NumAnalogs> default_analogs; private: + void Initialize(const std::string& config_name); + void ReadValues(); void ReadAudioValues(); void ReadCameraValues(); @@ -68,11 +73,78 @@ private: void SaveWebServiceValues(); void SaveVideoDumpingValues(); + /** + * Reads a setting from the qt_config. + * + * @param name The setting's identifier + * @param default_value The value to use when the setting is not already present in the config + */ QVariant ReadSetting(const QString& name) const; QVariant ReadSetting(const QString& name, const QVariant& default_value) const; + + /** + * Only reads a setting from the qt_config if the current config is a global config, or if the + * current config is a custom config and the setting is overriding the global setting. Otherwise + * it does nothing. + * + * @param setting The variable to be modified + * @param name The setting's identifier + * @param default_value The value to use when the setting is not already present in the config + */ + template + void ReadSettingGlobal(Type& setting, const QString& name, const QVariant& default_value) const; + + /** + * Writes a setting to the qt_config. + * + * @param name The setting's idetentifier + * @param value Value of the setting + * @param default_value Default of the setting if not present in qt_config + * @param use_global Specifies if the custom or global config should be in use, for custom + * configs + */ void WriteSetting(const QString& name, const QVariant& value); void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value); + void WriteSetting(const QString& name, const QVariant& value, const QVariant& default_value, + bool use_global); + /** + * Reads a value from the qt_config and applies it to the setting, using its label and default + * value. If the config is a custom config, this will also read the global state of the setting + * and apply that information to it. + * + * @param The setting + */ + template + void ReadGlobalSetting(Settings::SwitchableSetting& setting); + + /** + * Sets a value to the qt_config using the setting's label and default value. If the config is a + * custom config, it will apply the global state, and the custom value if needed. + * + * @param The setting + */ + template + void WriteGlobalSetting(const Settings::SwitchableSetting& setting); + + /** + * Reads a value from the qt_config using the setting's label and default value and applies the + * value to the setting. + * + * @param The setting + */ + template + void ReadBasicSetting(Settings::Setting& setting); + + /** Sets a value from the setting in the qt_config using the setting's label and default value. + * + * @param The setting + */ + template + void WriteBasicSetting(const Settings::Setting& setting); + + ConfigType type; std::unique_ptr qt_config; std::string qt_config_loc; + bool global; }; diff --git a/src/citra_qt/configuration/configuration_shared.cpp b/src/citra_qt/configuration/configuration_shared.cpp new file mode 100644 index 000000000..e5039256a --- /dev/null +++ b/src/citra_qt/configuration/configuration_shared.cpp @@ -0,0 +1,95 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include "citra_qt/configuration/configuration_shared.h" +#include "citra_qt/configuration/configure_per_game.h" +#include "common/settings.h" + +void ConfigurationShared::ApplyPerGameSetting(Settings::SwitchableSetting* setting, + const QCheckBox* checkbox, + const CheckState& tracker) { + if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { + setting->SetValue(checkbox->checkState()); + } else if (!Settings::IsConfiguringGlobal()) { + if (tracker == CheckState::Global) { + setting->SetGlobal(true); + } else { + setting->SetGlobal(false); + setting->SetValue(checkbox->checkState()); + } + } +} + +void ConfigurationShared::SetPerGameSetting(QCheckBox* checkbox, + const Settings::SwitchableSetting* setting) { + if (setting->UsingGlobal()) { + checkbox->setCheckState(Qt::PartiallyChecked); + } else { + checkbox->setCheckState(setting->GetValue() ? Qt::Checked : Qt::Unchecked); + } +} + +void ConfigurationShared::SetHighlight(QWidget* widget, bool highlighted) { + if (highlighted) { + widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,203,255,0.5) }") + .arg(widget->objectName())); + } else { + widget->setStyleSheet(QStringLiteral("QWidget#%1 { background-color:rgba(0,0,0,0) }") + .arg(widget->objectName())); + } + widget->show(); +} + +void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, + const Settings::SwitchableSetting& setting, + CheckState& tracker) { + if (setting.UsingGlobal()) { + tracker = CheckState::Global; + } else { + tracker = (setting.GetValue() == setting.GetValue(true)) ? CheckState::On : CheckState::Off; + } + SetHighlight(checkbox, tracker != CheckState::Global); + QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, setting, &tracker] { + tracker = static_cast((static_cast(tracker) + 1) % + static_cast(CheckState::Count)); + if (tracker == CheckState::Global) { + checkbox->setChecked(setting.GetValue(true)); + } + SetHighlight(checkbox, tracker != CheckState::Global); + }); +} + +void ConfigurationShared::SetColoredTristate(QCheckBox* checkbox, bool global, bool state, + bool global_state, CheckState& tracker) { + if (global) { + tracker = CheckState::Global; + } else { + tracker = (state == global_state) ? CheckState::On : CheckState::Off; + } + SetHighlight(checkbox, tracker != CheckState::Global); + QObject::connect(checkbox, &QCheckBox::clicked, checkbox, [checkbox, global_state, &tracker] { + tracker = static_cast((static_cast(tracker) + 1) % + static_cast(CheckState::Count)); + if (tracker == CheckState::Global) { + checkbox->setChecked(global_state); + } + SetHighlight(checkbox, tracker != CheckState::Global); + }); +} + +void ConfigurationShared::SetColoredComboBox(QComboBox* combobox, QWidget* target, int global) { + InsertGlobalItem(combobox, global); + QObject::connect(combobox, qOverload(&QComboBox::activated), target, + [target](int index) { SetHighlight(target, index != 0); }); +} + +void ConfigurationShared::InsertGlobalItem(QComboBox* combobox, int global_index) { + const QString use_global_text = + ConfigurePerGame::tr("Use global configuration (%1)").arg(combobox->itemText(global_index)); + combobox->insertItem(ConfigurationShared::USE_GLOBAL_INDEX, use_global_text); + combobox->insertSeparator(ConfigurationShared::USE_GLOBAL_SEPARATOR_INDEX); +} diff --git a/src/citra_qt/configuration/configuration_shared.h b/src/citra_qt/configuration/configuration_shared.h new file mode 100644 index 000000000..63a77edd3 --- /dev/null +++ b/src/citra_qt/configuration/configuration_shared.h @@ -0,0 +1,98 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include "common/settings.h" + +namespace ConfigurationShared { + +constexpr int USE_GLOBAL_INDEX = + 0; ///< The index of the "Use global configuration" option in checkboxes +constexpr int USE_GLOBAL_SEPARATOR_INDEX = 1; +constexpr int USE_GLOBAL_OFFSET = 2; + +/// CheckBoxes require a tracker for their state since we emulate a tristate CheckBox +enum class CheckState { + Off, ///< Checkbox overrides to off/false + On, ///< Checkbox overrides to on/true + Global, ///< Checkbox defers to the global state + Count, ///< Simply the number of states, not a valid checkbox state +}; + +/** + * @brief ApplyPerGameSetting given a setting and a Qt UI element, properly applies a Setting + * taking into account the global/per-game check state. This is used for configuring checkboxes + * @param setting + * @param checkbox + * @param tracker + */ +void ApplyPerGameSetting(Settings::SwitchableSetting* setting, const QCheckBox* checkbox, + const CheckState& tracker); + +/** + * @brief ApplyPerGameSetting given a setting and a Qt UI element, properly applies a Setting + * taking into account the global/per-game check state. This is used for both combo boxes + * as well as any other widget that is accompanied by a combo box in per-game settings. + * @param setting The setting class that stores the desired option + * @param combobox The Qt combo box that stores the value/per-game status + * @param transform A function that accepts the combo box index and transforms it to the + * desired settings value. When used with sliders/edit the user can ignore the input value + * and set a custom value this making this function useful for these widgets as well + */ +template +void ApplyPerGameSetting(Settings::SwitchableSetting* setting, + const QComboBox* combobox, auto transform) { + if (Settings::IsConfiguringGlobal() && setting->UsingGlobal()) { + setting->SetValue(static_cast(transform(combobox->currentIndex()))); + } else if (!Settings::IsConfiguringGlobal()) { + if (combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) { + setting->SetGlobal(true); + } else { + setting->SetGlobal(false); + setting->SetValue(static_cast( + transform(combobox->currentIndex() - ConfigurationShared::USE_GLOBAL_OFFSET))); + } + } +} + +/// Simpler version of ApplyPerGameSetting without a transform parameter +template +void ApplyPerGameSetting(Settings::SwitchableSetting* setting, + const QComboBox* combobox) { + const auto transform = [](s32 index) { return index; }; + return ApplyPerGameSetting(setting, combobox, transform); +} + +/// Sets a Qt UI element given a Settings::Setting +void SetPerGameSetting(QCheckBox* checkbox, const Settings::SwitchableSetting* setting); + +template +void SetPerGameSetting(QComboBox* combobox, + const Settings::SwitchableSetting* setting) { + combobox->setCurrentIndex(setting->UsingGlobal() ? ConfigurationShared::USE_GLOBAL_INDEX + : static_cast(setting->GetValue()) + + ConfigurationShared::USE_GLOBAL_OFFSET); +} + +/// Given a Qt widget sets the background color to indicate whether the setting +/// is per-game overriden (highlighted) or global (non-highlighted) +void SetHighlight(QWidget* widget, bool highlighted); + +/// Sets up a QCheckBox like a tristate one, given a Setting +void SetColoredTristate(QCheckBox* checkbox, const Settings::SwitchableSetting& setting, + CheckState& tracker); +void SetColoredTristate(QCheckBox* checkbox, bool global, bool state, bool global_state, + CheckState& tracker); + +/// Sets up coloring of a QWidget `target` based on the state of a QComboBox, and calls +/// InsertGlobalItem +void SetColoredComboBox(QComboBox* combobox, QWidget* target, int global); + +/// Adds the "Use Global Configuration" selection and separator to the beginning of a QComboBox +void InsertGlobalItem(QComboBox* combobox, int global_index); + +} // namespace ConfigurationShared diff --git a/src/citra_qt/configuration/configure_audio.cpp b/src/citra_qt/configuration/configure_audio.cpp index 7e9dba159..af42da8ce 100644 --- a/src/citra_qt/configuration/configure_audio.cpp +++ b/src/citra_qt/configuration/configure_audio.cpp @@ -9,10 +9,11 @@ #endif #include "audio_core/sink.h" #include "audio_core/sink_details.h" +#include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configure_audio.h" +#include "common/settings.h" #include "core/core.h" #include "core/frontend/mic.h" -#include "core/settings.h" #include "ui_configure_audio.h" #if defined(__APPLE__) @@ -31,25 +32,30 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) ui->output_sink_combo_box->addItem(QString::fromUtf8(id)); } - ui->emulation_combo_box->addItem(tr("HLE (fast)")); - ui->emulation_combo_box->addItem(tr("LLE (accurate)")); - ui->emulation_combo_box->addItem(tr("LLE multi-core")); - ui->emulation_combo_box->setEnabled(!Core::System::GetInstance().IsPoweredOn()); + const bool is_running = Core::System::GetInstance().IsPoweredOn(); + ui->emulation_combo_box->setEnabled(!is_running); connect(ui->volume_slider, &QSlider::valueChanged, this, &ConfigureAudio::SetVolumeIndicatorText); ui->input_device_combo_box->clear(); ui->input_device_combo_box->addItem(tr("Default")); + #ifdef HAVE_CUBEB for (const auto& device : AudioCore::ListCubebInputDevices()) { ui->input_device_combo_box->addItem(QString::fromStdString(device)); } #endif + connect(ui->input_type_combo_box, qOverload(&QComboBox::currentIndexChanged), this, &ConfigureAudio::UpdateAudioInputDevices); + ui->volume_label->setVisible(Settings::IsConfiguringGlobal()); + ui->volume_combo_box->setVisible(!Settings::IsConfiguringGlobal()); + + SetupPerGameUI(); SetConfiguration(); + connect(ui->output_sink_combo_box, qOverload(&QComboBox::currentIndexChanged), this, &ConfigureAudio::UpdateAudioOutputDevices); } @@ -61,27 +67,35 @@ void ConfigureAudio::SetConfiguration() { // The device list cannot be pre-populated (nor listed) until the output sink is known. UpdateAudioOutputDevices(ui->output_sink_combo_box->currentIndex()); - SetAudioDeviceFromDeviceID(); - ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching); - ui->volume_slider->setValue( - static_cast(Settings::values.volume * ui->volume_slider->maximum())); + ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue()); + + const s32 volume = + static_cast(Settings::values.volume.GetValue() * ui->volume_slider->maximum()); + ui->volume_slider->setValue(volume); SetVolumeIndicatorText(ui->volume_slider->sliderPosition()); - int selection; - if (Settings::values.enable_dsp_lle) { - if (Settings::values.enable_dsp_lle_multithread) { - selection = 2; + if (!Settings::IsConfiguringGlobal()) { + if (Settings::values.volume.UsingGlobal()) { + ui->volume_combo_box->setCurrentIndex(0); + ui->volume_slider->setEnabled(false); } else { - selection = 1; + ui->volume_combo_box->setCurrentIndex(1); + ui->volume_slider->setEnabled(true); } + ConfigurationShared::SetHighlight(ui->volume_layout, + !Settings::values.volume.UsingGlobal()); + ConfigurationShared::SetHighlight(ui->widget_emulation, + !Settings::values.audio_emulation.UsingGlobal()); + ConfigurationShared::SetPerGameSetting(ui->emulation_combo_box, + &Settings::values.audio_emulation); } else { - selection = 0; + s32 selection = static_cast(Settings::values.audio_emulation.GetValue()); + ui->emulation_combo_box->setCurrentIndex(selection); } - ui->emulation_combo_box->setCurrentIndex(selection); - int index = static_cast(Settings::values.mic_input_type); + s32 index = static_cast(Settings::values.mic_input_type.GetValue()); ui->input_type_combo_box->setCurrentIndex(index); UpdateAudioInputDevices(index); @@ -90,7 +104,7 @@ void ConfigureAudio::SetConfiguration() { void ConfigureAudio::SetOutputSinkFromSinkID() { int new_sink_index = 0; - const QString sink_id = QString::fromStdString(Settings::values.sink_id); + const QString sink_id = QString::fromStdString(Settings::values.sink_id.GetValue()); for (int index = 0; index < ui->output_sink_combo_box->count(); index++) { if (ui->output_sink_combo_box->itemText(index) == sink_id) { new_sink_index = index; @@ -104,7 +118,7 @@ void ConfigureAudio::SetOutputSinkFromSinkID() { void ConfigureAudio::SetAudioDeviceFromDeviceID() { int new_device_index = -1; - const QString device_id = QString::fromStdString(Settings::values.audio_device_id); + const QString device_id = QString::fromStdString(Settings::values.audio_device_id.GetValue()); for (int index = 0; index < ui->audio_device_combo_box->count(); index++) { if (ui->audio_device_combo_box->itemText(index) == device_id) { new_device_index = index; @@ -120,24 +134,31 @@ 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(ui->volume_slider->sliderPosition()) / ui->volume_slider->maximum(); - Settings::values.enable_dsp_lle = ui->emulation_combo_box->currentIndex() != 0; - Settings::values.enable_dsp_lle_multithread = ui->emulation_combo_box->currentIndex() == 2; - Settings::values.mic_input_type = - static_cast(ui->input_type_combo_box->currentIndex()); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.enable_audio_stretching, + ui->toggle_audio_stretching, audio_stretching); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.audio_emulation, + ui->emulation_combo_box); + ConfigurationShared::ApplyPerGameSetting( + &Settings::values.volume, ui->volume_combo_box, [this](s32) { + return static_cast(ui->volume_slider->value()) / ui->volume_slider->maximum(); + }); - if (ui->input_device_combo_box->currentIndex() == DEFAULT_INPUT_DEVICE_INDEX) { - Settings::values.mic_input_device = Frontend::Mic::default_device_name; - } else { - Settings::values.mic_input_device = ui->input_device_combo_box->currentText().toStdString(); + if (Settings::IsConfiguringGlobal()) { + 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(); + Settings::values.mic_input_type = + static_cast(ui->input_type_combo_box->currentIndex()); + + if (ui->input_device_combo_box->currentIndex() == DEFAULT_INPUT_DEVICE_INDEX) { + Settings::values.mic_input_device = Frontend::Mic::default_device_name; + } else { + Settings::values.mic_input_device = + ui->input_device_combo_box->currentText().toStdString(); + } } } @@ -157,12 +178,41 @@ void ConfigureAudio::UpdateAudioInputDevices(int index) { AppleAuthorization::CheckAuthorizationForMicrophone(); } #endif - if (Settings::values.mic_input_device != Frontend::Mic::default_device_name) { + if (Settings::values.mic_input_device.GetValue() != Frontend::Mic::default_device_name) { ui->input_device_combo_box->setCurrentText( - QString::fromStdString(Settings::values.mic_input_device)); + QString::fromStdString(Settings::values.mic_input_device.GetValue())); } } void ConfigureAudio::RetranslateUI() { ui->retranslateUi(this); } + +void ConfigureAudio::SetupPerGameUI() { + if (Settings::IsConfiguringGlobal()) { + ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal()); + return; + } + + 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); + ui->input_type_label->setVisible(false); + ui->input_type_combo_box->setVisible(false); + ui->input_device_label->setVisible(false); + ui->input_device_combo_box->setVisible(false); + ui->microphone_layout->setVisible(false); + + connect(ui->volume_combo_box, qOverload(&QComboBox::activated), this, [this](int index) { + ui->volume_slider->setEnabled(index == 1); + ConfigurationShared::SetHighlight(ui->volume_layout, index == 1); + }); + + ConfigurationShared::SetColoredComboBox( + ui->emulation_combo_box, ui->widget_emulation, + static_cast(Settings::values.audio_emulation.GetValue(true))); + + ConfigurationShared::SetColoredTristate( + ui->toggle_audio_stretching, Settings::values.enable_audio_stretching, audio_stretching); +} diff --git a/src/citra_qt/configuration/configure_audio.h b/src/citra_qt/configuration/configure_audio.h index cdaf73819..514c7945c 100644 --- a/src/citra_qt/configuration/configure_audio.h +++ b/src/citra_qt/configuration/configure_audio.h @@ -11,6 +11,10 @@ namespace Ui { class ConfigureAudio; } +namespace ConfigurationShared { +enum class CheckState; +} + class ConfigureAudio : public QWidget { Q_OBJECT @@ -30,5 +34,8 @@ private: void SetAudioDeviceFromDeviceID(); void SetVolumeIndicatorText(int percentage); + void SetupPerGameUI(); + + ConfigurationShared::CheckState audio_stretching; std::unique_ptr ui; }; diff --git a/src/citra_qt/configuration/configure_audio.ui b/src/citra_qt/configuration/configure_audio.ui index 0eaaf39a2..3a1a28dd3 100644 --- a/src/citra_qt/configuration/configure_audio.ui +++ b/src/citra_qt/configuration/configure_audio.ui @@ -6,8 +6,8 @@ 0 0 - 329 - 344 + 696 + 527 @@ -18,26 +18,53 @@ - - - 0 - - - - - Emulation: - - - - - - - + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Emulation: + + + + + + + + HLE (fast) + + + + + LLE (accurate) + + + + + LLE multi-core + + + + + + - + - + Output Engine @@ -59,9 +86,9 @@ - + - + Audio Device @@ -73,72 +100,97 @@ - - - 0 - - - - - Volume: - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 0 - 0 - - - - 100 - - - 10 - - - Qt::Horizontal - - - - - - - - 32 - 0 - - - - 0 % - - - Qt::AlignCenter - - - - + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + Use global volume + + + + + Set volume: + + + + + + + + Volume: + + + + + + + Qt::Horizontal + + + + 30 + 20 + + + + + + + + + 0 + 0 + + + + 100 + + + 5 + + + Qt::Horizontal + + + + + + + + 32 + 0 + + + + 0 % + + + Qt::AlignCenter + + + + + - + Microphone @@ -146,7 +198,7 @@ - + Input Type @@ -176,7 +228,7 @@ - + Input Device diff --git a/src/citra_qt/configuration/configure_camera.cpp b/src/citra_qt/configuration/configure_camera.cpp index 1eb0c2604..79f21bb74 100644 --- a/src/citra_qt/configuration/configure_camera.cpp +++ b/src/citra_qt/configuration/configure_camera.cpp @@ -9,10 +9,10 @@ #include #include #include "citra_qt/configuration/configure_camera.h" +#include "common/settings.h" #include "core/frontend/camera/factory.h" #include "core/frontend/camera/interface.h" #include "core/hle/service/cam/cam.h" -#include "core/settings.h" #include "ui_configure_camera.h" #if defined(__APPLE__) diff --git a/src/citra_qt/configuration/configure_debug.cpp b/src/citra_qt/configuration/configure_debug.cpp index 798734e12..accc61b65 100644 --- a/src/citra_qt/configuration/configure_debug.cpp +++ b/src/citra_qt/configuration/configure_debug.cpp @@ -8,11 +8,9 @@ #include "citra_qt/debugger/console.h" #include "citra_qt/uisettings.h" #include "common/file_util.h" -#include "common/logging/backend.h" -#include "common/logging/filter.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "ui_configure_debug.h" ConfigureDebug::ConfigureDebug(QWidget* parent) @@ -32,13 +30,13 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) ConfigureDebug::~ConfigureDebug() = default; void ConfigureDebug::SetConfiguration() { - ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub); - ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub); - ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port); + ui->toggle_gdbstub->setChecked(Settings::values.use_gdbstub.GetValue()); + ui->gdbport_spinbox->setEnabled(Settings::values.use_gdbstub.GetValue()); + ui->gdbport_spinbox->setValue(Settings::values.gdbstub_port.GetValue()); ui->toggle_console->setEnabled(!Core::System::GetInstance().IsPoweredOn()); - ui->toggle_console->setChecked(UISettings::values.show_console); - ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); - ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit); + ui->toggle_console->setChecked(UISettings::values.show_console.GetValue()); + ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue())); + ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue()); } void ConfigureDebug::ApplyConfiguration() { @@ -48,7 +46,7 @@ void ConfigureDebug::ApplyConfiguration() { Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); Debugger::ToggleConsole(); Log::Filter filter; - filter.ParseFilterString(Settings::values.log_filter); + filter.ParseFilterString(Settings::values.log_filter.GetValue()); Log::SetGlobalFilter(filter); Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked(); } diff --git a/src/citra_qt/configuration/configure_dialog.cpp b/src/citra_qt/configuration/configure_dialog.cpp index 66eb3ec83..80670f2af 100644 --- a/src/citra_qt/configuration/configure_dialog.cpp +++ b/src/citra_qt/configuration/configure_dialog.cpp @@ -7,11 +7,13 @@ #include "citra_qt/configuration/config.h" #include "citra_qt/configuration/configure_dialog.h" #include "citra_qt/hotkeys.h" -#include "core/settings.h" +#include "common/settings.h" #include "ui_configure.h" ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, bool enable_web_config) : QDialog(parent), ui(std::make_unique()), registry(registry) { + Settings::SetConfiguringGlobal(true); + ui->setupUi(this); ui->hotkeysTab->Populate(registry); ui->webTab->SetWebServiceConfigEnabled(enable_web_config); diff --git a/src/citra_qt/configuration/configure_enhancements.cpp b/src/citra_qt/configuration/configure_enhancements.cpp index 0efb86fba..23f3b62d5 100644 --- a/src/citra_qt/configuration/configure_enhancements.cpp +++ b/src/citra_qt/configuration/configure_enhancements.cpp @@ -4,8 +4,8 @@ #include #include "citra_qt/configuration/configure_enhancements.h" +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "ui_configure_enhancements.h" #include "video_core/renderer_opengl/post_processing_opengl.h" #include "video_core/renderer_opengl/texture_filters/texture_filterer.h" @@ -21,7 +21,7 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent) ui->layoutBox->setEnabled(!Settings::values.custom_layout); - ui->resolution_factor_combobox->setEnabled(Settings::values.use_hw_renderer); + ui->resolution_factor_combobox->setEnabled(Settings::values.use_hw_renderer.GetValue()); connect(ui->render_3d_combobox, static_cast(&QComboBox::currentIndexChanged), this, @@ -50,27 +50,30 @@ ConfigureEnhancements::ConfigureEnhancements(QWidget* parent) } void ConfigureEnhancements::SetConfiguration() { - ui->resolution_factor_combobox->setCurrentIndex(Settings::values.resolution_factor); - ui->render_3d_combobox->setCurrentIndex(static_cast(Settings::values.render_3d)); - ui->factor_3d->setValue(Settings::values.factor_3d); - ui->mono_render_left_eye->setChecked(Settings::values.mono_render_left_eye); - updateShaders(Settings::values.render_3d); - ui->toggle_linear_filter->setChecked(Settings::values.filter_mode); + ui->resolution_factor_combobox->setCurrentIndex(Settings::values.resolution_factor.GetValue()); + ui->render_3d_combobox->setCurrentIndex( + static_cast(Settings::values.render_3d.GetValue())); + ui->factor_3d->setValue(Settings::values.factor_3d.GetValue()); + ui->mono_render_left_eye->setChecked(Settings::values.mono_render_left_eye.GetValue()); + updateShaders(Settings::values.render_3d.GetValue()); + ui->toggle_linear_filter->setChecked(Settings::values.filter_mode.GetValue()); int tex_filter_idx = ui->texture_filter_combobox->findText( - QString::fromStdString(Settings::values.texture_filter_name)); + QString::fromStdString(Settings::values.texture_filter_name.GetValue())); if (tex_filter_idx == -1) { ui->texture_filter_combobox->setCurrentIndex(0); } else { ui->texture_filter_combobox->setCurrentIndex(tex_filter_idx); } - ui->layout_combobox->setCurrentIndex(static_cast(Settings::values.layout_option)); - ui->swap_screen->setChecked(Settings::values.swap_screen); - ui->upright_screen->setChecked(Settings::values.upright_screen); - ui->toggle_dump_textures->setChecked(Settings::values.dump_textures); - ui->toggle_custom_textures->setChecked(Settings::values.custom_textures); - ui->toggle_preload_textures->setChecked(Settings::values.preload_textures); - bg_color = QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green, - Settings::values.bg_blue); + ui->layout_combobox->setCurrentIndex( + static_cast(Settings::values.layout_option.GetValue())); + ui->swap_screen->setChecked(Settings::values.swap_screen.GetValue()); + ui->upright_screen->setChecked(Settings::values.upright_screen.GetValue()); + ui->toggle_dump_textures->setChecked(Settings::values.dump_textures.GetValue()); + ui->toggle_custom_textures->setChecked(Settings::values.custom_textures.GetValue()); + ui->toggle_preload_textures->setChecked(Settings::values.preload_textures.GetValue()); + bg_color = + QColor::fromRgbF(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(), + Settings::values.bg_blue.GetValue()); QPixmap pixmap(ui->bg_button->size()); pixmap.fill(bg_color); const QIcon color_icon(pixmap); @@ -93,7 +96,7 @@ void ConfigureEnhancements::updateShaders(Settings::StereoRenderOption stereo_op for (const auto& shader : OpenGL::GetPostProcessingShaderList( stereo_option == Settings::StereoRenderOption::Anaglyph)) { ui->shader_combobox->addItem(QString::fromStdString(shader)); - if (Settings::values.pp_shader_name == shader) + if (Settings::values.pp_shader_name.GetValue() == shader) ui->shader_combobox->setCurrentIndex(ui->shader_combobox->count() - 1); } } diff --git a/src/citra_qt/configuration/configure_enhancements.h b/src/citra_qt/configuration/configure_enhancements.h index 3541ccf3d..59f9ba1ad 100644 --- a/src/citra_qt/configuration/configure_enhancements.h +++ b/src/citra_qt/configuration/configure_enhancements.h @@ -6,9 +6,10 @@ #include #include +#include "common/common_types.h" namespace Settings { -enum class StereoRenderOption; +enum class StereoRenderOption : u32; } namespace Ui { diff --git a/src/citra_qt/configuration/configure_general.cpp b/src/citra_qt/configuration/configure_general.cpp index 8fd404c80..daaa44821 100644 --- a/src/citra_qt/configuration/configure_general.cpp +++ b/src/citra_qt/configuration/configure_general.cpp @@ -6,10 +6,11 @@ #include #include #include +#include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configure_general.h" #include "citra_qt/uisettings.h" -#include "core/core.h" -#include "core/settings.h" +#include "common/file_util.h" +#include "common/settings.h" #include "ui_configure_general.h" // The QSlider doesn't have an easy way to set a custom step amount, @@ -31,14 +32,17 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) // Set a minimum width for the label to prevent the slider from changing size. // This scales across DPIs, and is acceptable for uncapitalized strings. ui->emulation_speed_display_label->setMinimumWidth(tr("unthrottled").size() * 6); + ui->emulation_speed_combo->setVisible(!Settings::IsConfiguringGlobal()); + ui->screenshot_combo->setVisible(!Settings::IsConfiguringGlobal()); + SetupPerGameUI(); SetConfiguration(); ui->updateBox->setVisible(UISettings::values.updater_found); connect(ui->button_reset_defaults, &QPushButton::clicked, this, &ConfigureGeneral::ResetDefaults); - connect(ui->frame_limit, &QSlider::valueChanged, [&](int value) { + connect(ui->frame_limit, &QSlider::valueChanged, this, [&](int value) { if (value == ui->frame_limit->maximum()) { ui->emulation_speed_display_label->setText(tr("unthrottled")); } else { @@ -49,17 +53,6 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) } }); - connect(ui->frame_limit_alternate, &QSlider::valueChanged, [&](int value) { - if (value == ui->frame_limit_alternate->maximum()) { - ui->emulation_speed_alternate_display_label->setText(tr("unthrottled")); - } else { - ui->emulation_speed_alternate_display_label->setText( - QStringLiteral("%1%") - .arg(SliderToSettings(value)) - .rightJustified(tr("unthrottled").size())); - } - }); - connect(ui->change_screenshot_dir, &QToolButton::clicked, this, [this] { const QString dir_path = QFileDialog::getExistingDirectory( this, tr("Select Screenshot Directory"), ui->screenshot_dir_path->text(), @@ -73,20 +66,21 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) ConfigureGeneral::~ConfigureGeneral() = default; void ConfigureGeneral::SetConfiguration() { - ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); - ui->toggle_background_pause->setChecked(UISettings::values.pause_when_in_background); - ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse); + if (Settings::IsConfiguringGlobal()) { + ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing.GetValue()); + ui->toggle_background_pause->setChecked( + UISettings::values.pause_when_in_background.GetValue()); + ui->toggle_hide_mouse->setChecked(UISettings::values.hide_mouse.GetValue()); - ui->toggle_update_check->setChecked(UISettings::values.check_for_update_on_start); - ui->toggle_auto_update->setChecked(UISettings::values.update_on_close); + ui->toggle_update_check->setChecked( + UISettings::values.check_for_update_on_start.GetValue()); + ui->toggle_auto_update->setChecked(UISettings::values.update_on_close.GetValue()); + } - // The first item is "auto-select" with actual value -1, so plus one here will do the trick - ui->region_combobox->setCurrentIndex(Settings::values.region_value + 1); - - if (Settings::values.frame_limit == 0) { + if (Settings::values.frame_limit.GetValue() == 0) { ui->frame_limit->setValue(ui->frame_limit->maximum()); } else { - ui->frame_limit->setValue(SettingsToSlider(Settings::values.frame_limit)); + ui->frame_limit->setValue(SettingsToSlider(Settings::values.frame_limit.GetValue())); } if (ui->frame_limit->value() == ui->frame_limit->maximum()) { ui->emulation_speed_display_label->setText(tr("unthrottled")); @@ -97,32 +91,48 @@ void ConfigureGeneral::SetConfiguration() { .rightJustified(tr("unthrottled").size())); } - ui->toggle_alternate_speed->setChecked(Settings::values.use_frame_limit_alternate); - - if (Settings::values.frame_limit_alternate == 0) { - ui->frame_limit_alternate->setValue(ui->frame_limit_alternate->maximum()); + if (!Settings::IsConfiguringGlobal()) { + if (Settings::values.frame_limit.UsingGlobal()) { + ui->emulation_speed_combo->setCurrentIndex(0); + ui->frame_limit->setEnabled(false); + } else { + ui->emulation_speed_combo->setCurrentIndex(1); + ui->frame_limit->setEnabled(true); + } + if (UISettings::values.screenshot_path.UsingGlobal()) { + ui->screenshot_combo->setCurrentIndex(0); + ui->screenshot_dir_path->setEnabled(false); + ui->change_screenshot_dir->setEnabled(false); + } else { + ui->screenshot_combo->setCurrentIndex(1); + ui->screenshot_dir_path->setEnabled(true); + ui->change_screenshot_dir->setEnabled(true); + } + ConfigurationShared::SetHighlight(ui->widget_screenshot, + !UISettings::values.screenshot_path.UsingGlobal()); + ConfigurationShared::SetHighlight(ui->emulation_speed_layout, + !Settings::values.frame_limit.UsingGlobal()); + ConfigurationShared::SetHighlight(ui->widget_region, + !Settings::values.region_value.UsingGlobal()); + const bool is_region_global = Settings::values.region_value.UsingGlobal(); + ui->region_combobox->setCurrentIndex( + is_region_global ? ConfigurationShared::USE_GLOBAL_INDEX + : static_cast(Settings::values.region_value.GetValue()) + + ConfigurationShared::USE_GLOBAL_OFFSET + 1); } else { - ui->frame_limit_alternate->setValue( - SettingsToSlider(Settings::values.frame_limit_alternate)); - } - if (ui->frame_limit_alternate->value() == ui->frame_limit_alternate->maximum()) { - ui->emulation_speed_alternate_display_label->setText(tr("unthrottled")); - } else { - ui->emulation_speed_alternate_display_label->setText( - QStringLiteral("%1%") - .arg(SliderToSettings(ui->frame_limit_alternate->value())) - .rightJustified(tr("unthrottled").size())); + // The first item is "auto-select" with actual value -1, so plus one here will do the trick + ui->region_combobox->setCurrentIndex(Settings::values.region_value.GetValue() + 1); } - QString screenshot_path = UISettings::values.screenshot_path; - if (screenshot_path.isEmpty()) { - screenshot_path = - QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir)); - screenshot_path.append(QStringLiteral("screenshots/")); - FileUtil::CreateFullPath(screenshot_path.toStdString()); + UISettings::values.screenshot_path.SetGlobal(ui->screenshot_combo->currentIndex() == + ConfigurationShared::USE_GLOBAL_INDEX); + std::string screenshot_path = UISettings::values.screenshot_path.GetValue(); + if (screenshot_path.empty()) { + screenshot_path = FileUtil::GetUserPath(FileUtil::UserPath::UserDir) + "screenshots/"; + FileUtil::CreateFullPath(screenshot_path); UISettings::values.screenshot_path = screenshot_path; } - ui->screenshot_dir_path->setText(screenshot_path); + ui->screenshot_dir_path->setText(QString::fromStdString(screenshot_path)); } void ConfigureGeneral::ResetDefaults() { @@ -139,31 +149,57 @@ void ConfigureGeneral::ResetDefaults() { } void ConfigureGeneral::ApplyConfiguration() { - UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); - UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); - UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.region_value, ui->region_combobox, + [](s32 index) { return index - 1; }); - UISettings::values.check_for_update_on_start = ui->toggle_update_check->isChecked(); - UISettings::values.update_on_close = ui->toggle_auto_update->isChecked(); + ConfigurationShared::ApplyPerGameSetting( + &Settings::values.frame_limit, ui->emulation_speed_combo, [this](s32) { + const bool is_maximum = ui->frame_limit->value() == ui->frame_limit->maximum(); + return is_maximum ? 0 : SliderToSettings(ui->frame_limit->value()); + }); - UISettings::values.screenshot_path = ui->screenshot_dir_path->text(); + ConfigurationShared::ApplyPerGameSetting( + &UISettings::values.screenshot_path, ui->screenshot_combo, + [this](s32) { return ui->screenshot_dir_path->text().toStdString(); }); - Settings::values.region_value = ui->region_combobox->currentIndex() - 1; + if (Settings::IsConfiguringGlobal()) { + UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked(); + UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked(); + UISettings::values.hide_mouse = ui->toggle_hide_mouse->isChecked(); - if (ui->frame_limit->value() == ui->frame_limit->maximum()) { - Settings::values.frame_limit = 0; - } else { - Settings::values.frame_limit = SliderToSettings(ui->frame_limit->value()); - } - Settings::values.use_frame_limit_alternate = ui->toggle_alternate_speed->isChecked(); - if (ui->frame_limit_alternate->value() == ui->frame_limit_alternate->maximum()) { - Settings::values.frame_limit_alternate = 0; - } else { - Settings::values.frame_limit_alternate = - SliderToSettings(ui->frame_limit_alternate->value()); + UISettings::values.check_for_update_on_start = ui->toggle_update_check->isChecked(); + UISettings::values.update_on_close = ui->toggle_auto_update->isChecked(); } } void ConfigureGeneral::RetranslateUI() { ui->retranslateUi(this); } + +void ConfigureGeneral::SetupPerGameUI() { + if (Settings::IsConfiguringGlobal()) { + ui->region_combobox->setEnabled(Settings::values.region_value.UsingGlobal()); + ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal()); + return; + } + + connect(ui->emulation_speed_combo, qOverload(&QComboBox::activated), this, + [this](int index) { + ui->frame_limit->setEnabled(index == 1); + ConfigurationShared::SetHighlight(ui->emulation_speed_layout, index == 1); + }); + + connect(ui->screenshot_combo, qOverload(&QComboBox::activated), this, [this](int index) { + ui->screenshot_dir_path->setEnabled(index == 1); + ui->change_screenshot_dir->setEnabled(index == 1); + ConfigurationShared::SetHighlight(ui->widget_screenshot, index == 1); + }); + + ui->general_group->setVisible(false); + ui->updateBox->setVisible(false); + ui->button_reset_defaults->setVisible(false); + + ConfigurationShared::SetColoredComboBox( + ui->region_combobox, ui->widget_region, + static_cast(Settings::values.region_value.GetValue(true) + 1)); +} diff --git a/src/citra_qt/configuration/configure_general.h b/src/citra_qt/configuration/configure_general.h index d3bae474a..3c6cef219 100644 --- a/src/citra_qt/configuration/configure_general.h +++ b/src/citra_qt/configuration/configure_general.h @@ -25,6 +25,8 @@ public: void RetranslateUI(); void SetConfiguration(); + void SetupPerGameUI(); + private: std::unique_ptr ui; }; diff --git a/src/citra_qt/configuration/configure_general.ui b/src/citra_qt/configuration/configure_general.ui index 5cb817e1d..4452d69e4 100644 --- a/src/citra_qt/configuration/configure_general.ui +++ b/src/citra_qt/configuration/configure_general.ui @@ -6,8 +6,8 @@ 0 0 - 408 - 436 + 524 + 578 @@ -17,7 +17,7 @@ - + General @@ -70,144 +70,152 @@ - + Emulation - - - - - - Auto-select + + + + + + 0 - - - - JPN + + 0 - - - - USA + + 0 - - - - EUR + + 0 - - - - AUS - - - - - CHN - - - - - KOR - - - - - TWN - - + + + + Region: + + + + + + + + Auto-select + + + + + JPN + + + + + USA + + + + + EUR + + + + + AUS + + + + + CHN + + + + + KOR + + + + + TWN + + + + + - - - - Emulation Speed: - - - - - - - Use Alternate Speed: - - - - - - - 0 - - - 199 - - - 5 - - - 15 - - - 19 - - - Qt::Horizontal - - - QSlider::TicksBelow - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Region: - - - - - - - 0 - - - 199 - - - 5 - - - 15 - - - 39 - - - Qt::Horizontal - - - QSlider::TicksBelow - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + Use global emulation speed + + + + + Set emulation speed: + + + + + + + + Emulation Speed: + + + + + + + 0 + + + 199 + + + 5 + + + 15 + + + 19 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + @@ -218,23 +226,57 @@ Screenshots - - - - - Save Screenshots To - - - - - - - - - - - ... - + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + Use global screenshot path + + + + + Set screenshot path: + + + + + + + + Save Screenshots To + + + + + + + + + + ... + + + + @@ -270,12 +312,6 @@ toggle_hide_mouse toggle_update_check toggle_auto_update - region_combobox - frame_limit - toggle_alternate_speed - frame_limit_alternate - screenshot_dir_path - change_screenshot_dir button_reset_defaults diff --git a/src/citra_qt/configuration/configure_graphics.cpp b/src/citra_qt/configuration/configure_graphics.cpp index 75602a8b4..58c281490 100644 --- a/src/citra_qt/configuration/configure_graphics.cpp +++ b/src/citra_qt/configuration/configure_graphics.cpp @@ -6,22 +6,25 @@ #ifdef __APPLE__ #include #endif +#include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configure_graphics.h" +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "ui_configure_graphics.h" -#include "video_core/renderer_opengl/post_processing_opengl.h" ConfigureGraphics::ConfigureGraphics(QWidget* parent) : QWidget(parent), ui(std::make_unique()) { ui->setupUi(this); + + SetupPerGameUI(); SetConfiguration(); - ui->hw_renderer_group->setEnabled(ui->toggle_hw_renderer->isChecked()); + ui->hw_renderer_group->setEnabled(ui->hw_renderer_group->isEnabled() && + ui->toggle_hw_renderer->isChecked()); ui->toggle_vsync_new->setEnabled(!Core::System::GetInstance().IsPoweredOn()); connect(ui->toggle_hw_renderer, &QCheckBox::toggled, this, [this] { - auto checked = ui->toggle_hw_renderer->isChecked(); + const bool checked = ui->toggle_hw_renderer->isChecked(); ui->hw_renderer_group->setEnabled(checked); ui->toggle_disk_shader_cache->setEnabled(checked && ui->toggle_hw_shader->isChecked()); }); @@ -31,7 +34,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) ui->toggle_hw_shader->isChecked()); connect(ui->toggle_hw_shader, &QCheckBox::toggled, this, [this] { - auto checked = ui->toggle_hw_shader->isChecked(); + const bool checked = ui->toggle_hw_shader->isChecked(); ui->hw_shader_group->setEnabled(checked); ui->toggle_disk_shader_cache->setEnabled(checked); }); @@ -62,25 +65,67 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) ConfigureGraphics::~ConfigureGraphics() = default; void ConfigureGraphics::SetConfiguration() { - ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer); - ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader); - ui->toggle_separable_shader->setChecked(Settings::values.separable_shader); - ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul); - ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit); - ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); - ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new); + ui->toggle_hw_renderer->setChecked(Settings::values.use_hw_renderer.GetValue()); + ui->toggle_hw_shader->setChecked(Settings::values.use_hw_shader.GetValue()); + ui->toggle_separable_shader->setChecked(Settings::values.separable_shader.GetValue()); + ui->toggle_accurate_mul->setChecked(Settings::values.shaders_accurate_mul.GetValue()); + ui->toggle_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue()); + ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new.GetValue()); + + if (Settings::IsConfiguringGlobal()) { + ui->toggle_shader_jit->setChecked(Settings::values.use_shader_jit.GetValue()); + } } void ConfigureGraphics::ApplyConfiguration() { - Settings::values.use_hw_renderer = ui->toggle_hw_renderer->isChecked(); - Settings::values.use_hw_shader = ui->toggle_hw_shader->isChecked(); - Settings::values.separable_shader = ui->toggle_separable_shader->isChecked(); - Settings::values.shaders_accurate_mul = ui->toggle_accurate_mul->isChecked(); - Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked(); - Settings::values.use_disk_shader_cache = ui->toggle_disk_shader_cache->isChecked(); - Settings::values.use_vsync_new = ui->toggle_vsync_new->isChecked(); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_renderer, + ui->toggle_hw_renderer, use_hw_renderer); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_hw_shader, ui->toggle_hw_shader, + use_hw_shader); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.separable_shader, + ui->toggle_separable_shader, separable_shader); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.shaders_accurate_mul, + ui->toggle_accurate_mul, shaders_accurate_mul); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_disk_shader_cache, + ui->toggle_disk_shader_cache, use_disk_shader_cache); + ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_vsync_new, ui->toggle_vsync_new, + use_vsync_new); + + if (Settings::IsConfiguringGlobal()) { + Settings::values.use_shader_jit = ui->toggle_shader_jit->isChecked(); + } } void ConfigureGraphics::RetranslateUI() { ui->retranslateUi(this); } + +void ConfigureGraphics::SetupPerGameUI() { + // Block the global settings if a game is currently running that overrides them + if (Settings::IsConfiguringGlobal()) { + ui->toggle_hw_renderer->setEnabled(Settings::values.use_hw_renderer.UsingGlobal()); + ui->toggle_hw_shader->setEnabled(Settings::values.use_hw_shader.UsingGlobal()); + ui->toggle_separable_shader->setEnabled(Settings::values.separable_shader.UsingGlobal()); + ui->toggle_accurate_mul->setEnabled(Settings::values.shaders_accurate_mul.UsingGlobal()); + ui->toggle_disk_shader_cache->setEnabled( + Settings::values.use_disk_shader_cache.UsingGlobal()); + ui->toggle_vsync_new->setEnabled(Settings::values.use_vsync_new.UsingGlobal()); + return; + } + + ui->toggle_shader_jit->setVisible(false); + + ConfigurationShared::SetColoredTristate(ui->toggle_hw_renderer, + Settings::values.use_hw_renderer, use_hw_renderer); + ConfigurationShared::SetColoredTristate(ui->toggle_hw_shader, Settings::values.use_hw_shader, + use_hw_shader); + ConfigurationShared::SetColoredTristate(ui->toggle_separable_shader, + Settings::values.separable_shader, separable_shader); + ConfigurationShared::SetColoredTristate( + ui->toggle_accurate_mul, Settings::values.shaders_accurate_mul, shaders_accurate_mul); + ConfigurationShared::SetColoredTristate(ui->toggle_disk_shader_cache, + Settings::values.use_disk_shader_cache, + use_disk_shader_cache); + ConfigurationShared::SetColoredTristate(ui->toggle_vsync_new, Settings::values.use_vsync_new, + use_vsync_new); +} diff --git a/src/citra_qt/configuration/configure_graphics.h b/src/citra_qt/configuration/configure_graphics.h index 9ed932090..2517e1553 100644 --- a/src/citra_qt/configuration/configure_graphics.h +++ b/src/citra_qt/configuration/configure_graphics.h @@ -11,6 +11,10 @@ namespace Ui { class ConfigureGraphics; } +namespace ConfigurationShared { +enum class CheckState; +} + class ConfigureGraphics : public QWidget { Q_OBJECT @@ -24,6 +28,14 @@ public: void UpdateBackgroundColorButton(const QColor& color); + void SetupPerGameUI(); + + ConfigurationShared::CheckState use_hw_renderer; + ConfigurationShared::CheckState use_hw_shader; + ConfigurationShared::CheckState separable_shader; + ConfigurationShared::CheckState shaders_accurate_mul; + ConfigurationShared::CheckState use_disk_shader_cache; + ConfigurationShared::CheckState use_vsync_new; std::unique_ptr ui; QColor bg_color; }; diff --git a/src/citra_qt/configuration/configure_hotkeys.cpp b/src/citra_qt/configuration/configure_hotkeys.cpp index 1479283f0..287e7baa4 100644 --- a/src/citra_qt/configuration/configure_hotkeys.cpp +++ b/src/citra_qt/configuration/configure_hotkeys.cpp @@ -7,7 +7,7 @@ #include "citra_qt/configuration/configure_hotkeys.h" #include "citra_qt/hotkeys.h" #include "citra_qt/util/sequence_dialog/sequence_dialog.h" -#include "core/settings.h" +#include "common/settings.h" #include "ui_configure_hotkeys.h" ConfigureHotkeys::ConfigureHotkeys(QWidget* parent) diff --git a/src/citra_qt/configuration/configure_input.h b/src/citra_qt/configuration/configure_input.h index a08510fb1..b6867a39c 100644 --- a/src/citra_qt/configuration/configure_input.h +++ b/src/citra_qt/configuration/configure_input.h @@ -13,7 +13,7 @@ #include #include #include "common/param_package.h" -#include "core/settings.h" +#include "common/settings.h" #include "input_common/main.h" class QKeyEvent; diff --git a/src/citra_qt/configuration/configure_motion_touch.h b/src/citra_qt/configuration/configure_motion_touch.h index 2aefd393e..3b10f752a 100644 --- a/src/citra_qt/configuration/configure_motion_touch.h +++ b/src/citra_qt/configuration/configure_motion_touch.h @@ -7,7 +7,7 @@ #include #include #include "common/param_package.h" -#include "core/settings.h" +#include "common/settings.h" #include "input_common/main.h" #include "input_common/udp/udp.h" diff --git a/src/citra_qt/configuration/configure_per_game.cpp b/src/citra_qt/configuration/configure_per_game.cpp new file mode 100644 index 000000000..63421550c --- /dev/null +++ b/src/citra_qt/configuration/configure_per_game.cpp @@ -0,0 +1,128 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include "citra_qt/configuration/config.h" +#include "citra_qt/configuration/configure_audio.h" +#include "citra_qt/configuration/configure_general.h" +#include "citra_qt/configuration/configure_graphics.h" +#include "citra_qt/configuration/configure_per_game.h" +#include "citra_qt/configuration/configure_system.h" +#include "citra_qt/util/util.h" +#include "core/core.h" +#include "core/loader/loader.h" +#include "core/loader/smdh.h" +#include "ui_configure_per_game.h" + +ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name, + Core::System& system_) + : QDialog(parent), ui(std::make_unique()), + filename{file_name.toStdString()}, title_id{title_id_}, system{system_} { + const auto config_file_name = title_id == 0 ? filename : fmt::format("{:016X}", title_id); + game_config = std::make_unique(config_file_name, Config::ConfigType::PerGameConfig); + + audio_tab = std::make_unique(this); + general_tab = std::make_unique(this); + graphics_tab = std::make_unique(this); + system_tab = std::make_unique(this); + + ui->setupUi(this); + + ui->tabWidget->addTab(general_tab.get(), tr("General")); + ui->tabWidget->addTab(system_tab.get(), tr("System")); + ui->tabWidget->addTab(graphics_tab.get(), tr("Graphics")); + ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); + + setFocusPolicy(Qt::ClickFocus); + setWindowTitle(tr("Properties")); + // remove Help question mark button from the title bar + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + scene = new QGraphicsScene; + ui->icon_view->setScene(scene); + + if (system.IsPoweredOn()) { + QPushButton* apply_button = ui->buttonBox->addButton(QDialogButtonBox::Apply); + connect(apply_button, &QAbstractButton::clicked, this, + &ConfigurePerGame::HandleApplyButtonClicked); + } + + LoadConfiguration(); +} + +ConfigurePerGame::~ConfigurePerGame() = default; + +void ConfigurePerGame::ApplyConfiguration() { + general_tab->ApplyConfiguration(); + system_tab->ApplyConfiguration(); + graphics_tab->ApplyConfiguration(); + audio_tab->ApplyConfiguration(); + + 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::HandleApplyButtonClicked() { + ApplyConfiguration(); +} + +static QPixmap GetQPixmapFromSMDH(std::vector& smdh_data) { + Loader::SMDH smdh; + memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH)); + + bool large = true; + std::vector icon_data = smdh.GetIcon(large); + const uchar* data = reinterpret_cast(icon_data.data()); + int size = large ? 48 : 24; + QImage icon(data, size, size, QImage::Format::Format_RGB16); + return QPixmap::fromImage(icon); +} + +void ConfigurePerGame::LoadConfiguration() { + if (filename.empty()) { + return; + } + + ui->display_title_id->setText( + QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper()); + + const auto loader = Loader::GetLoader(filename); + + std::string title; + if (loader->ReadTitle(title) == Loader::ResultStatus::Success) + ui->display_name->setText(QString::fromStdString(title)); + + std::vector bytes; + if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) { + scene->clear(); + + QPixmap map = GetQPixmapFromSMDH(bytes); + scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(), + Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + } + + ui->display_filepath->setText(QString::fromStdString(filename)); + + ui->display_format->setText( + QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))); + + const auto valueText = ReadableByteSize(FileUtil::GetSize(filename)); + ui->display_size->setText(valueText); +} diff --git a/src/citra_qt/configuration/configure_per_game.h b/src/citra_qt/configuration/configure_per_game.h new file mode 100644 index 000000000..9b1b14306 --- /dev/null +++ b/src/citra_qt/configuration/configure_per_game.h @@ -0,0 +1,66 @@ +// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include "citra_qt/configuration/config.h" + +namespace Core { +class System; +} + +class ConfigureAudio; +class ConfigureGeneral; +class ConfigureGraphics; +class ConfigureSystem; + +class QGraphicsScene; +class QStandardItem; +class QStandardItemModel; +class QTreeView; +class QVBoxLayout; + +namespace Ui { +class ConfigurePerGame; +} + +class ConfigurePerGame : public QDialog { + Q_OBJECT + +public: + // Cannot use std::filesystem::path due to https://bugreports.qt.io/browse/QTBUG-73263 + explicit ConfigurePerGame(QWidget* parent, u64 title_id_, const QString& file_name, + Core::System& system_); + ~ConfigurePerGame() override; + + /// Loads all button configurations to settings file + void LoadConfiguration(); + + /// Save all button configurations to settings file + void ApplyConfiguration(); + +private: + void changeEvent(QEvent* event) override; + void RetranslateUI(); + + void HandleApplyButtonClicked(); + + std::unique_ptr ui; + std::string filename; + u64 title_id; + + QGraphicsScene* scene; + + std::unique_ptr game_config; + + Core::System& system; + + std::unique_ptr audio_tab; + std::unique_ptr general_tab; + std::unique_ptr graphics_tab; + std::unique_ptr system_tab; +}; diff --git a/src/citra_qt/configuration/configure_per_game.ui b/src/citra_qt/configuration/configure_per_game.ui new file mode 100644 index 000000000..a193afd65 --- /dev/null +++ b/src/citra_qt/configuration/configure_per_game.ui @@ -0,0 +1,264 @@ + + + ConfigurePerGame + + + + 0 + 0 + 900 + 661 + + + + + 900 + 0 + + + + Dialog + + + + + + + + + 0 + 0 + + + + Info + + + + + + + + + 0 + 0 + + + + + 128 + 128 + + + + + 128 + 128 + + + + Qt::ScrollBarAlwaysOff + + + Qt::ScrollBarAlwaysOff + + + false + + + + + + + + + + + Size + + + + + + + true + + + + 160 + 0 + + + + true + + + + + + + Format + + + + + + + Name + + + + + + + true + + + true + + + + + + + Filepath + + + + + + + Title ID + + + + + + + true + + + true + + + + + + + true + + + true + + + + + + + true + + + true + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + true + + + -1 + + + true + + + false + + + false + + + + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + ConfigurePerGame + accept() + + + 20 + 20 + + + 20 + 20 + + + + + buttonBox + rejected() + ConfigurePerGame + reject() + + + 20 + 20 + + + 20 + 20 + + + + + diff --git a/src/citra_qt/configuration/configure_storage.cpp b/src/citra_qt/configuration/configure_storage.cpp index 043254350..694395ead 100644 --- a/src/citra_qt/configuration/configure_storage.cpp +++ b/src/citra_qt/configuration/configure_storage.cpp @@ -6,8 +6,8 @@ #include #include #include "citra_qt/configuration/configure_storage.h" +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "ui_configure_storage.h" ConfigureStorage::ConfigureStorage(QWidget* parent) @@ -60,7 +60,7 @@ ConfigureStorage::ConfigureStorage(QWidget* parent) ConfigureStorage::~ConfigureStorage() = default; void ConfigureStorage::SetConfiguration() { - ui->nand_group->setVisible(Settings::values.use_custom_storage); + ui->nand_group->setVisible(Settings::values.use_custom_storage.GetValue()); QString nand_path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); ui->nand_dir_path->setText(nand_path); ui->open_nand_dir->setEnabled(!nand_path.isEmpty()); @@ -71,8 +71,8 @@ void ConfigureStorage::SetConfiguration() { ui->sdmc_dir_path->setText(sdmc_path); ui->open_sdmc_dir->setEnabled(!sdmc_path.isEmpty()); - ui->toggle_virtual_sd->setChecked(Settings::values.use_virtual_sd); - ui->toggle_custom_storage->setChecked(Settings::values.use_custom_storage); + ui->toggle_virtual_sd->setChecked(Settings::values.use_virtual_sd.GetValue()); + ui->toggle_custom_storage->setChecked(Settings::values.use_custom_storage.GetValue()); ui->storage_group->setEnabled(!Core::System::GetInstance().IsPoweredOn()); } diff --git a/src/citra_qt/configuration/configure_system.cpp b/src/citra_qt/configuration/configure_system.cpp index 11b4fe77b..cfde68c2d 100644 --- a/src/citra_qt/configuration/configure_system.cpp +++ b/src/citra_qt/configuration/configure_system.cpp @@ -4,12 +4,12 @@ #include #include +#include "citra_qt/configuration/configuration_shared.h" #include "citra_qt/configuration/configure_system.h" -#include "citra_qt/uisettings.h" +#include "common/settings.h" #include "core/core.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/ptm/ptm.h" -#include "core/settings.h" #include "ui_configure_system.h" static const std::array days_in_month = {{ @@ -249,10 +249,14 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) // This scales across DPIs. (This value should be enough for "xxx%") ui->clock_display_label->setMinimumWidth(40); - connect(ui->slider_clock_speed, &QSlider::valueChanged, [&](int value) { + connect(ui->slider_clock_speed, &QSlider::valueChanged, this, [&](int value) { ui->clock_display_label->setText(QStringLiteral("%1%").arg(SliderToSettings(value))); }); + ui->clock_speed_label->setVisible(Settings::IsConfiguringGlobal()); + ui->clock_speed_combo->setVisible(!Settings::IsConfiguringGlobal()); + + SetupPerGameUI(); ConfigureTime(); } @@ -261,12 +265,12 @@ ConfigureSystem::~ConfigureSystem() = default; void ConfigureSystem::SetConfiguration() { enabled = !Core::System::GetInstance().IsPoweredOn(); - ui->combo_init_clock->setCurrentIndex(static_cast(Settings::values.init_clock)); + ui->combo_init_clock->setCurrentIndex(static_cast(Settings::values.init_clock.GetValue())); QDateTime date_time; - date_time.setTime_t(Settings::values.init_time); + date_time.setTime_t(Settings::values.init_time.GetValue()); ui->edit_init_time->setDateTime(date_time); - long long init_time_offset = Settings::values.init_time_offset; + long long init_time_offset = Settings::values.init_time_offset.GetValue(); long long days_offset = init_time_offset / 86400; ui->edit_init_time_offset_days->setValue(days_offset); @@ -287,20 +291,29 @@ void ConfigureSystem::SetConfiguration() { ui->label_disable_info->hide(); } - ui->slider_clock_speed->setValue(SettingsToSlider(Settings::values.cpu_clock_percentage)); - ui->clock_display_label->setText( - QStringLiteral("%1%").arg(Settings::values.cpu_clock_percentage)); + if (!Settings::IsConfiguringGlobal()) { + if (Settings::values.cpu_clock_percentage.UsingGlobal()) { + ui->clock_speed_combo->setCurrentIndex(0); + ui->slider_clock_speed->setEnabled(false); + } else { + ui->clock_speed_combo->setCurrentIndex(1); + ui->slider_clock_speed->setEnabled(true); + } + ConfigurationShared::SetHighlight(ui->clock_speed_widget, + !Settings::values.cpu_clock_percentage.UsingGlobal()); + } - ui->toggle_new_3ds->setChecked(Settings::values.is_new_3ds); + ui->slider_clock_speed->setValue( + SettingsToSlider(Settings::values.cpu_clock_percentage.GetValue())); + ui->clock_display_label->setText( + QStringLiteral("%1%").arg(Settings::values.cpu_clock_percentage.GetValue())); + ui->toggle_new_3ds->setChecked(Settings::values.is_new_3ds.GetValue()); } void ConfigureSystem::ReadSystemSettings() { // set username username = cfg->GetUsername(); - // TODO(wwylele): Use this when we move to Qt 5.5 - // ui->edit_username->setText(QString::fromStdU16String(username)); - ui->edit_username->setText( - QString::fromUtf16(reinterpret_cast(username.data()))); + ui->edit_username->setText(QString::fromStdU16String(username)); // set birthday std::tie(birthmonth, birthday) = cfg->GetBirthday(); @@ -337,32 +350,29 @@ void ConfigureSystem::ApplyConfiguration() { bool modified = false; // apply username - // TODO(wwylele): Use this when we move to Qt 5.5 - // std::u16string new_username = ui->edit_username->text().toStdU16String(); - std::u16string new_username( - reinterpret_cast(ui->edit_username->text().utf16())); + std::u16string new_username = ui->edit_username->text().toStdU16String(); if (new_username != username) { cfg->SetUsername(new_username); modified = true; } // apply birthday - int new_birthmonth = ui->combo_birthmonth->currentIndex() + 1; - int new_birthday = ui->combo_birthday->currentIndex() + 1; + s32 new_birthmonth = ui->combo_birthmonth->currentIndex() + 1; + s32 new_birthday = ui->combo_birthday->currentIndex() + 1; if (birthmonth != new_birthmonth || birthday != new_birthday) { cfg->SetBirthday(new_birthmonth, new_birthday); modified = true; } // apply language - int new_language = ui->combo_language->currentIndex(); + s32 new_language = ui->combo_language->currentIndex(); if (language_index != new_language) { cfg->SetSystemLanguage(static_cast(new_language)); modified = true; } // apply sound - int new_sound = ui->combo_sound->currentIndex(); + s32 new_sound = ui->combo_sound->currentIndex(); if (sound_index != new_sound) { cfg->SetSoundOutputMode(static_cast(new_sound)); modified = true; @@ -386,6 +396,9 @@ void ConfigureSystem::ApplyConfiguration() { cfg->UpdateConfigNANDSavegame(); } + ConfigurationShared::ApplyPerGameSetting(&Settings::values.is_new_3ds, ui->toggle_new_3ds, + is_new_3ds); + Settings::values.init_clock = static_cast(ui->combo_init_clock->currentIndex()); Settings::values.init_time = ui->edit_init_time->dateTime().toTime_t(); @@ -398,11 +411,11 @@ void ConfigureSystem::ApplyConfiguration() { } Settings::values.init_time_offset = time_offset_days + time_offset_time; - Settings::values.is_new_3ds = ui->toggle_new_3ds->isChecked(); } - Settings::values.cpu_clock_percentage = SliderToSettings(ui->slider_clock_speed->value()); - Settings::Apply(); + ConfigurationShared::ApplyPerGameSetting( + &Settings::values.cpu_clock_percentage, ui->clock_speed_combo, + [this](s32) { return SliderToSettings(ui->slider_clock_speed->value()); }); } void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) { @@ -410,10 +423,10 @@ void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) { return; // store current day selection - int birthday_index = ui->combo_birthday->currentIndex(); + s32 birthday_index = ui->combo_birthday->currentIndex(); // get number of days in the new selected month - int days = days_in_month[birthmonth_index]; + s32 days = days_in_month[birthmonth_index]; // if the selected day is out of range, // reset it to 1st @@ -422,7 +435,7 @@ void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) { // update the day combo box ui->combo_birthday->clear(); - for (int i = 1; i <= days; ++i) { + for (s32 i = 1; i <= days; ++i) { ui->combo_birthday->addItem(QString::number(i)); } @@ -442,15 +455,16 @@ void ConfigureSystem::ConfigureTime() { } void ConfigureSystem::UpdateInitTime(int init_clock) { + const bool is_global = Settings::IsConfiguringGlobal(); const bool is_fixed_time = static_cast(init_clock) == Settings::InitClock::FixedTime; - ui->label_init_time->setVisible(is_fixed_time); - ui->edit_init_time->setVisible(is_fixed_time); + ui->label_init_time->setVisible(is_fixed_time && is_global); + ui->edit_init_time->setVisible(is_fixed_time && is_global); - ui->label_init_time_offset->setVisible(!is_fixed_time); - ui->edit_init_time_offset_days->setVisible(!is_fixed_time); - ui->edit_init_time_offset_time->setVisible(!is_fixed_time); + ui->label_init_time_offset->setVisible(!is_fixed_time && is_global); + ui->edit_init_time_offset_days->setVisible(!is_fixed_time && is_global); + ui->edit_init_time_offset_time->setVisible(!is_fixed_time && is_global); } void ConfigureSystem::RefreshConsoleID() { @@ -475,3 +489,43 @@ void ConfigureSystem::RefreshConsoleID() { void ConfigureSystem::RetranslateUI() { ui->retranslateUi(this); } + +void ConfigureSystem::SetupPerGameUI() { + // Block the global settings if a game is currently running that overrides them + if (Settings::IsConfiguringGlobal()) { + ui->toggle_new_3ds->setEnabled(Settings::values.is_new_3ds.UsingGlobal()); + ui->slider_clock_speed->setEnabled(Settings::values.cpu_clock_percentage.UsingGlobal()); + return; + } + + // Hide most settings for now, we can implement them later + ui->label_username->setVisible(false); + ui->label_birthday->setVisible(false); + ui->label_init_clock->setVisible(false); + ui->label_init_time->setVisible(false); + ui->label_console_id->setVisible(false); + ui->label_sound->setVisible(false); + ui->label_language->setVisible(false); + ui->label_country->setVisible(false); + ui->label_play_coins->setVisible(false); + ui->edit_username->setVisible(false); + ui->spinBox_play_coins->setVisible(false); + ui->combo_birthday->setVisible(false); + ui->combo_birthmonth->setVisible(false); + ui->combo_init_clock->setVisible(false); + ui->combo_sound->setVisible(false); + ui->combo_language->setVisible(false); + ui->combo_country->setVisible(false); + ui->label_init_time_offset->setVisible(false); + ui->edit_init_time_offset_days->setVisible(false); + ui->edit_init_time_offset_time->setVisible(false); + ui->button_regenerate_console_id->setVisible(false); + + connect(ui->clock_speed_combo, qOverload(&QComboBox::activated), this, [this](int index) { + ui->slider_clock_speed->setEnabled(index == 1); + ConfigurationShared::SetHighlight(ui->clock_speed_widget, index == 1); + }); + + ConfigurationShared::SetColoredTristate(ui->toggle_new_3ds, Settings::values.is_new_3ds, + is_new_3ds); +} diff --git a/src/citra_qt/configuration/configure_system.h b/src/citra_qt/configuration/configure_system.h index 99becf54a..007bed199 100644 --- a/src/citra_qt/configuration/configure_system.h +++ b/src/citra_qt/configuration/configure_system.h @@ -12,6 +12,10 @@ namespace Ui { class ConfigureSystem; } +namespace ConfigurationShared { +enum class CheckState; +} + namespace Service { namespace CFG { class Module; @@ -37,6 +41,9 @@ private: void UpdateInitTime(int init_clock); void RefreshConsoleID(); + void SetupPerGameUI(); + + ConfigurationShared::CheckState is_new_3ds; std::unique_ptr ui; bool enabled = false; diff --git a/src/citra_qt/configuration/configure_system.ui b/src/citra_qt/configuration/configure_system.ui index 37337f537..b2bb618b3 100644 --- a/src/citra_qt/configuration/configure_system.ui +++ b/src/citra_qt/configuration/configure_system.ui @@ -6,8 +6,8 @@ 0 0 - 520 - 564 + 525 + 619 @@ -344,54 +344,77 @@ - + Advanced - - - CPU Clock Speed - - - - - - - <html><body>Changes the emulated CPU clock frequency.<br>Underclocking can increase performance but may cause the game to freeze.<br>Overclocking may reduce in game lag but also might cause freezes</body></html> - - - 0 - - - 79 - - - 5 - - - 15 - - - 25 - - - Qt::Horizontal - - - QSlider::TicksBelow - - - - - - - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + + + + 7 + + + + + + Use global clock speed + + + + + Set clock speed: + + + + + + + + CPU Clock Speed + + + + + + + <html><body>Changes the emulated CPU clock frequency.<br>Underclocking can increase performance but may cause the game to freeze.<br>Overclocking may reduce in game lag but also might cause freezes</body></html> + + + 0 + + + 79 + + + 5 + + + 15 + + + 25 + + + Qt::Horizontal + + + QSlider::TicksBelow + + + + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + @@ -449,7 +472,6 @@ edit_init_time spinBox_play_coins button_regenerate_console_id - slider_clock_speed diff --git a/src/citra_qt/configuration/configure_touch_from_button.h b/src/citra_qt/configuration/configure_touch_from_button.h index a2fa02dc1..bd95a4945 100644 --- a/src/citra_qt/configuration/configure_touch_from_button.h +++ b/src/citra_qt/configuration/configure_touch_from_button.h @@ -9,7 +9,7 @@ #include #include #include -#include "core/settings.h" +#include "common/settings.h" class QItemSelection; class QModelIndex; diff --git a/src/citra_qt/configuration/configure_ui.cpp b/src/citra_qt/configuration/configure_ui.cpp index a3785247f..b3aad532c 100644 --- a/src/citra_qt/configuration/configure_ui.cpp +++ b/src/citra_qt/configuration/configure_ui.cpp @@ -46,12 +46,14 @@ void ConfigureUi::SetConfiguration() { ui->language_combobox->setCurrentIndex( ui->language_combobox->findData(UISettings::values.language)); ui->icon_size_combobox->setCurrentIndex( - static_cast(UISettings::values.game_list_icon_size)); - ui->row_1_text_combobox->setCurrentIndex(static_cast(UISettings::values.game_list_row_1)); - ui->row_2_text_combobox->setCurrentIndex(static_cast(UISettings::values.game_list_row_2) + - 1); - ui->toggle_hide_no_icon->setChecked(UISettings::values.game_list_hide_no_icon); - ui->toggle_single_line_mode->setChecked(UISettings::values.game_list_single_line_mode); + static_cast(UISettings::values.game_list_icon_size.GetValue())); + ui->row_1_text_combobox->setCurrentIndex( + static_cast(UISettings::values.game_list_row_1.GetValue())); + ui->row_2_text_combobox->setCurrentIndex( + static_cast(UISettings::values.game_list_row_2.GetValue()) + 1); + ui->toggle_hide_no_icon->setChecked(UISettings::values.game_list_hide_no_icon.GetValue()); + ui->toggle_single_line_mode->setChecked( + UISettings::values.game_list_single_line_mode.GetValue()); } void ConfigureUi::ApplyConfiguration() { diff --git a/src/citra_qt/configuration/configure_web.cpp b/src/citra_qt/configuration/configure_web.cpp index e97a22720..b9a15b6ec 100644 --- a/src/citra_qt/configuration/configure_web.cpp +++ b/src/citra_qt/configuration/configure_web.cpp @@ -87,7 +87,7 @@ void ConfigureWeb::SetConfiguration() { tr("Telemetry ID: 0x%1").arg(QString::number(Core::GetTelemetryId(), 16).toUpper())); user_verified = true; - ui->toggle_discordrpc->setChecked(UISettings::values.enable_discord_presence); + ui->toggle_discordrpc->setChecked(UISettings::values.enable_discord_presence.GetValue()); } void ConfigureWeb::ApplyConfiguration() { diff --git a/src/citra_qt/debugger/console.cpp b/src/citra_qt/debugger/console.cpp index 95ce9b0c7..eecf86048 100644 --- a/src/citra_qt/debugger/console.cpp +++ b/src/citra_qt/debugger/console.cpp @@ -15,10 +15,10 @@ namespace Debugger { void ToggleConsole() { static bool console_shown = false; - if (console_shown == UISettings::values.show_console) { + if (console_shown == UISettings::values.show_console.GetValue()) { return; } else { - console_shown = UISettings::values.show_console; + console_shown = UISettings::values.show_console.GetValue(); } #ifdef _WIN32 diff --git a/src/citra_qt/debugger/lle_service_modules.cpp b/src/citra_qt/debugger/lle_service_modules.cpp index dca3ce638..508381326 100644 --- a/src/citra_qt/debugger/lle_service_modules.cpp +++ b/src/citra_qt/debugger/lle_service_modules.cpp @@ -7,7 +7,7 @@ #include #include #include "citra_qt/debugger/lle_service_modules.h" -#include "core/settings.h" +#include "common/settings.h" LLEServiceModulesWidget::LLEServiceModulesWidget(QWidget* parent) : QDockWidget(tr("Toggle LLE Service Modules"), parent) { diff --git a/src/citra_qt/debugger/wait_tree.cpp b/src/citra_qt/debugger/wait_tree.cpp index d46d99758..5892ce48c 100644 --- a/src/citra_qt/debugger/wait_tree.cpp +++ b/src/citra_qt/debugger/wait_tree.cpp @@ -7,13 +7,13 @@ #include "citra_qt/uisettings.h" #include "citra_qt/util/util.h" #include "common/assert.h" +#include "common/settings.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/semaphore.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" #include "core/hle/kernel/wait_object.h" -#include "core/settings.h" namespace { diff --git a/src/citra_qt/dumping/dumping_dialog.cpp b/src/citra_qt/dumping/dumping_dialog.cpp index 70d165c01..bbe69f245 100644 --- a/src/citra_qt/dumping/dumping_dialog.cpp +++ b/src/citra_qt/dumping/dumping_dialog.cpp @@ -7,7 +7,7 @@ #include "citra_qt/dumping/dumping_dialog.h" #include "citra_qt/dumping/options_dialog.h" #include "citra_qt/uisettings.h" -#include "core/settings.h" +#include "common/settings.h" #include "ui_dumping_dialog.h" DumpingDialog::DumpingDialog(QWidget* parent) diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp index 43c6eaa78..4a3771bf8 100644 --- a/src/citra_qt/game_list.cpp +++ b/src/citra_qt/game_list.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include #include @@ -28,9 +29,11 @@ #include "citra_qt/main.h" #include "citra_qt/uisettings.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/file_sys/archive_extsavedata.h" #include "core/file_sys/archive_source_sd_savedata.h" #include "core/hle/service/fs/archive.h" +#include "qcursor.h" GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist, QObject* parent) : QObject(parent), gamelist{gamelist} {} @@ -462,9 +465,20 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { default: break; } + context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); } +void ForEachOpenGLCacheFile(u64 program_id, auto func) { + for (const std::string_view cache_type : {"separable", "conventional"}) { + const std::string path = fmt::format("{}opengl/precompiled/{}/{:016X}.bin", + FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir), + cache_type, program_id); + QFile file{QString::fromStdString(path)}; + func(file); + } +} + void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 program_id, u64 extdata_id) { QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); @@ -475,19 +489,32 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra QAction* open_texture_load_location = context_menu.addAction(tr("Open Custom Texture Location")); QAction* open_mods_location = context_menu.addAction(tr("Open Mods Location")); + QAction* open_dlc_location = context_menu.addAction(tr("Open DLC Data Location")); + QMenu* shader_menu = context_menu.addMenu(tr("Disk Shader Cache")); QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); + context_menu.addSeparator(); + QAction* properties = context_menu.addAction(tr("Properties")); + + QAction* open_shader_cache_location = shader_menu->addAction(tr("Open Shader Cache Location")); + shader_menu->addSeparator(); + QAction* delete_opengl_disk_shader_cache = + shader_menu->addAction(tr("Delete OpenGL Shader Cache")); const bool is_application = 0x0004000000000000 <= program_id && program_id <= 0x00040000FFFFFFFF; + bool opengl_cache_exists = false; + ForEachOpenGLCacheFile( + program_id, [&opengl_cache_exists](QFile& file) { opengl_cache_exists |= file.exists(); }); + std::string sdmc_dir = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir); - open_save_location->setVisible( + open_save_location->setEnabled( is_application && FileUtil::Exists(FileSys::ArchiveSource_SDSaveData::GetSaveDataPathFor( sdmc_dir, program_id))); if (extdata_id) { - open_extdata_location->setVisible( + open_extdata_location->setEnabled( is_application && FileUtil::Exists(FileSys::GetExtDataPathFromId(sdmc_dir, extdata_id))); } else { @@ -495,18 +522,20 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra } auto media_type = Service::AM::GetTitleMediaType(program_id); - open_application_location->setVisible(path.toStdString() == + open_application_location->setEnabled(path.toStdString() == Service::AM::GetTitleContentPath(media_type, program_id)); - open_update_location->setVisible( + open_update_location->setEnabled( is_application && FileUtil::Exists(Service::AM::GetTitlePath(Service::FS::MediaType::SDMC, program_id + 0xe00000000) + "content/")); auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); - open_texture_dump_location->setVisible(is_application); - open_texture_load_location->setVisible(is_application); - open_mods_location->setVisible(is_application); - dump_romfs->setVisible(is_application); + open_texture_dump_location->setEnabled(is_application); + open_texture_load_location->setEnabled(is_application); + open_mods_location->setEnabled(is_application); + open_dlc_location->setEnabled(is_application); + dump_romfs->setEnabled(is_application); + delete_opengl_disk_shader_cache->setEnabled(opengl_cache_exists); navigate_to_gamedb_entry->setVisible(it != compatibility_list.end()); @@ -543,11 +572,32 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra emit OpenFolderRequested(program_id, GameListOpenTarget::MODS); } }); + connect(open_dlc_location, &QAction::triggered, this, [this, program_id] { + const u64 trimmed_id = program_id & 0xFFFFFFF; + const std::string dlc_path = + fmt::format("{}Nintendo 3DS/00000000000000000000000000000000/" + "00000000000000000000000000000000/title/0004008c/{:08x}/content/", + FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), trimmed_id); + fmt::print("DLC path {}\n", dlc_path); + if (FileUtil::CreateFullPath(dlc_path)) { + emit OpenFolderRequested(trimmed_id, GameListOpenTarget::DLC_DATA); + } + }); connect(dump_romfs, &QAction::triggered, this, [this, path, program_id] { emit DumpRomFSRequested(path, program_id); }); connect(navigate_to_gamedb_entry, &QAction::triggered, this, [this, program_id]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); + connect(properties, &QAction::triggered, this, + [this, path]() { emit OpenPerGameGeneralRequested(path); }); + connect(open_shader_cache_location, &QAction::triggered, this, [this, program_id] { + if (FileUtil::CreateFullPath(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir))) { + emit OpenFolderRequested(program_id, GameListOpenTarget::SHADER_CACHE); + } + }); + connect(delete_opengl_disk_shader_cache, &QAction::triggered, this, [program_id] { + ForEachOpenGLCacheFile(program_id, [](QFile& file) { file.remove(); }); + }); }; void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) { diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h index bf20cc9b8..fced8f9e3 100644 --- a/src/citra_qt/game_list.h +++ b/src/citra_qt/game_list.h @@ -37,6 +37,8 @@ enum class GameListOpenTarget { TEXTURE_DUMP = 4, TEXTURE_LOAD = 5, MODS = 6, + DLC_DATA = 7, + SHADER_CACHE = 8 }; class GameList : public QWidget { @@ -82,6 +84,7 @@ signals: void OpenFolderRequested(u64 program_id, GameListOpenTarget target); void NavigateToGamedbEntryRequested(u64 program_id, const CompatibilityList& compatibility_list); + void OpenPerGameGeneralRequested(const QString file); void DumpRomFSRequested(QString game_path, u64 program_id); void OpenDirectory(const QString& directory); void AddDirectory(); diff --git a/src/citra_qt/game_list_p.h b/src/citra_qt/game_list_p.h index 75f1b90fa..19d04053d 100644 --- a/src/citra_qt/game_list_p.h +++ b/src/citra_qt/game_list_p.h @@ -160,17 +160,19 @@ public: setData(qulonglong(program_id), ProgramIdRole); setData(qulonglong(extdata_id), ExtdataIdRole); - if (UISettings::values.game_list_icon_size == UISettings::GameListIconSize::NoIcon) { + if (UISettings::values.game_list_icon_size.GetValue() == + UISettings::GameListIconSize::NoIcon) { // Do not display icons setData(QPixmap(), Qt::DecorationRole); } - bool large = - UISettings::values.game_list_icon_size == UISettings::GameListIconSize::LargeIcon; + bool large = UISettings::values.game_list_icon_size.GetValue() == + UISettings::GameListIconSize::LargeIcon; if (!Loader::IsValidSMDH(smdh_data)) { // SMDH is not valid, set a default icon - if (UISettings::values.game_list_icon_size != UISettings::GameListIconSize::NoIcon) + if (UISettings::values.game_list_icon_size.GetValue() != + UISettings::GameListIconSize::NoIcon) setData(GetDefaultIcon(large), Qt::DecorationRole); return; } @@ -179,7 +181,8 @@ public: memcpy(&smdh, smdh_data.data(), sizeof(Loader::SMDH)); // Get icon from SMDH - if (UISettings::values.game_list_icon_size != UISettings::GameListIconSize::NoIcon) { + if (UISettings::values.game_list_icon_size.GetValue() != + UISettings::GameListIconSize::NoIcon) { setData(GetQPixmapFromSMDH(smdh, large), Qt::DecorationRole); } @@ -211,16 +214,17 @@ public: QString::fromStdString(fmt::format("{:016X}", data(ProgramIdRole).toULongLong()))}, }; - const QString& row1 = display_texts.at(UISettings::values.game_list_row_1).simplified(); + const QString& row1 = + display_texts.at(UISettings::values.game_list_row_1.GetValue()).simplified(); if (role == SortRole) return row1.toLower(); QString row2; - auto row_2_id = UISettings::values.game_list_row_2; + const auto row_2_id = UISettings::values.game_list_row_2.GetValue(); if (row_2_id != UISettings::GameListText::NoText) { if (!row1.isEmpty()) { - row2 = UISettings::values.game_list_single_line_mode + row2 = UISettings::values.game_list_single_line_mode.GetValue() ? QStringLiteral(" ") : QStringLiteral("\n "); } @@ -355,7 +359,7 @@ public: UISettings::GameDir* game_dir = &directory; setData(QVariant(UISettings::values.game_dirs.indexOf(directory)), GameDirRole); - const int icon_size = IconSizes.at(UISettings::values.game_list_icon_size); + const int icon_size = IconSizes.at(UISettings::values.game_list_icon_size.GetValue()); switch (dir_type) { case GameListItemType::InstalledDir: setData(QIcon::fromTheme(QStringLiteral("sd_card")).pixmap(icon_size), @@ -398,7 +402,7 @@ public: explicit GameListAddDir() { setData(type(), TypeRole); - int icon_size = IconSizes.at(UISettings::values.game_list_icon_size); + int icon_size = IconSizes.at(UISettings::values.game_list_icon_size.GetValue()); setData(QIcon::fromTheme(QStringLiteral("plus")).pixmap(icon_size), Qt::DecorationRole); setData(QObject::tr("Add New Game Directory"), Qt::DisplayRole); } diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 225694969..40767a49a 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include #include @@ -34,6 +35,7 @@ #include "citra_qt/compatibility_list.h" #include "citra_qt/configuration/config.h" #include "citra_qt/configuration/configure_dialog.h" +#include "citra_qt/configuration/configure_per_game.h" #include "citra_qt/debugger/console.h" #include "citra_qt/debugger/graphics/graphics.h" #include "citra_qt/debugger/graphics/graphics_breakpoints.h" @@ -70,9 +72,11 @@ #include "common/microprofile.h" #include "common/scm_rev.h" #include "common/scope_exit.h" +#include "common/string_util.h" #ifdef ARCHITECTURE_x86_64 #include "common/x64/cpu_detect.h" #endif +#include "common/settings.h" #include "core/core.h" #include "core/dumping/backend.h" #include "core/file_sys/archive_extsavedata.h" @@ -85,7 +89,6 @@ #include "core/loader/loader.h" #include "core/movie.h" #include "core/savestate.h" -#include "core/settings.h" #include "game_list_p.h" #include "input_common/main.h" #include "network/network_settings.h" @@ -124,11 +127,13 @@ enum class CalloutFlag : uint32_t { }; void GMainWindow::ShowTelemetryCallout() { - if (UISettings::values.callout_flags & static_cast(CalloutFlag::Telemetry)) { + if (UISettings::values.callout_flags.GetValue() & + static_cast(CalloutFlag::Telemetry)) { return; } - UISettings::values.callout_flags |= static_cast(CalloutFlag::Telemetry); + UISettings::values.callout_flags = + UISettings::values.callout_flags.GetValue() | static_cast(CalloutFlag::Telemetry); const QString telemetry_message = tr("Anonymous " "data is collected to help improve Citra. " @@ -143,7 +148,7 @@ const int GMainWindow::max_recent_files_item; static void InitializeLogging() { Log::Filter log_filter; - log_filter.ParseFilterString(Settings::values.log_filter); + log_filter.ParseFilterString(Settings::values.log_filter.GetValue()); Log::SetGlobalFilter(log_filter); const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir); @@ -175,7 +180,7 @@ GMainWindow::GMainWindow() default_theme_paths = QIcon::themeSearchPaths(); UpdateUITheme(); - SetDiscordEnabled(UISettings::values.enable_discord_presence); + SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); discord_rpc->Update(); Network::Init(); @@ -556,8 +561,7 @@ void GMainWindow::InitializeHotkeys() { }); connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Alternate Speed"), this), &QShortcut::activated, this, [&] { - Settings::values.use_frame_limit_alternate = - !Settings::values.use_frame_limit_alternate; + Settings::values.frame_limit.SetGlobal(!Settings::values.frame_limit.UsingGlobal()); UpdateStatusBar(); }); connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Toggle Texture Dumping"), this), @@ -568,42 +572,25 @@ void GMainWindow::InitializeHotkeys() { static constexpr u16 SPEED_LIMIT_STEP = 5; connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Increase Speed Limit"), this), &QShortcut::activated, this, [&] { - if (Settings::values.use_frame_limit_alternate) { - if (Settings::values.frame_limit_alternate == 0) { - return; - } - if (Settings::values.frame_limit_alternate < 995 - SPEED_LIMIT_STEP) { - Settings::values.frame_limit_alternate += SPEED_LIMIT_STEP; - } else { - Settings::values.frame_limit_alternate = 0; - } + if (Settings::values.frame_limit.GetValue() == 0) { + return; + } + if (Settings::values.frame_limit.GetValue() < 995 - SPEED_LIMIT_STEP) { + Settings::values.frame_limit.SetValue(Settings::values.frame_limit.GetValue() + + SPEED_LIMIT_STEP); } else { - if (Settings::values.frame_limit == 0) { - return; - } - if (Settings::values.frame_limit < 995 - SPEED_LIMIT_STEP) { - Settings::values.frame_limit += SPEED_LIMIT_STEP; - } else { - Settings::values.frame_limit = 0; - } + Settings::values.frame_limit = 0; } UpdateStatusBar(); }); connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Decrease Speed Limit"), this), &QShortcut::activated, this, [&] { - if (Settings::values.use_frame_limit_alternate) { - if (Settings::values.frame_limit_alternate == 0) { - Settings::values.frame_limit_alternate = 995; - } else if (Settings::values.frame_limit_alternate > SPEED_LIMIT_STEP) { - Settings::values.frame_limit_alternate -= SPEED_LIMIT_STEP; - } - } else { - if (Settings::values.frame_limit == 0) { - Settings::values.frame_limit = 995; - } else if (Settings::values.frame_limit > SPEED_LIMIT_STEP) { - Settings::values.frame_limit -= SPEED_LIMIT_STEP; - UpdateStatusBar(); - } + if (Settings::values.frame_limit.GetValue() == 0) { + Settings::values.frame_limit = 995; + } else if (Settings::values.frame_limit.GetValue() > SPEED_LIMIT_STEP) { + Settings::values.frame_limit.SetValue(Settings::values.frame_limit.GetValue() - + SPEED_LIMIT_STEP); + UpdateStatusBar(); } UpdateStatusBar(); }); @@ -663,25 +650,26 @@ void GMainWindow::RestoreUIState() { render_window->restoreGeometry(UISettings::values.renderwindow_geometry); #if MICROPROFILE_ENABLED microProfileDialog->restoreGeometry(UISettings::values.microprofile_geometry); - microProfileDialog->setVisible(UISettings::values.microprofile_visible); + microProfileDialog->setVisible(UISettings::values.microprofile_visible.GetValue()); #endif ui->action_Cheats->setEnabled(false); game_list->LoadInterfaceLayout(); - ui->action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode); + ui->action_Single_Window_Mode->setChecked(UISettings::values.single_window_mode.GetValue()); ToggleWindowMode(); - ui->action_Fullscreen->setChecked(UISettings::values.fullscreen); + ui->action_Fullscreen->setChecked(UISettings::values.fullscreen.GetValue()); SyncMenuUISettings(); - ui->action_Display_Dock_Widget_Headers->setChecked(UISettings::values.display_titlebar); + ui->action_Display_Dock_Widget_Headers->setChecked( + UISettings::values.display_titlebar.GetValue()); OnDisplayTitleBars(ui->action_Display_Dock_Widget_Headers->isChecked()); - ui->action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar); + ui->action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar.GetValue()); game_list->SetFilterVisible(ui->action_Show_Filter_Bar->isChecked()); - ui->action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar); + ui->action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar.GetValue()); statusBar()->setVisible(ui->action_Show_Status_Bar->isChecked()); } @@ -717,6 +705,9 @@ void GMainWindow::ConnectWidgetEvents() { connect(game_list, &GameList::PopulatingCompleted, this, [this] { multiplayer_state->UpdateGameList(game_list->GetModel()); }); + connect(game_list, &GameList::OpenPerGameGeneralRequested, this, + &GMainWindow::OnGameListOpenPerGameProperties); + connect(this, &GMainWindow::EmulationStarting, render_window, &GRenderWindow::OnEmulationStarting); connect(this, &GMainWindow::EmulationStopping, render_window, @@ -752,6 +743,8 @@ void GMainWindow::ConnectMenuEvents() { connect(ui->action_Report_Compatibility, &QAction::triggered, this, &GMainWindow::OnMenuReportCompatibility); connect(ui->action_Configure, &QAction::triggered, this, &GMainWindow::OnConfigure); + connect(ui->action_Configure_Current_Game, &QAction::triggered, this, + &GMainWindow::OnConfigurePerGame); connect(ui->action_Cheats, &QAction::triggered, this, &GMainWindow::OnCheats); // View @@ -1086,6 +1079,22 @@ void GMainWindow::BootGame(const QString& filename) { Core::Movie::GetInstance().PrepareForPlayback(movie_playback_path.toStdString()); } + u64 title_id{0}; + const std::string path = filename.toStdString(); + const auto loader = Loader::GetLoader(path); + + if (loader != nullptr && loader->ReadProgramId(title_id) == Loader::ResultStatus::Success) { + // Load per game settings + const std::string name{FileUtil::GetFilename(filename.toStdString())}; + const std::string config_file_name = + title_id == 0 ? name : fmt::format("{:016X}", title_id); + Config per_game_config(config_file_name, Config::ConfigType::PerGameConfig); + Settings::Apply(); + + LOG_INFO(Frontend, "Using per game config file for title id {}", config_file_name); + Settings::LogSettings(); + } + // Save configurations UpdateUISettings(); game_list->SaveInterfaceLayout(); @@ -1239,6 +1248,7 @@ void GMainWindow::ShutdownGame() { ui->action_Stop->setEnabled(false); ui->action_Restart->setEnabled(false); ui->action_Cheats->setEnabled(false); + ui->action_Configure_Current_Game->setEnabled(false); ui->action_Load_Amiibo->setEnabled(false); ui->action_Remove_Amiibo->setEnabled(false); ui->action_Report_Compatibility->setEnabled(false); @@ -1398,26 +1408,42 @@ void GMainWindow::OnGameListOpenFolder(u64 data_id, GameListOpenTarget target) { path = Service::AM::GetTitlePath(media_type, data_id) + "content/"; break; } - case GameListOpenTarget::UPDATE_DATA: + case GameListOpenTarget::UPDATE_DATA: { open_target = "Update Data"; path = Service::AM::GetTitlePath(Service::FS::MediaType::SDMC, data_id + 0xe00000000) + "content/"; break; - case GameListOpenTarget::TEXTURE_DUMP: + } + case GameListOpenTarget::TEXTURE_DUMP: { open_target = "Dumped Textures"; path = fmt::format("{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), data_id); break; - case GameListOpenTarget::TEXTURE_LOAD: + } + case GameListOpenTarget::TEXTURE_LOAD: { open_target = "Custom Textures"; path = fmt::format("{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), data_id); break; - case GameListOpenTarget::MODS: + } + case GameListOpenTarget::MODS: { open_target = "Mods"; path = fmt::format("{}mods/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), data_id); break; + } + case GameListOpenTarget::DLC_DATA: { + open_target = "DLC Data"; + path = fmt::format("{}Nintendo 3DS/00000000000000000000000000000000/" + "00000000000000000000000000000000/title/0004008c/{:08x}/content/", + FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), data_id); + break; + } + case GameListOpenTarget::SHADER_CACHE: { + open_target = "Shader Cache"; + path = FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir); + break; + } default: LOG_ERROR(Frontend, "Unexpected target {}", static_cast(target)); return; @@ -1529,6 +1555,19 @@ void GMainWindow::OnGameListShowList(bool show) { game_list_placeholder->setVisible(!show); }; +void GMainWindow::OnGameListOpenPerGameProperties(const QString& file) { + const auto loader = Loader::GetLoader(file.toStdString()); + + u64 title_id{}; + if (!loader || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) { + QMessageBox::information(this, tr("Properties"), + tr("The game properties could not be loaded.")); + return; + } + + OpenPerGameConfiguration(title_id, file); +} + void GMainWindow::OnMenuLoadFile() { const QString extensions = QStringLiteral("*.").append( GameList::supported_file_extensions.join(QStringLiteral(" *."))); @@ -1654,6 +1693,7 @@ void GMainWindow::OnStartGame() { ui->action_Stop->setEnabled(true); ui->action_Restart->setEnabled(true); ui->action_Cheats->setEnabled(true); + ui->action_Configure_Current_Game->setEnabled(true); ui->action_Load_Amiibo->setEnabled(true); ui->action_Report_Compatibility->setEnabled(true); ui->action_Capture_Screenshot->setEnabled(true); @@ -1676,6 +1716,7 @@ void GMainWindow::OnPauseGame() { void GMainWindow::OnStopGame() { ShutdownGame(); + Settings::RestoreGlobalState(false); } void GMainWindow::OnLoadComplete() { @@ -1769,7 +1810,7 @@ void GMainWindow::UpdateSecondaryWindowVisibility() { if (!emulation_running) { return; } - if (Settings::values.layout_option == Settings::LayoutOption::SeparateWindows) { + if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows) { secondary_window->RestoreGeometry(); secondary_window->show(); } else { @@ -1800,7 +1841,7 @@ void GMainWindow::ChangeScreenLayout() { void GMainWindow::ToggleScreenLayout() { const Settings::LayoutOption new_layout = []() { - switch (Settings::values.layout_option) { + switch (Settings::values.layout_option.GetValue()) { case Settings::LayoutOption::Default: return Settings::LayoutOption::SingleScreen; case Settings::LayoutOption::SingleScreen: @@ -1812,7 +1853,8 @@ void GMainWindow::ToggleScreenLayout() { case Settings::LayoutOption::SeparateWindows: return Settings::LayoutOption::Default; default: - LOG_ERROR(Frontend, "Unknown layout option {}", Settings::values.layout_option); + LOG_ERROR(Frontend, "Unknown layout option {}", + Settings::values.layout_option.GetValue()); return Settings::LayoutOption::Default; } }(); @@ -1864,6 +1906,7 @@ void GMainWindow::OnLoadState() { } void GMainWindow::OnConfigure() { + Settings::SetConfiguringGlobal(true); ConfigureDialog configureDialog(this, hotkey_registry, !multiplayer_state->IsHostingPublicRoom()); connect(&configureDialog, &ConfigureDialog::LanguageChanged, this, @@ -1872,15 +1915,15 @@ void GMainWindow::OnConfigure() { const int old_input_profile_index = Settings::values.current_input_profile_index; const auto old_input_profiles = Settings::values.input_profiles; const auto old_touch_from_button_maps = Settings::values.touch_from_button_maps; - const bool old_discord_presence = UISettings::values.enable_discord_presence; + const bool old_discord_presence = UISettings::values.enable_discord_presence.GetValue(); auto result = configureDialog.exec(); if (result == QDialog::Accepted) { configureDialog.ApplyConfiguration(); InitializeHotkeys(); if (UISettings::values.theme != old_theme) UpdateUITheme(); - if (UISettings::values.enable_discord_presence != old_discord_presence) - SetDiscordEnabled(UISettings::values.enable_discord_presence); + if (UISettings::values.enable_discord_presence.GetValue() != old_discord_presence) + SetDiscordEnabled(UISettings::values.enable_discord_presence.GetValue()); if (!multiplayer_state->IsHostingPublicRoom()) multiplayer_state->UpdateCredentials(); emit UpdateThemedIcons(); @@ -2057,24 +2100,26 @@ void GMainWindow::OnSaveMovie() { void GMainWindow::OnCaptureScreenshot() { OnPauseGame(); - QString path = UISettings::values.screenshot_path; - if (!FileUtil::IsDirectory(path.toStdString())) { - if (!FileUtil::CreateFullPath(path.toStdString())) { + std::string path = UISettings::values.screenshot_path.GetValue(); + if (!FileUtil::IsDirectory(path)) { + if (!FileUtil::CreateFullPath(path)) { QMessageBox::information(this, tr("Invalid Screenshot Directory"), tr("Cannot create specified screenshot directory. Screenshot " "path is set back to its default value.")); - path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir)); - path.append(QStringLiteral("screenshots/")); + path = FileUtil::GetUserPath(FileUtil::UserPath::UserDir); + path.append("screenshots/"); UISettings::values.screenshot_path = path; }; } - const QString filename = game_title.remove(QRegularExpression(QStringLiteral("[\\/:?\"<>|]"))); - const QString timestamp = - QDateTime::currentDateTime().toString(QStringLiteral("dd.MM.yy_hh.mm.ss.z")); - path.append(QStringLiteral("/%1_%2.png").arg(filename, timestamp)); + const std::string filename = + game_title.remove(QRegularExpression(QStringLiteral("[\\/:?\"<>|]"))).toStdString(); + const std::string timestamp = + QDateTime::currentDateTime().toString(QStringLiteral("dd.MM.yy_hh.mm.ss.z")).toStdString(); + path.append(fmt::format("/{}_{}.png", filename, timestamp)); auto* const screenshot_window = secondary_window->HasFocus() ? secondary_window : render_window; - screenshot_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path); + screenshot_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor.GetValue(), + QString::fromStdString(path)); OnStartGame(); } @@ -2166,22 +2211,12 @@ void GMainWindow::UpdateStatusBar() { auto results = Core::System::GetInstance().GetAndResetPerfStats(); - if (Settings::values.use_frame_limit_alternate) { - if (Settings::values.frame_limit_alternate == 0) { - emu_speed_label->setText( - tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0)); - - } else { - emu_speed_label->setText(tr("Speed: %1% / %2%") - .arg(results.emulation_speed * 100.0, 0, 'f', 0) - .arg(Settings::values.frame_limit_alternate)); - } - } else if (Settings::values.frame_limit == 0) { + if (Settings::values.frame_limit.GetValue() == 0) { emu_speed_label->setText(tr("Speed: %1%").arg(results.emulation_speed * 100.0, 0, 'f', 0)); } else { emu_speed_label->setText(tr("Speed: %1% / %2%") .arg(results.emulation_speed * 100.0, 0, 'f', 0) - .arg(Settings::values.frame_limit)); + .arg(Settings::values.frame_limit.GetValue())); } game_fps_label->setText(tr("Game: %1 FPS").arg(results.game_fps, 0, 'f', 0)); emu_frametime_label->setText(tr("Frame: %1 ms").arg(results.frametime * 1000.0, 0, 'f', 2)); @@ -2192,7 +2227,7 @@ void GMainWindow::UpdateStatusBar() { } void GMainWindow::HideMouseCursor() { - if (emu_thread == nullptr || UISettings::values.hide_mouse == false) { + if (emu_thread == nullptr || !UISettings::values.hide_mouse.GetValue()) { mouse_hide_timer.stop(); ShowMouseCursor(); return; @@ -2455,6 +2490,35 @@ void GMainWindow::OnLanguageChanged(const QString& locale) { ui->action_Start->setText(tr("Continue")); } +void GMainWindow::OnConfigurePerGame() { + u64 title_id{}; + Core::System::GetInstance().GetAppLoader().ReadProgramId(title_id); + OpenPerGameConfiguration(title_id, game_path); +} + +void GMainWindow::OpenPerGameConfiguration(u64 title_id, const QString& file_name) { + Core::System& system = Core::System::GetInstance(); + + Settings::SetConfiguringGlobal(false); + ConfigurePerGame dialog(this, title_id, file_name, system); + const auto result = dialog.exec(); + + if (result != QDialog::Accepted) { + Settings::RestoreGlobalState(system.IsPoweredOn()); + return; + } else if (result == QDialog::Accepted) { + dialog.ApplyConfiguration(); + } + + // Do not cause the global config to write local settings into the config file + const bool is_powered_on = system.IsPoweredOn(); + Settings::RestoreGlobalState(system.IsPoweredOn()); + + if (!is_powered_on) { + config->Save(); + } +} + void GMainWindow::OnMoviePlaybackCompleted() { OnPauseGame(); QMessageBox::information(this, tr("Playback Completed"), tr("Movie playback completed.")); @@ -2489,18 +2553,19 @@ void GMainWindow::UpdateUISettings() { } void GMainWindow::SyncMenuUISettings() { - ui->action_Screen_Layout_Default->setChecked(Settings::values.layout_option == + ui->action_Screen_Layout_Default->setChecked(Settings::values.layout_option.GetValue() == Settings::LayoutOption::Default); - ui->action_Screen_Layout_Single_Screen->setChecked(Settings::values.layout_option == + ui->action_Screen_Layout_Single_Screen->setChecked(Settings::values.layout_option.GetValue() == Settings::LayoutOption::SingleScreen); - ui->action_Screen_Layout_Large_Screen->setChecked(Settings::values.layout_option == + ui->action_Screen_Layout_Large_Screen->setChecked(Settings::values.layout_option.GetValue() == Settings::LayoutOption::LargeScreen); - ui->action_Screen_Layout_Side_by_Side->setChecked(Settings::values.layout_option == + ui->action_Screen_Layout_Side_by_Side->setChecked(Settings::values.layout_option.GetValue() == Settings::LayoutOption::SideScreen); - ui->action_Screen_Layout_Separate_Windows->setChecked(Settings::values.layout_option == - Settings::LayoutOption::SeparateWindows); - ui->action_Screen_Layout_Swap_Screens->setChecked(Settings::values.swap_screen); - ui->action_Screen_Layout_Upright_Screens->setChecked(Settings::values.upright_screen); + ui->action_Screen_Layout_Separate_Windows->setChecked( + Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows); + ui->action_Screen_Layout_Swap_Screens->setChecked(Settings::values.swap_screen.GetValue()); + ui->action_Screen_Layout_Upright_Screens->setChecked( + Settings::values.upright_screen.GetValue()); } void GMainWindow::RetranslateStatusBar() { @@ -2573,11 +2638,13 @@ int main(int argc, char* argv[]) { // Register frontend applets Frontend::RegisterDefaultApplets(); - Core::System::GetInstance().RegisterMiiSelector(std::make_shared(main_window)); - Core::System::GetInstance().RegisterSoftwareKeyboard(std::make_shared(main_window)); + + Core::System& system = Core::System::GetInstance(); + system.RegisterMiiSelector(std::make_shared(main_window)); + system.RegisterSoftwareKeyboard(std::make_shared(main_window)); // Register Qt image interface - Core::System::GetInstance().RegisterImageInterface(std::make_shared()); + system.RegisterImageInterface(std::make_shared()); main_window.show(); diff --git a/src/citra_qt/main.h b/src/citra_qt/main.h index e11f99d0d..03689cc5f 100644 --- a/src/citra_qt/main.h +++ b/src/citra_qt/main.h @@ -183,6 +183,8 @@ private slots: void OnGameListOpenDirectory(const QString& directory); void OnGameListAddDirectory(); void OnGameListShowList(bool show); + void OnGameListOpenPerGameProperties(const QString& file); + void OnConfigurePerGame(); void OnMenuLoadFile(); void OnMenuInstallCIA(); void OnUpdateProgress(std::size_t written, std::size_t total); @@ -238,6 +240,7 @@ private: void InstallCIA(QStringList filepaths); void HideMouseCursor(); void ShowMouseCursor(); + void OpenPerGameConfiguration(u64 title_id, const QString& file_name); std::unique_ptr ui; diff --git a/src/citra_qt/main.ui b/src/citra_qt/main.ui index 2c2185ed8..b0d5b0387 100644 --- a/src/citra_qt/main.ui +++ b/src/citra_qt/main.ui @@ -45,7 +45,7 @@ 0 0 1081 - 21 + 25 @@ -104,6 +104,7 @@ + @@ -239,20 +240,20 @@ - - false - - - Save - + + false + + + Save + - - false - - - Load - + + false + + + Load + @@ -541,6 +542,14 @@ Open Citra Folder + + + false + + + Configure Current Game... + + diff --git a/src/citra_qt/uisettings.h b/src/citra_qt/uisettings.h index 2226c67e1..43e84ced8 100644 --- a/src/citra_qt/uisettings.h +++ b/src/citra_qt/uisettings.h @@ -13,7 +13,7 @@ #include #include #include -#include "common/common_types.h" +#include "common/settings.h" namespace UISettings { @@ -40,13 +40,13 @@ struct GameDir { } }; -enum class GameListIconSize { +enum class GameListIconSize : u32 { NoIcon, ///< Do not display icons SmallIcon, ///< Display a small (24x24) icon LargeIcon, ///< Display a large (48x48) icon }; -enum class GameListText { +enum class GameListText : s32 { NoText = -1, ///< No text FileName, ///< Display the file name of the entry FullPath, ///< Display the full path of the entry @@ -65,40 +65,41 @@ struct Values { QByteArray gamelist_header_state; QByteArray microprofile_geometry; - bool microprofile_visible; + Settings::Setting microprofile_visible{false, "microProfileDialogVisible"}; - bool single_window_mode; - bool fullscreen; - bool display_titlebar; - bool show_filter_bar; - bool show_status_bar; + Settings::Setting single_window_mode{true, "singleWindowMode"}; + Settings::Setting fullscreen{false, "fullscreen"}; + Settings::Setting display_titlebar{true, "displayTitleBars"}; + Settings::Setting show_filter_bar{true, "showFilterBar"}; + Settings::Setting show_status_bar{true, "showStatusBar"}; - bool confirm_before_closing; - bool first_start; - bool pause_when_in_background; - bool hide_mouse; + Settings::Setting confirm_before_closing{true, "confirmClose"}; + Settings::Setting first_start{true, "firstStart"}; + Settings::Setting pause_when_in_background{false, "pauseWhenInBackground"}; + Settings::Setting hide_mouse{false, "hideInactiveMouse"}; bool updater_found; - bool update_on_close; - bool check_for_update_on_start; + Settings::Setting update_on_close{false, "update_on_close"}; + Settings::Setting check_for_update_on_start{true, "check_for_update_on_start"}; // Discord RPC - bool enable_discord_presence; + Settings::Setting enable_discord_presence{true, "enable_discord_presence"}; // Game List - GameListIconSize game_list_icon_size; - GameListText game_list_row_1; - GameListText game_list_row_2; - bool game_list_hide_no_icon; - bool game_list_single_line_mode; + Settings::Setting game_list_icon_size{GameListIconSize::LargeIcon, + "iconSize"}; + Settings::Setting game_list_row_1{GameListText::TitleName, "row1"}; + Settings::Setting game_list_row_2{GameListText::FileName, "row2"}; + Settings::Setting game_list_hide_no_icon{false, "hideNoIcon"}; + Settings::Setting game_list_single_line_mode{false, "singleLineMode"}; - u16 screenshot_resolution_factor; + Settings::Setting screenshot_resolution_factor{0, "screenshot_resolution_factor"}; + Settings::SwitchableSetting screenshot_path{"", "screenshotPath"}; QString roms_path; QString symbols_path; QString movie_record_path; QString movie_playback_path; - QString screenshot_path; QString video_dumping_path; QString game_dir_deprecated; bool game_dir_deprecated_deepscan; @@ -111,7 +112,7 @@ struct Values { // Shortcut name std::vector shortcuts; - uint32_t callout_flags; + Settings::Setting callout_flags{0, "calloutFlags"}; // multiplayer settings QString nickname; @@ -127,7 +128,7 @@ struct Values { std::pair, std::vector> ban_list; // logging - bool show_console; + Settings::Setting show_console{false, "showConsole"}; }; extern Values values; diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index e4f8b25e8..52eac11b9 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -99,6 +99,8 @@ add_library(common STATIC scm_rev.cpp scm_rev.h scope_exit.h + settings.cpp + settings.h serialization/atomic.h serialization/boost_discrete_interval.hpp serialization/boost_flat_set.h diff --git a/src/core/settings.cpp b/src/common/settings.cpp similarity index 54% rename from src/core/settings.cpp rename to src/common/settings.cpp index b05389cc1..f6688e1e5 100644 --- a/src/core/settings.cpp +++ b/src/common/settings.cpp @@ -5,6 +5,7 @@ #include #include #include "audio_core/dsp_interface.h" +#include "common/settings.h" #include "core/core.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/shared_page.h" @@ -13,24 +14,24 @@ #include "core/hle/service/ir/ir_rst.h" #include "core/hle/service/ir/ir_user.h" #include "core/hle/service/mic_u.h" -#include "core/settings.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" namespace Settings { Values values = {}; +static bool configuring_global = true; void Apply() { - GDBStub::SetServerPort(values.gdbstub_port); - GDBStub::ToggleServer(values.use_gdbstub); + GDBStub::SetServerPort(values.gdbstub_port.GetValue()); + GDBStub::ToggleServer(values.use_gdbstub.GetValue()); - VideoCore::g_hw_renderer_enabled = values.use_hw_renderer; - VideoCore::g_shader_jit_enabled = values.use_shader_jit; - VideoCore::g_hw_shader_enabled = values.use_hw_shader; - VideoCore::g_separable_shader_enabled = values.separable_shader; - VideoCore::g_hw_shader_accurate_mul = values.shaders_accurate_mul; - VideoCore::g_use_disk_shader_cache = values.use_disk_shader_cache; + VideoCore::g_hw_renderer_enabled = values.use_hw_renderer.GetValue(); + VideoCore::g_shader_jit_enabled = values.use_shader_jit.GetValue(); + VideoCore::g_hw_shader_enabled = values.use_hw_shader.GetValue(); + VideoCore::g_separable_shader_enabled = values.separable_shader.GetValue(); + VideoCore::g_hw_shader_accurate_mul = values.shaders_accurate_mul.GetValue(); + VideoCore::g_use_disk_shader_cache = values.use_disk_shader_cache.GetValue(); #ifndef ANDROID if (VideoCore::g_renderer) { @@ -45,9 +46,9 @@ void Apply() { auto& system = Core::System::GetInstance(); if (system.IsPoweredOn()) { - system.CoreTiming().UpdateClockSpeed(values.cpu_clock_percentage); - Core::DSP().SetSink(values.sink_id, values.audio_device_id); - Core::DSP().EnableStretching(values.enable_audio_stretching); + system.CoreTiming().UpdateClockSpeed(values.cpu_clock_percentage.GetValue()); + Core::DSP().SetSink(values.sink_id.GetValue(), values.audio_device_id.GetValue()); + Core::DSP().EnableStretching(values.enable_audio_stretching.GetValue()); auto hid = Service::HID::GetModule(system); if (hid) { @@ -76,39 +77,47 @@ void LogSettings() { LOG_INFO(Config, "{}: {}", name, value); }; + const auto to_string = [](AudioEmulation emulation) -> std::string_view { + switch (emulation) { + case AudioEmulation::HLE: + return "HLE"; + case AudioEmulation::LLE: + return "LLE"; + case AudioEmulation::LLEMultithreaded: + return "LLE Multithreaded"; + } + }; + LOG_INFO(Config, "Citra Configuration:"); - log_setting("Core_UseCpuJit", values.use_cpu_jit); - log_setting("Core_CPUClockPercentage", values.cpu_clock_percentage); - log_setting("Renderer_UseGLES", values.use_gles); - log_setting("Renderer_UseHwRenderer", values.use_hw_renderer); - log_setting("Renderer_UseHwShader", values.use_hw_shader); - log_setting("Renderer_SeparableShader", values.separable_shader); - log_setting("Renderer_ShadersAccurateMul", values.shaders_accurate_mul); - log_setting("Renderer_UseShaderJit", values.use_shader_jit); - log_setting("Renderer_UseResolutionFactor", values.resolution_factor); - log_setting("Renderer_FrameLimit", values.frame_limit); - log_setting("Renderer_UseFrameLimitAlternate", values.use_frame_limit_alternate); - log_setting("Renderer_FrameLimitAlternate", values.frame_limit_alternate); - log_setting("Renderer_VSyncNew", values.use_vsync_new); - log_setting("Renderer_PostProcessingShader", values.pp_shader_name); - log_setting("Renderer_FilterMode", values.filter_mode); - log_setting("Renderer_TextureFilterName", values.texture_filter_name); - log_setting("Stereoscopy_Render3d", values.render_3d); - log_setting("Stereoscopy_Factor3d", values.factor_3d); - log_setting("Stereoscopy_MonoRenderLeftEye", values.mono_render_left_eye); - log_setting("Layout_LayoutOption", values.layout_option); - log_setting("Layout_SwapScreen", values.swap_screen); - log_setting("Layout_UprightScreen", values.upright_screen); - log_setting("Utility_DumpTextures", values.dump_textures); - log_setting("Utility_CustomTextures", values.custom_textures); - log_setting("Utility_UseDiskShaderCache", values.use_disk_shader_cache); - log_setting("Audio_EnableDspLle", values.enable_dsp_lle); - log_setting("Audio_EnableDspLleMultithread", values.enable_dsp_lle_multithread); - log_setting("Audio_OutputEngine", values.sink_id); - log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching); - log_setting("Audio_OutputDevice", values.audio_device_id); - log_setting("Audio_InputDeviceType", values.mic_input_type); - log_setting("Audio_InputDevice", values.mic_input_device); + log_setting("Core_UseCpuJit", values.use_cpu_jit.GetValue()); + log_setting("Core_CPUClockPercentage", values.cpu_clock_percentage.GetValue()); + log_setting("Renderer_UseGLES", values.use_gles.GetValue()); + log_setting("Renderer_UseHwRenderer", values.use_hw_renderer.GetValue()); + log_setting("Renderer_UseHwShader", values.use_hw_shader.GetValue()); + log_setting("Renderer_SeparableShader", values.separable_shader.GetValue()); + log_setting("Renderer_ShadersAccurateMul", values.shaders_accurate_mul.GetValue()); + log_setting("Renderer_UseShaderJit", values.use_shader_jit.GetValue()); + log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue()); + log_setting("Renderer_FrameLimit", values.frame_limit.GetValue()); + log_setting("Renderer_VSyncNew", values.use_vsync_new.GetValue()); + log_setting("Renderer_PostProcessingShader", values.pp_shader_name.GetValue()); + log_setting("Renderer_FilterMode", values.filter_mode.GetValue()); + log_setting("Renderer_TextureFilterName", values.texture_filter_name.GetValue()); + log_setting("Stereoscopy_Render3d", values.render_3d.GetValue()); + log_setting("Stereoscopy_Factor3d", values.factor_3d.GetValue()); + log_setting("Stereoscopy_MonoRenderLeftEye", values.mono_render_left_eye.GetValue()); + log_setting("Layout_LayoutOption", values.layout_option.GetValue()); + log_setting("Layout_SwapScreen", values.swap_screen.GetValue()); + log_setting("Layout_UprightScreen", values.upright_screen.GetValue()); + log_setting("Utility_DumpTextures", values.dump_textures.GetValue()); + log_setting("Utility_CustomTextures", values.custom_textures.GetValue()); + log_setting("Utility_UseDiskShaderCache", values.use_disk_shader_cache.GetValue()); + log_setting("Audio_Emulation", to_string(values.audio_emulation.GetValue())); + log_setting("Audio_OutputEngine", values.sink_id.GetValue()); + log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); + log_setting("Audio_OutputDevice", values.audio_device_id.GetValue()); + log_setting("Audio_InputDeviceType", values.mic_input_type.GetValue()); + log_setting("Audio_InputDevice", values.mic_input_device.GetValue()); using namespace Service::CAM; log_setting("Camera_OuterRightName", values.camera_name[OuterRightCamera]); log_setting("Camera_OuterRightConfig", values.camera_config[OuterRightCamera]); @@ -119,23 +128,68 @@ void LogSettings() { log_setting("Camera_OuterLeftName", values.camera_name[OuterLeftCamera]); log_setting("Camera_OuterLeftConfig", values.camera_config[OuterLeftCamera]); log_setting("Camera_OuterLeftFlip", values.camera_flip[OuterLeftCamera]); - log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd); - log_setting("DataStorage_UseCustomStorage", values.use_custom_storage); + log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd.GetValue()); + log_setting("DataStorage_UseCustomStorage", values.use_custom_storage.GetValue()); if (values.use_custom_storage) { log_setting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)); log_setting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)); } - log_setting("System_IsNew3ds", values.is_new_3ds); - log_setting("System_RegionValue", values.region_value); - log_setting("Debugging_UseGdbstub", values.use_gdbstub); - log_setting("Debugging_GdbstubPort", values.gdbstub_port); + log_setting("System_IsNew3ds", values.is_new_3ds.GetValue()); + log_setting("System_RegionValue", values.region_value.GetValue()); + log_setting("Debugging_UseGdbstub", values.use_gdbstub.GetValue()); + log_setting("Debugging_GdbstubPort", values.gdbstub_port.GetValue()); +} + +bool IsConfiguringGlobal() { + return configuring_global; +} + +void SetConfiguringGlobal(bool is_global) { + configuring_global = is_global; } float Volume() { if (values.audio_muted) { return 0.0f; } - return values.volume; + return values.volume.GetValue(); +} + +void RestoreGlobalState(bool is_powered_on) { + // If a game is running, DO NOT restore the global settings state + if (is_powered_on) { + return; + } + + // Audio + values.audio_emulation.SetGlobal(true); + values.enable_audio_stretching.SetGlobal(true); + values.volume.SetGlobal(true); + + // Core + values.cpu_clock_percentage.SetGlobal(true); + values.is_new_3ds.SetGlobal(true); + + // Renderer + values.use_hw_renderer.SetGlobal(true); + values.use_hw_shader.SetGlobal(true); + values.separable_shader.SetGlobal(true); + values.use_disk_shader_cache.SetGlobal(true); + values.shaders_accurate_mul.SetGlobal(true); + values.use_vsync_new.SetGlobal(true); + values.resolution_factor.SetGlobal(true); + values.frame_limit.SetGlobal(true); + values.texture_filter_name.SetGlobal(true); + values.layout_option.SetGlobal(true); + values.swap_screen.SetGlobal(true); + values.upright_screen.SetGlobal(true); + values.bg_red.SetGlobal(true); + values.bg_green.SetGlobal(true); + values.bg_blue.SetGlobal(true); + values.render_3d.SetGlobal(true); + values.factor_3d.SetGlobal(true); + values.filter_mode.SetGlobal(true); + values.pp_shader_name.SetGlobal(true); } void LoadProfile(int index) { diff --git a/src/common/settings.h b/src/common/settings.h new file mode 100644 index 000000000..38f69a97e --- /dev/null +++ b/src/common/settings.h @@ -0,0 +1,538 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "common/common_types.h" +#include "core/hle/service/cam/cam_params.h" + +namespace Settings { + +enum class InitClock : u32 { + SystemTime = 0, + FixedTime = 1, +}; + +enum class LayoutOption : u32 { + Default, + SingleScreen, + LargeScreen, + SideScreen, +#ifndef ANDROID + SeparateWindows, +#endif + // Similiar to default, but better for mobile devices in portrait mode. Top screen in clamped to + // the top of the frame, and the bottom screen is enlarged to match the top screen. + MobilePortrait, + + // Similiar to LargeScreen, but better for mobile devices in landscape mode. The screens are + // clamped to the top of the frame, and the bottom screen is a bit bigger. + MobileLandscape, +}; + +enum class MicInputType : u32 { + None = 0, + Real = 1, + Static = 2, +}; + +enum class StereoRenderOption : u32 { + Off = 0, + SideBySide = 1, + Anaglyph = 2, + Interlaced = 3, + ReverseInterlaced = 4, + CardboardVR = 5 +}; + +enum class AudioEmulation : u32 { HLE = 0, LLE = 1, LLEMultithreaded = 2 }; + +namespace NativeButton { + +enum Values { + A, + B, + X, + Y, + Up, + Down, + Left, + Right, + L, + R, + Start, + Select, + Debug, + Gpio14, + + ZL, + ZR, + + Home, + + NumButtons, +}; + +constexpr int BUTTON_HID_BEGIN = A; +constexpr int BUTTON_IR_BEGIN = ZL; +constexpr int BUTTON_NS_BEGIN = Home; + +constexpr int BUTTON_HID_END = BUTTON_IR_BEGIN; +constexpr int BUTTON_IR_END = BUTTON_NS_BEGIN; +constexpr int BUTTON_NS_END = NumButtons; + +constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN; +constexpr int NUM_BUTTONS_IR = BUTTON_IR_END - BUTTON_IR_BEGIN; +constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN; + +static const std::array mapping = {{ + "button_a", + "button_b", + "button_x", + "button_y", + "button_up", + "button_down", + "button_left", + "button_right", + "button_l", + "button_r", + "button_start", + "button_select", + "button_debug", + "button_gpio14", + "button_zl", + "button_zr", + "button_home", +}}; + +} // namespace NativeButton + +namespace NativeAnalog { +enum Values { + CirclePad, + CStick, + NumAnalogs, +}; + +constexpr std::array mapping = {{ + "circle_pad", + "c_stick", +}}; +} // namespace NativeAnalog + +/** The Setting class is a simple resource manager. It defines a label and default value alongside + * the actual value of the setting for simpler and less-error prone use with frontend + * configurations. Specifying a default value and label is required. A minimum and maximum range can + * be specified for sanitization. + */ +template +class Setting { +protected: + Setting() = default; + + /** + * Only sets the setting to the given initializer, leaving the other members to their default + * initializers. + * + * @param global_val Initial value of the setting + */ + explicit Setting(const Type& val) : value{val} {} + +public: + /** + * Sets a default value, label, and setting value. + * + * @param default_val Intial value of the setting, and default value of the setting + * @param name Label for the setting + */ + explicit Setting(const Type& default_val, const std::string& name) requires(!ranged) + : value{default_val}, default_value{default_val}, label{name} {} + virtual ~Setting() = default; + + /** + * Sets a default value, minimum value, maximum value, and label. + * + * @param default_val Intial value of the setting, and default value of the setting + * @param min_val Sets the minimum allowed value of the setting + * @param max_val Sets the maximum allowed value of the setting + * @param name Label for the setting + */ + explicit Setting(const Type& default_val, const Type& min_val, const Type& max_val, + const std::string& name) requires(ranged) + : value{default_val}, + default_value{default_val}, maximum{max_val}, minimum{min_val}, label{name} {} + + /** + * Returns a reference to the setting's value. + * + * @returns A reference to the setting + */ + [[nodiscard]] virtual const Type& GetValue() const { + return value; + } + + /** + * Sets the setting to the given value. + * + * @param val The desired value + */ + virtual void SetValue(const Type& val) { + Type temp{ranged ? std::clamp(val, minimum, maximum) : val}; + std::swap(value, temp); + } + + /** + * Returns the value that this setting was created with. + * + * @returns A reference to the default value + */ + [[nodiscard]] const Type& GetDefault() const { + return default_value; + } + + /** + * Returns the label this setting was created with. + * + * @returns A reference to the label + */ + [[nodiscard]] const std::string& GetLabel() const { + return label; + } + + /** + * Assigns a value to the setting. + * + * @param val The desired setting value + * + * @returns A reference to the setting + */ + virtual const Type& operator=(const Type& val) { + Type temp{ranged ? std::clamp(val, minimum, maximum) : val}; + std::swap(value, temp); + return value; + } + + /** + * Returns a reference to the setting. + * + * @returns A reference to the setting + */ + explicit virtual operator const Type&() const { + return value; + } + +protected: + Type value{}; ///< The setting + const Type default_value{}; ///< The default value + const Type maximum{}; ///< Maximum allowed value of the setting + const Type minimum{}; ///< Minimum allowed value of the setting + const std::string label{}; ///< The setting's label +}; + +/** + * The SwitchableSetting class is a slightly more complex version of the Setting class. This adds a + * custom setting to switch to when a guest application specifically requires it. The effect is that + * other components of the emulator can access the setting's intended value without any need for the + * component to ask whether the custom or global setting is needed at the moment. + * + * By default, the global setting is used. + */ +template +class SwitchableSetting : virtual public Setting { +public: + /** + * Sets a default value, label, and setting value. + * + * @param default_val Intial value of the setting, and default value of the setting + * @param name Label for the setting + */ + explicit SwitchableSetting(const Type& default_val, const std::string& name) requires(!ranged) + : Setting{default_val, name} {} + virtual ~SwitchableSetting() = default; + + /** + * Sets a default value, minimum value, maximum value, and label. + * + * @param default_val Intial value of the setting, and default value of the setting + * @param min_val Sets the minimum allowed value of the setting + * @param max_val Sets the maximum allowed value of the setting + * @param name Label for the setting + */ + explicit SwitchableSetting(const Type& default_val, const Type& min_val, const Type& max_val, + const std::string& name) requires(ranged) + : Setting{default_val, min_val, max_val, name} {} + + /** + * Tells this setting to represent either the global or custom setting when other member + * functions are used. + * + * @param to_global Whether to use the global or custom setting. + */ + void SetGlobal(bool to_global) { + use_global = to_global; + } + + /** + * Returns whether this setting is using the global setting or not. + * + * @returns The global state + */ + [[nodiscard]] bool UsingGlobal() const { + return use_global; + } + + /** + * Returns either the global or custom setting depending on the values of this setting's global + * state or if the global value was specifically requested. + * + * @param need_global Request global value regardless of setting's state; defaults to false + * + * @returns The required value of the setting + */ + [[nodiscard]] virtual const Type& GetValue() const override { + if (use_global) { + return this->value; + } + return custom; + } + [[nodiscard]] virtual const Type& GetValue(bool need_global) const { + if (use_global || need_global) { + return this->value; + } + return custom; + } + + /** + * Sets the current setting value depending on the global state. + * + * @param val The new value + */ + void SetValue(const Type& val) override { + Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val}; + if (use_global) { + std::swap(this->value, temp); + } else { + std::swap(custom, temp); + } + } + + /** + * Assigns the current setting value depending on the global state. + * + * @param val The new value + * + * @returns A reference to the current setting value + */ + const Type& operator=(const Type& val) override { + Type temp{ranged ? std::clamp(val, this->minimum, this->maximum) : val}; + if (use_global) { + std::swap(this->value, temp); + return this->value; + } + std::swap(custom, temp); + return custom; + } + + /** + * Returns the current setting value depending on the global state. + * + * @returns A reference to the current setting value + */ + virtual explicit operator const Type&() const override { + if (use_global) { + return this->value; + } + return custom; + } + +protected: + bool use_global{true}; ///< The setting's global state + Type custom{}; ///< The custom value of the setting +}; + +/** + * The InputSetting class allows for getting a reference to either the global or custom members. + * This is required as we cannot easily modify the values of user-defined types within containers + * using the SetValue() member function found in the Setting class. The primary purpose of this + * class is to store an array of 10 PlayerInput structs for both the global and custom setting and + * allows for easily accessing and modifying both settings. + */ +template +class InputSetting final { +public: + InputSetting() = default; + explicit InputSetting(Type val) : Setting(val) {} + ~InputSetting() = default; + void SetGlobal(bool to_global) { + use_global = to_global; + } + [[nodiscard]] bool UsingGlobal() const { + return use_global; + } + [[nodiscard]] Type& GetValue(bool need_global = false) { + if (use_global || need_global) { + return global; + } + return custom; + } + +private: + bool use_global{true}; ///< The setting's global state + Type global{}; ///< The setting + Type custom{}; ///< The custom setting value +}; + +struct InputProfile { + std::string name; + std::array buttons; + std::array analogs; + std::string motion_device; + std::string touch_device; + bool use_touch_from_button; + int touch_from_button_map_index; + std::string udp_input_address; + u16 udp_input_port; + u8 udp_pad_index; +}; + +struct TouchFromButtonMap { + std::string name; + std::vector buttons; +}; + +/// A special region value indicating that citra will automatically select a region +/// value to fit the region lockout info of the game +static constexpr s32 REGION_VALUE_AUTO_SELECT = -1; + +struct Values { + // Controls + InputProfile current_input_profile; ///< The current input profile + int current_input_profile_index; ///< The current input profile index + std::vector input_profiles; ///< The list of input profiles + std::vector touch_from_button_maps; + + // Core + Setting use_cpu_jit{true, "use_cpu_jit"}; + SwitchableSetting cpu_clock_percentage{100, 5, 400, "cpu_clock_percentage"}; + SwitchableSetting is_new_3ds{true, "is_new_3ds"}; + + // Data Storage + Setting use_virtual_sd{true, "use_virtual_sd"}; + Setting use_custom_storage{false, "use_custom_storage"}; + + // System + SwitchableSetting region_value{REGION_VALUE_AUTO_SELECT, "region_value"}; + Setting init_clock{InitClock::SystemTime, "init_clock"}; + Setting init_time{946681277ULL, "init_time"}; + Setting init_time_offset{0, "init_time_offset"}; + + // Renderer + Setting use_gles{false, "use_gles"}; + SwitchableSetting use_hw_renderer{true, "use_hw_renderer"}; + SwitchableSetting use_hw_shader{true, "use_hw_shader"}; + SwitchableSetting separable_shader{false, "use_separable_shader"}; + SwitchableSetting use_disk_shader_cache{true, "use_disk_shader_cache"}; + SwitchableSetting shaders_accurate_mul{true, "shaders_accurate_mul"}; + SwitchableSetting use_vsync_new{true, "use_vsync_new"}; + Setting use_shader_jit{true, "use_shader_jit"}; + SwitchableSetting resolution_factor{1, 1, 10, "resolution_factor"}; + SwitchableSetting frame_limit{100, 0, 1000, "frame_limit"}; + SwitchableSetting texture_filter_name{"none", "texture_filter_name"}; + + SwitchableSetting layout_option{LayoutOption::Default, "layout_option"}; + SwitchableSetting swap_screen{false, "swap_screen"}; + SwitchableSetting upright_screen{false, "upright_screen"}; + Setting custom_layout{false, "custom_layout"}; + Setting custom_top_left{0, "custom_top_left"}; + Setting custom_top_top{0, "custom_top_top"}; + Setting custom_top_right{400, "custom_top_right"}; + Setting custom_top_bottom{240, "custom_top_bottom"}; + Setting custom_bottom_left{40, "custom_bottom_left"}; + Setting custom_bottom_top{240, "custom_bottom_top"}; + Setting custom_bottom_right{360, "custom_bottom_right"}; + Setting custom_bottom_bottom{480, "custom_bottom_bottom"}; + + SwitchableSetting bg_red{0.f, "bg_red"}; + SwitchableSetting bg_green{0.f, "bg_green"}; + SwitchableSetting bg_blue{0.f, "bg_blue"}; + + SwitchableSetting render_3d{StereoRenderOption::Off, "render_3d"}; + SwitchableSetting factor_3d{0, "factor_3d"}; + + Setting mono_render_left_eye{false, "mono_render_left_eye"}; + Setting cardboard_screen_size{85, "cardboard_screen_size"}; + Setting cardboard_x_shift{0, "cardboard_x_shift"}; + Setting cardboard_y_shift{0, "cardboard_y_shift"}; + + SwitchableSetting filter_mode{true, "filter_mode"}; + SwitchableSetting pp_shader_name{"none (builtin)", "pp_shader_name"}; + + Setting dump_textures{false, "dump_textures"}; + Setting custom_textures{false, "custom_textures"}; + Setting preload_textures{false, "preload_textures"}; + + // Audio + bool audio_muted; + SwitchableSetting audio_emulation{AudioEmulation::HLE, "audio_emulation"}; + Setting sink_id{"auto", "output_engine"}; + SwitchableSetting enable_audio_stretching{true, "enable_audio_stretching"}; + Setting audio_device_id{"auto", "output_device"}; + SwitchableSetting volume{1.f, 0.f, 1.f, "volume"}; + Setting mic_input_type{MicInputType::None, "mic_input_type"}; + Setting mic_input_device{"Default", "mic_input_device"}; + + // Camera + std::array camera_name; + std::array camera_config; + std::array camera_flip; + + // Debugging + bool record_frame_times; + std::unordered_map lle_modules; + Setting use_gdbstub{false, "use_gdbstub"}; + Setting gdbstub_port{24689, "gdbstub_port"}; + + // Miscellaneous + Setting log_filter{"*:Info", "log_filter"}; + + // Video Dumping + std::string output_format; + std::string format_options; + + std::string video_encoder; + std::string video_encoder_options; + u64 video_bitrate; + + std::string audio_encoder; + std::string audio_encoder_options; + u64 audio_bitrate; +}; + +extern Values values; + +bool IsConfiguringGlobal(); +void SetConfiguringGlobal(bool is_global); + +float Volume(); + +void Apply(); +void LogSettings(); + +// Restore the global state of all applicable settings in the Values struct +void RestoreGlobalState(bool is_powered_on); + +// Input profiles +void LoadProfile(int index); +void SaveProfile(int index); +void CreateProfile(std::string name); +void DeleteProfile(int index); +void RenameCurrentProfile(std::string new_name); + +} // namespace Settings diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index d57aef53e..fb9b78600 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -454,8 +454,6 @@ add_library(core STATIC rpc/udp_server.h savestate.cpp savestate.h - settings.cpp - settings.h telemetry_session.cpp telemetry_session.h tracer/citrace.h diff --git a/src/core/arm/exclusive_monitor.cpp b/src/core/arm/exclusive_monitor.cpp index 5c8a3bc20..53906eff9 100644 --- a/src/core/arm/exclusive_monitor.cpp +++ b/src/core/arm/exclusive_monitor.cpp @@ -4,9 +4,9 @@ #if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64) #include "core/arm/dynarmic/arm_exclusive_monitor.h" #endif +#include "common/settings.h" #include "core/arm/exclusive_monitor.h" #include "core/memory.h" -#include "core/settings.h" namespace Core { diff --git a/src/core/cheats/gateway_cheat.h b/src/core/cheats/gateway_cheat.h index 46512fb96..14c81786b 100644 --- a/src/core/cheats/gateway_cheat.h +++ b/src/core/cheats/gateway_cheat.h @@ -6,6 +6,8 @@ #include #include +#include +#include "common/common_types.h" #include "core/cheats/cheat_base.h" namespace Cheats { diff --git a/src/core/core.cpp b/src/core/core.cpp index e409265dc..6d2bd6682 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -25,6 +25,7 @@ #ifdef ENABLE_FFMPEG_VIDEO_DUMPER #include "core/dumping/ffmpeg_backend.h" #endif +#include "common/settings.h" #include "core/custom_tex_cache.h" #include "core/gdbstub/gdbstub.h" #include "core/global.h" @@ -45,7 +46,6 @@ #include "core/loader/loader.h" #include "core/movie.h" #include "core/rpc/rpc_server.h" -#include "core/settings.h" #include "network/network.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" @@ -365,7 +365,7 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, memory = std::make_unique(); - timing = std::make_unique(num_cores, Settings::values.cpu_clock_percentage); + timing = std::make_unique(num_cores, Settings::values.cpu_clock_percentage.GetValue()); kernel = std::make_unique( *memory, *timing, [this] { PrepareReschedule(); }, system_mode, num_cores, n3ds_mode); @@ -395,17 +395,19 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, kernel->SetCPUs(cpu_cores); kernel->SetRunningCPU(cpu_cores[0].get()); - if (Settings::values.enable_dsp_lle) { - dsp_core = std::make_unique(*memory, - Settings::values.enable_dsp_lle_multithread); - } else { + const auto audio_emulation = Settings::values.audio_emulation.GetValue(); + if (audio_emulation == Settings::AudioEmulation::HLE) { dsp_core = std::make_unique(*memory); + } else { + const bool multithread = audio_emulation == Settings::AudioEmulation::LLEMultithreaded; + dsp_core = std::make_unique(*memory, multithread); } memory->SetDSP(*dsp_core); - dsp_core->SetSink(Settings::values.sink_id, Settings::values.audio_device_id); - dsp_core->EnableStretching(Settings::values.enable_audio_stretching); + dsp_core->SetSink(Settings::values.sink_id.GetValue(), + Settings::values.audio_device_id.GetValue()); + dsp_core->EnableStretching(Settings::values.enable_audio_stretching.GetValue()); telemetry_session = std::make_unique(); diff --git a/src/core/dumping/ffmpeg_backend.cpp b/src/core/dumping/ffmpeg_backend.cpp index cad321d87..5f38d02fd 100644 --- a/src/core/dumping/ffmpeg_backend.cpp +++ b/src/core/dumping/ffmpeg_backend.cpp @@ -7,10 +7,10 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/param_package.h" +#include "common/settings.h" #include "common/string_util.h" #include "core/dumping/ffmpeg_backend.h" #include "core/hw/gpu.h" -#include "core/settings.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index 4c3ce6d69..81db641e4 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp @@ -7,11 +7,11 @@ #include "common/archives.h" #include "common/file_util.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/file_sys/archive_sdmc.h" #include "core/file_sys/disk_archive.h" #include "core/file_sys/errors.h" #include "core/file_sys/path_parser.h" -#include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/file_sys/archive_sdmcwriteonly.cpp b/src/core/file_sys/archive_sdmcwriteonly.cpp index 241e93f0a..8272ce789 100644 --- a/src/core/file_sys/archive_sdmcwriteonly.cpp +++ b/src/core/file_sys/archive_sdmcwriteonly.cpp @@ -5,11 +5,11 @@ #include #include "common/archives.h" #include "common/file_util.h" +#include "common/settings.h" #include "core/file_sys/archive_sdmcwriteonly.h" #include "core/file_sys/directory_backend.h" #include "core/file_sys/errors.h" #include "core/file_sys/file_backend.h" -#include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 1144c049f..2222ece02 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -4,9 +4,10 @@ #include #include +#include "common/settings.h" +#include "core/3ds.h" #include "core/frontend/emu_window.h" #include "core/frontend/input.h" -#include "core/settings.h" namespace Frontend { /// We need a global touch state that is shared across the different window instances @@ -63,14 +64,14 @@ EmuWindow::~EmuWindow() = default; */ static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, unsigned framebuffer_x, unsigned framebuffer_y) { - if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) { + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) { return (framebuffer_y >= layout.bottom_screen.top && framebuffer_y < layout.bottom_screen.bottom && ((framebuffer_x >= layout.bottom_screen.left / 2 && framebuffer_x < layout.bottom_screen.right / 2) || (framebuffer_x >= (layout.bottom_screen.left / 2) + (layout.width / 2) && framebuffer_x < (layout.bottom_screen.right / 2) + (layout.width / 2)))); - } else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) { + } else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) { return (framebuffer_y >= layout.bottom_screen.top && framebuffer_y < layout.bottom_screen.bottom && ((framebuffer_x >= layout.bottom_screen.left && @@ -88,13 +89,13 @@ static bool IsWithinTouchscreen(const Layout::FramebufferLayout& layout, unsigne std::tuple EmuWindow::ClipToTouchScreen(unsigned new_x, unsigned new_y) const { if (new_x >= framebuffer_layout.width / 2) { - if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) new_x -= framebuffer_layout.width / 2; - else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) + else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) new_x -= (framebuffer_layout.width / 2) - (framebuffer_layout.cardboard.user_x_shift * 2); } - if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) { + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) { new_x = std::max(new_x, framebuffer_layout.bottom_screen.left / 2); new_x = std::min(new_x, framebuffer_layout.bottom_screen.right / 2 - 1); } else { @@ -122,14 +123,14 @@ bool EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { return false; if (framebuffer_x >= framebuffer_layout.width / 2) { - if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) framebuffer_x -= framebuffer_layout.width / 2; - else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) + else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) framebuffer_x -= (framebuffer_layout.width / 2) - (framebuffer_layout.cardboard.user_x_shift * 2); } std::lock_guard guard(touch_state->mutex); - if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) { + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::SideBySide) { touch_state->touch_x = static_cast(framebuffer_x - framebuffer_layout.bottom_screen.left / 2) / (framebuffer_layout.bottom_screen.right / 2 - @@ -173,55 +174,59 @@ void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height, bool is_portrait_mode) { Layout::FramebufferLayout layout; const auto layout_option = Settings::values.layout_option; - const auto min_size = - Layout::GetMinimumSizeFromLayout(layout_option, Settings::values.upright_screen); + const auto min_size = Layout::GetMinimumSizeFromLayout( + layout_option.GetValue(), Settings::values.upright_screen.GetValue()); - if (Settings::values.custom_layout == true) { + if (Settings::values.custom_layout.GetValue() == true) { layout = Layout::CustomFrameLayout(width, height); } else { width = std::max(width, min_size.first); height = std::max(height, min_size.second); // If in portrait mode, only the MobilePortrait option really makes sense - const Settings::LayoutOption layout_option = is_portrait_mode - ? Settings::LayoutOption::MobilePortrait - : Settings::values.layout_option; + const Settings::LayoutOption layout_option = + is_portrait_mode ? Settings::LayoutOption::MobilePortrait + : Settings::values.layout_option.GetValue(); switch (layout_option) { case Settings::LayoutOption::SingleScreen: - layout = Layout::SingleFrameLayout(width, height, Settings::values.swap_screen, - Settings::values.upright_screen); + layout = + Layout::SingleFrameLayout(width, height, Settings::values.swap_screen.GetValue(), + Settings::values.upright_screen.GetValue()); break; case Settings::LayoutOption::LargeScreen: - layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen, - Settings::values.upright_screen); + layout = + Layout::LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), + Settings::values.upright_screen.GetValue()); break; case Settings::LayoutOption::SideScreen: - layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen, - Settings::values.upright_screen); + layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen.GetValue(), + Settings::values.upright_screen.GetValue()); break; #ifndef ANDROID case Settings::LayoutOption::SeparateWindows: layout = Layout::SeparateWindowsLayout(width, height, is_secondary, - Settings::values.upright_screen); + Settings::values.upright_screen.GetValue()); break; #endif case Settings::LayoutOption::MobilePortrait: - layout = Layout::MobilePortraitFrameLayout(width, height, Settings::values.swap_screen); + layout = Layout::MobilePortraitFrameLayout(width, height, + Settings::values.swap_screen.GetValue()); break; case Settings::LayoutOption::MobileLandscape: - layout = Layout::MobileLandscapeFrameLayout(width, height, Settings::values.swap_screen, - 2.25f, false); + layout = Layout::MobileLandscapeFrameLayout( + width, height, Settings::values.swap_screen.GetValue(), 2.25f, false); break; case Settings::LayoutOption::Default: default: - layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen, - Settings::values.upright_screen); + layout = + Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen.GetValue(), + Settings::values.upright_screen.GetValue()); break; } UpdateMinimumWindowSize(min_size); } - if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) { + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) { layout = Layout::GetCardboardSettings(layout); } NotifyFramebufferLayoutChanged(layout); diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index ad476edcc..4b3512d6a 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -5,9 +5,9 @@ #include #include "common/assert.h" +#include "common/settings.h" #include "core/3ds.h" #include "core/frontend/framebuffer_layout.h" -#include "core/settings.h" namespace Layout { @@ -355,12 +355,14 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height) { FramebufferLayout res{width, height, true, true, {}, {}, !Settings::values.upright_screen}; - Common::Rectangle top_screen{ - Settings::values.custom_top_left, Settings::values.custom_top_top, - Settings::values.custom_top_right, Settings::values.custom_top_bottom}; - Common::Rectangle bot_screen{ - Settings::values.custom_bottom_left, Settings::values.custom_bottom_top, - Settings::values.custom_bottom_right, Settings::values.custom_bottom_bottom}; + Common::Rectangle top_screen{Settings::values.custom_top_left.GetValue(), + Settings::values.custom_top_top.GetValue(), + Settings::values.custom_top_right.GetValue(), + Settings::values.custom_top_bottom.GetValue()}; + Common::Rectangle bot_screen{Settings::values.custom_bottom_left.GetValue(), + Settings::values.custom_bottom_top.GetValue(), + Settings::values.custom_bottom_right.GetValue(), + Settings::values.custom_bottom_bottom.GetValue()}; res.top_screen = top_screen; res.bottom_screen = bot_screen; @@ -369,20 +371,21 @@ FramebufferLayout CustomFrameLayout(u32 width, u32 height) { FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondary) { FramebufferLayout layout; - if (Settings::values.custom_layout == true) { - layout = CustomFrameLayout( - std::max(Settings::values.custom_top_right, Settings::values.custom_bottom_right), - std::max(Settings::values.custom_top_bottom, Settings::values.custom_bottom_bottom)); + if (Settings::values.custom_layout.GetValue() == true) { + layout = CustomFrameLayout(std::max(Settings::values.custom_top_right.GetValue(), + Settings::values.custom_bottom_right.GetValue()), + std::max(Settings::values.custom_top_bottom.GetValue(), + Settings::values.custom_bottom_bottom.GetValue())); } else { int width, height; - switch (Settings::values.layout_option) { + switch (Settings::values.layout_option.GetValue()) { case Settings::LayoutOption::SingleScreen: #ifndef ANDROID case Settings::LayoutOption::SeparateWindows: #endif { - const bool swap_screens = is_secondary || Settings::values.swap_screen; - if (Settings::values.upright_screen) { + const bool swap_screens = is_secondary || Settings::values.swap_screen.GetValue(); + if (Settings::values.upright_screen.GetValue()) { if (swap_screens) { width = Core::kScreenBottomHeight * res_scale; height = Core::kScreenBottomWidth * res_scale; @@ -399,13 +402,13 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar height = Core::kScreenTopHeight * res_scale; } } - layout = - SingleFrameLayout(width, height, swap_screens, Settings::values.upright_screen); + layout = SingleFrameLayout(width, height, swap_screens, + Settings::values.upright_screen.GetValue()); break; } case Settings::LayoutOption::LargeScreen: - if (Settings::values.upright_screen) { - if (Settings::values.swap_screen) { + if (Settings::values.upright_screen.GetValue()) { + if (Settings::values.swap_screen.GetValue()) { width = Core::kScreenBottomHeight * res_scale; height = (Core::kScreenBottomWidth + Core::kScreenTopWidth / 4) * res_scale; } else { @@ -413,7 +416,7 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar height = (Core::kScreenTopWidth + Core::kScreenBottomWidth / 4) * res_scale; } } else { - if (Settings::values.swap_screen) { + if (Settings::values.swap_screen.GetValue()) { width = (Core::kScreenBottomWidth + Core::kScreenTopWidth / 4) * res_scale; height = Core::kScreenBottomHeight * res_scale; } else { @@ -421,51 +424,52 @@ FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale, bool is_secondar height = Core::kScreenTopHeight * res_scale; } } - layout = LargeFrameLayout(width, height, Settings::values.swap_screen, - Settings::values.upright_screen); + layout = LargeFrameLayout(width, height, Settings::values.swap_screen.GetValue(), + Settings::values.upright_screen.GetValue()); break; case Settings::LayoutOption::SideScreen: - if (Settings::values.upright_screen) { + if (Settings::values.upright_screen.GetValue()) { width = Core::kScreenTopHeight * res_scale; height = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale; } else { width = (Core::kScreenTopWidth + Core::kScreenBottomWidth) * res_scale; height = Core::kScreenTopHeight * res_scale; } - layout = SideFrameLayout(width, height, Settings::values.swap_screen, - Settings::values.upright_screen); + layout = SideFrameLayout(width, height, Settings::values.swap_screen.GetValue(), + Settings::values.upright_screen.GetValue()); break; case Settings::LayoutOption::MobilePortrait: width = Core::kScreenTopWidth * res_scale; height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; - layout = MobilePortraitFrameLayout(width, height, Settings::values.swap_screen); + layout = + MobilePortraitFrameLayout(width, height, Settings::values.swap_screen.GetValue()); break; case Settings::LayoutOption::MobileLandscape: - if (Settings::values.swap_screen) { + if (Settings::values.swap_screen.GetValue()) { width = (Core::kScreenBottomWidth + Core::kScreenTopWidth / 2.25f) * res_scale; height = Core::kScreenBottomHeight * res_scale; } else { width = (Core::kScreenTopWidth + Core::kScreenBottomWidth / 2.25f) * res_scale; height = Core::kScreenTopHeight * res_scale; } - layout = MobileLandscapeFrameLayout(width, height, Settings::values.swap_screen, 2.25f, - false); + layout = MobileLandscapeFrameLayout( + width, height, Settings::values.swap_screen.GetValue(), 2.25f, false); break; case Settings::LayoutOption::Default: default: - if (Settings::values.upright_screen) { + if (Settings::values.upright_screen.GetValue()) { width = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; height = Core::kScreenTopWidth * res_scale; } else { width = Core::kScreenTopWidth * res_scale; height = (Core::kScreenTopHeight + Core::kScreenBottomHeight) * res_scale; } - layout = DefaultFrameLayout(width, height, Settings::values.swap_screen, - Settings::values.upright_screen); + layout = DefaultFrameLayout(width, height, Settings::values.swap_screen.GetValue(), + Settings::values.upright_screen.GetValue()); break; } } - if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) { + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::CardboardVR) { layout = Layout::GetCardboardSettings(layout); } return layout; @@ -478,17 +482,17 @@ FramebufferLayout GetCardboardSettings(FramebufferLayout layout) { float bottom_screen_left = 0; float bottom_screen_top = 0; - float cardboardScreenScale = Settings::values.cardboard_screen_size / 100.0f; + float cardboardScreenScale = Settings::values.cardboard_screen_size.GetValue() / 100.0f; float top_screen_width = layout.top_screen.GetWidth() / 2.0f * cardboardScreenScale; float top_screen_height = layout.top_screen.GetHeight() / 2.0f * cardboardScreenScale; float bottom_screen_width = layout.bottom_screen.GetWidth() / 2.0f * cardboardScreenScale; float bottom_screen_height = layout.bottom_screen.GetHeight() / 2.0f * cardboardScreenScale; - bool is_swapped = Settings::values.swap_screen; - bool is_portrait = layout.height > layout.width; + const bool is_swapped = Settings::values.swap_screen.GetValue(); + const bool is_portrait = layout.height > layout.width; float cardboardScreenWidth; float cardboardScreenHeight; - switch (Settings::values.layout_option) { + switch (Settings::values.layout_option.GetValue()) { case Settings::LayoutOption::MobileLandscape: case Settings::LayoutOption::SideScreen: // If orientation is portrait, only use MobilePortrait @@ -524,9 +528,11 @@ FramebufferLayout GetCardboardSettings(FramebufferLayout layout) { break; } float cardboardMaxXShift = (layout.width / 2.0f - cardboardScreenWidth) / 2.0f; - float cardboardUserXShift = (Settings::values.cardboard_x_shift / 100.0f) * cardboardMaxXShift; + float cardboardUserXShift = + (Settings::values.cardboard_x_shift.GetValue() / 100.0f) * cardboardMaxXShift; float cardboardMaxYShift = ((float)layout.height - cardboardScreenHeight) / 2.0f; - float cardboardUserYShift = (Settings::values.cardboard_y_shift / 100.0f) * cardboardMaxYShift; + float cardboardUserYShift = + (Settings::values.cardboard_y_shift.GetValue() / 100.0f) * cardboardMaxYShift; // Center the screens and apply user Y shift newLayout.top_screen.left = top_screen_left + cardboardMaxXShift; diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index 4566407f6..92b43f285 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -5,7 +5,7 @@ #pragma once #include "common/math_util.h" -#include "core/settings.h" +#include "common/settings.h" namespace Layout { diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index 7928aa55a..cdef9d996 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp @@ -11,6 +11,7 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" #include "core/hle/kernel/config_mem.h" #include "core/hle/kernel/memory.h" @@ -19,7 +20,6 @@ #include "core/hle/kernel/vm_manager.h" #include "core/hle/result.h" #include "core/memory.h" -#include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -52,7 +52,7 @@ enum N3DSMode : u8 { void KernelSystem::MemoryInit(u32 mem_type, u8 n3ds_mode) { ASSERT(mem_type != 1); - const bool is_new_3ds = Settings::values.is_new_3ds; + const bool is_new_3ds = Settings::values.is_new_3ds.GetValue(); u32 reported_mem_type = mem_type; if (is_new_3ds) { if (n3ds_mode == MemoryMode::Mode6 || n3ds_mode == MemoryMode::Mode6_2) { diff --git a/src/core/hle/kernel/shared_page.cpp b/src/core/hle/kernel/shared_page.cpp index 6deebf6b1..0bc839804 100644 --- a/src/core/hle/kernel/shared_page.cpp +++ b/src/core/hle/kernel/shared_page.cpp @@ -6,12 +6,12 @@ #include #include "common/archives.h" #include "common/assert.h" +#include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/shared_page.h" #include "core/hle/service/ptm/ptm.h" #include "core/movie.h" -#include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -37,7 +37,7 @@ static std::chrono::seconds GetInitTime() { return std::chrono::seconds(override_init_time); } - switch (Settings::values.init_clock) { + switch (Settings::values.init_clock.GetValue()) { case Settings::InitClock::SystemTime: { auto now = std::chrono::system_clock::now(); // If the system time is in daylight saving, we give an additional hour to console time @@ -47,7 +47,7 @@ static std::chrono::seconds GetInitTime() { now = now + std::chrono::hours(1); // add the offset - s64 init_time_offset = Settings::values.init_time_offset; + s64 init_time_offset = Settings::values.init_time_offset.GetValue(); long long days_offset = init_time_offset / 86400; long long days_offset_in_seconds = days_offset * 86400; // h/m/s truncated unsigned long long seconds_offset = @@ -58,9 +58,9 @@ static std::chrono::seconds GetInitTime() { return std::chrono::duration_cast(now.time_since_epoch()); } case Settings::InitClock::FixedTime: - return std::chrono::seconds(Settings::values.init_time); + return std::chrono::seconds(Settings::values.init_time.GetValue()); default: - UNREACHABLE_MSG("Invalid InitClock value ({})", Settings::values.init_clock); + UNREACHABLE_MSG("Invalid InitClock value ({})", Settings::values.init_clock.GetValue()); } } @@ -85,7 +85,7 @@ Handler::Handler(Core::Timing& timing) : timing(timing) { std::bind(&Handler::UpdateTimeCallback, this, _1, _2)); timing.ScheduleEvent(0, update_time_event, 0, 0); - float slidestate = Settings::values.factor_3d / 100.0f; + float slidestate = Settings::values.factor_3d.GetValue() / 100.0f; shared_page.sliderstate_3d = static_cast(slidestate); } diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index bb39074a2..ce094a8fe 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -8,6 +8,7 @@ #include "common/common_paths.h" #include "common/file_util.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" #include "core/file_sys/archive_ncch.h" #include "core/file_sys/file_backend.h" @@ -28,7 +29,6 @@ #include "core/hle/service/service.h" #include "core/hw/aes/ccm.h" #include "core/hw/aes/key.h" -#include "core/settings.h" SERVICE_CONSTRUCT_IMPL(Service::APT::Module) diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp index abd3a7ee5..2f4725bc0 100644 --- a/src/core/hle/service/cam/cam.cpp +++ b/src/core/hle/service/cam/cam.cpp @@ -6,6 +6,7 @@ #include "common/archives.h" #include "common/bit_set.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" #include "core/frontend/camera/factory.h" @@ -19,7 +20,6 @@ #include "core/hle/service/cam/cam_s.h" #include "core/hle/service/cam/cam_u.h" #include "core/memory.h" -#include "core/settings.h" SERVICE_CONSTRUCT_IMPL(Service::CAM::Module) diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index df1665236..0ed45156e 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -12,6 +12,7 @@ #include "common/archives.h" #include "common/file_util.h" #include "common/logging/log.h" +#include "common/settings.h" #include "common/string_util.h" #include "common/swap.h" #include "core/core.h" @@ -25,7 +26,6 @@ #include "core/hle/service/cfg/cfg_nor.h" #include "core/hle/service/cfg/cfg_s.h" #include "core/hle/service/cfg/cfg_u.h" -#include "core/settings.h" SERIALIZE_EXPORT_IMPL(Service::CFG::Module) @@ -189,10 +189,10 @@ void Module::Interface::GetCountryCodeID(Kernel::HLERequestContext& ctx) { } u32 Module::GetRegionValue() { - if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) + if (Settings::values.region_value.GetValue() == Settings::REGION_VALUE_AUTO_SELECT) return preferred_region_code; - return Settings::values.region_value; + return Settings::values.region_value.GetValue(); } void Module::Interface::SecureInfoGetRegion(Kernel::HLERequestContext& ctx, u16 id) { @@ -654,7 +654,7 @@ void Module::SetPreferredRegionCodes(const std::vector& region_codes) { preferred_region_code = region; LOG_INFO(Service_CFG, "Preferred region code set to {}", preferred_region_code); - if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) { + if (Settings::values.region_value.GetValue() == Settings::REGION_VALUE_AUTO_SELECT) { if (current_language != adjusted_language) { LOG_WARNING(Service_CFG, "System language {} does not fit the region. Adjusted to {}", current_language, adjusted_language); diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index af07e0866..8670788e3 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -9,6 +9,7 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/scope_exit.h" +#include "common/settings.h" #include "common/string_util.h" #include "core/core.h" #include "core/file_sys/errors.h" @@ -25,7 +26,6 @@ #include "core/hle/service/am/am.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/fs_user.h" -#include "core/settings.h" SERVICE_CONSTRUCT_IMPL(Service::FS::FS_USER) SERIALIZE_EXPORT_IMPL(Service::FS::FS_USER) @@ -350,7 +350,7 @@ void FS_USER::IsSdmcDetected(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp(ctx, 0x817, 0, 0); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); - rb.Push(Settings::values.use_virtual_sd); + rb.Push(Settings::values.use_virtual_sd.GetValue()); } void FS_USER::IsSdmcWriteable(Kernel::HLERequestContext& ctx) { @@ -358,7 +358,7 @@ void FS_USER::IsSdmcWriteable(Kernel::HLERequestContext& ctx) { IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); // If the SD isn't enabled, it can't be writeable...else, stubbed true - rb.Push(Settings::values.use_virtual_sd); + rb.Push(Settings::values.use_virtual_sd.GetValue()); LOG_DEBUG(Service_FS, " (STUBBED)"); } diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index aba6c8291..3fb9d22af 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -218,8 +218,9 @@ void Module::UpdatePadCallback(std::uintptr_t user_data, s64 cycles_late) { // TODO(xperia64): How the 3D Slider is updated by the HID module needs to be RE'd // and possibly moved to its own Core::Timing event. - mem->pad.sliderstate_3d = (Settings::values.factor_3d / 100.0f); - system.Kernel().GetSharedPageHandler().Set3DSlider(Settings::values.factor_3d / 100.0f); + mem->pad.sliderstate_3d = (Settings::values.factor_3d.GetValue() / 100.0f); + system.Kernel().GetSharedPageHandler().Set3DSlider(Settings::values.factor_3d.GetValue() / + 100.0f); // Reschedule recurrent event system.CoreTiming().ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event); @@ -406,7 +407,7 @@ void Module::Interface::GetGyroscopeLowCalibrateParam(Kernel::HLERequestContext& void Module::Interface::GetSoundVolume(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx, 0x17, 0, 0}; - const u8 volume = static_cast(0x3F * Settings::values.volume); + const u8 volume = static_cast(0x3F * Settings::values.volume.GetValue()); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); rb.Push(RESULT_SUCCESS); diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index c0f74ced1..609cb9276 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -13,10 +13,10 @@ #include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/settings.h" #include "core/core_timing.h" #include "core/frontend/input.h" #include "core/hle/service/service.h" -#include "core/settings.h" namespace Core { class System; diff --git a/src/core/hle/service/ir/extra_hid.cpp b/src/core/hle/service/ir/extra_hid.cpp index 777615d7f..5e7511033 100644 --- a/src/core/hle/service/ir/extra_hid.cpp +++ b/src/core/hle/service/ir/extra_hid.cpp @@ -3,12 +3,12 @@ // Refer to the license.txt file included. #include "common/alignment.h" +#include "common/settings.h" #include "common/string_util.h" #include "core/core.h" #include "core/core_timing.h" #include "core/hle/service/ir/extra_hid.h" #include "core/movie.h" -#include "core/settings.h" namespace Service::IR { diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp index 7372bec5e..d5e475468 100644 --- a/src/core/hle/service/ir/ir_rst.cpp +++ b/src/core/hle/service/ir/ir_rst.cpp @@ -5,6 +5,7 @@ #include #include #include "common/archives.h" +#include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" @@ -13,7 +14,6 @@ #include "core/hle/service/hid/hid.h" #include "core/hle/service/ir/ir_rst.h" #include "core/movie.h" -#include "core/settings.h" SERIALIZE_EXPORT_IMPL(Service::IR::IR_RST) SERVICE_CONSTRUCT_IMPL(Service::IR::IR_RST) diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index d95bcc890..f2e41519c 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp @@ -5,6 +5,7 @@ #include #include "common/archives.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" #include "core/frontend/mic.h" #include "core/hle/ipc.h" @@ -14,7 +15,6 @@ #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/mic_u.h" -#include "core/settings.h" SERVICE_CONSTRUCT_IMPL(Service::MIC::MIC_U) SERIALIZE_EXPORT_IMPL(Service::MIC::MIC_U) @@ -350,12 +350,12 @@ struct MIC_U::Impl { void CreateMic() { std::unique_ptr new_mic; - switch (Settings::values.mic_input_type) { + switch (Settings::values.mic_input_type.GetValue()) { case Settings::MicInputType::None: new_mic = std::make_unique(); break; case Settings::MicInputType::Real: - new_mic = Frontend::Mic::CreateRealMic(Settings::values.mic_input_device); + new_mic = Frontend::Mic::CreateRealMic(Settings::values.mic_input_device.GetValue()); break; case Settings::MicInputType::Static: new_mic = std::make_unique(); diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp index 9c4c09fad..8278b0c55 100644 --- a/src/core/hle/service/ptm/ptm.cpp +++ b/src/core/hle/service/ptm/ptm.cpp @@ -7,6 +7,7 @@ #include "common/common_paths.h" #include "common/file_util.h" #include "common/logging/log.h" +#include "common/settings.h" #include "core/core.h" #include "core/file_sys/archive_extsavedata.h" #include "core/file_sys/errors.h" @@ -17,7 +18,6 @@ #include "core/hle/service/ptm/ptm_sets.h" #include "core/hle/service/ptm/ptm_sysm.h" #include "core/hle/service/ptm/ptm_u.h" -#include "core/settings.h" SERIALIZE_EXPORT_IMPL(Service::PTM::Module) @@ -118,7 +118,7 @@ void Module::Interface::GetSoftwareClosedFlag(Kernel::HLERequestContext& ctx) { } void CheckNew3DS(IPC::RequestBuilder& rb) { - const bool is_new_3ds = Settings::values.is_new_3ds; + const bool is_new_3ds = Settings::values.is_new_3ds.GetValue(); rb.Push(RESULT_SUCCESS); rb.Push(is_new_3ds); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 91f8d154b..8e98ee99c 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -12,6 +12,7 @@ #include "common/atomic_ops.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "common/settings.h" #include "common/swap.h" #include "core/arm/arm_interface.h" #include "core/core.h" @@ -20,7 +21,6 @@ #include "core/hle/kernel/process.h" #include "core/hle/lock.h" #include "core/memory.h" -#include "core/settings.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" @@ -289,7 +289,7 @@ private: friend class boost::serialization::access; template void serialize(Archive& ar, const unsigned int file_version) { - bool save_n3ds_ram = Settings::values.is_new_3ds; + bool save_n3ds_ram = Settings::values.is_new_3ds.GetValue(); ar& save_n3ds_ram; ar& boost::serialization::make_binary_object(vram.get(), Memory::VRAM_SIZE); ar& boost::serialization::make_binary_object( diff --git a/src/core/movie.cpp b/src/core/movie.cpp index cf74cc4ef..2348fe827 100644 --- a/src/core/movie.cpp +++ b/src/core/movie.cpp @@ -601,8 +601,8 @@ void Movie::PrepareForPlayback(const std::string& movie_file) { } void Movie::PrepareForRecording() { - if (Settings::values.init_clock == Settings::InitClock::SystemTime) { - long long init_time_offset = Settings::values.init_time_offset; + if (Settings::values.init_clock.GetValue() == Settings::InitClock::SystemTime) { + long long init_time_offset = Settings::values.init_time_offset.GetValue(); long long days_offset = init_time_offset / 86400; unsigned long long seconds_offset = std::abs(init_time_offset) - std::abs(days_offset * 86400); @@ -610,7 +610,7 @@ void Movie::PrepareForRecording() { init_time = Common::Timer::GetTimeSinceJan1970().count() + seconds_offset + (days_offset * 86400); } else { - init_time = Settings::values.init_time; + init_time = Settings::values.init_time.GetValue(); } } diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 5b426c83b..6dffec97f 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp @@ -12,9 +12,9 @@ #include #include #include "common/file_util.h" +#include "common/settings.h" #include "core/hw/gpu.h" #include "core/perf_stats.h" -#include "core/settings.h" using namespace std::chrono_literals; using DoubleSecs = std::chrono::duration; @@ -136,14 +136,9 @@ void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { } auto now = Clock::now(); - double sleep_scale = Settings::values.frame_limit / 100.0; + double sleep_scale = Settings::values.frame_limit.GetValue() / 100.0; - if (Settings::values.use_frame_limit_alternate) { - if (Settings::values.frame_limit_alternate == 0) { - return; - } - sleep_scale = Settings::values.frame_limit_alternate / 100.0; - } else if (Settings::values.frame_limit == 0) { + if (Settings::values.frame_limit.GetValue() == 0) { return; } diff --git a/src/core/settings.h b/src/core/settings.h deleted file mode 100644 index accb39071..000000000 --- a/src/core/settings.h +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include -#include -#include -#include -#include -#include "common/common_types.h" -#include "core/hle/service/cam/cam_params.h" - -namespace Settings { - -enum class InitClock { - SystemTime = 0, - FixedTime = 1, -}; - -enum class LayoutOption { - Default, - SingleScreen, - LargeScreen, - SideScreen, -#ifndef ANDROID - SeparateWindows, -#endif - // Similiar to default, but better for mobile devices in portrait mode. Top screen in clamped to - // the top of the frame, and the bottom screen is enlarged to match the top screen. - MobilePortrait, - - // Similiar to LargeScreen, but better for mobile devices in landscape mode. The screens are - // clamped to the top of the frame, and the bottom screen is a bit bigger. - MobileLandscape, -}; - -enum class MicInputType { - None, - Real, - Static, -}; - -enum class StereoRenderOption { - Off, - SideBySide, - Anaglyph, - Interlaced, - ReverseInterlaced, - CardboardVR -}; - -namespace NativeButton { -enum Values { - A, - B, - X, - Y, - Up, - Down, - Left, - Right, - L, - R, - Start, - Select, - Debug, - Gpio14, - - ZL, - ZR, - - Home, - - NumButtons, -}; - -constexpr int BUTTON_HID_BEGIN = A; -constexpr int BUTTON_IR_BEGIN = ZL; -constexpr int BUTTON_NS_BEGIN = Home; - -constexpr int BUTTON_HID_END = BUTTON_IR_BEGIN; -constexpr int BUTTON_IR_END = BUTTON_NS_BEGIN; -constexpr int BUTTON_NS_END = NumButtons; - -constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN; -constexpr int NUM_BUTTONS_IR = BUTTON_IR_END - BUTTON_IR_BEGIN; -constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN; - -static const std::array mapping = {{ - "button_a", - "button_b", - "button_x", - "button_y", - "button_up", - "button_down", - "button_left", - "button_right", - "button_l", - "button_r", - "button_start", - "button_select", - "button_debug", - "button_gpio14", - "button_zl", - "button_zr", - "button_home", -}}; -} // namespace NativeButton - -namespace NativeAnalog { -enum Values { - CirclePad, - CStick, - - NumAnalogs, -}; - -static const std::array mapping = {{ - "circle_pad", - "c_stick", -}}; -} // namespace NativeAnalog - -struct InputProfile { - std::string name; - std::array buttons; - std::array analogs; - std::string motion_device; - std::string touch_device; - bool use_touch_from_button; - int touch_from_button_map_index; - std::string udp_input_address; - u16 udp_input_port; - u8 udp_pad_index; -}; - -struct TouchFromButtonMap { - std::string name; - std::vector buttons; -}; - -struct Values { - // CheckNew3DS - bool is_new_3ds; - - // Controls - InputProfile current_input_profile; ///< The current input profile - int current_input_profile_index; ///< The current input profile index - std::vector input_profiles; ///< The list of input profiles - std::vector touch_from_button_maps; - - // Core - bool use_cpu_jit; - int cpu_clock_percentage; - - // Data Storage - bool use_virtual_sd; - bool use_custom_storage; - - // System - int region_value; - InitClock init_clock; - u64 init_time; - s64 init_time_offset; - - // Renderer - bool use_gles; - bool use_hw_renderer; - bool use_hw_shader; - bool separable_shader; - bool use_disk_shader_cache; - bool shaders_accurate_mul; - bool use_shader_jit; - u16 resolution_factor; - bool use_frame_limit_alternate; - u16 frame_limit; - u16 frame_limit_alternate; - std::string texture_filter_name; - - LayoutOption layout_option; - bool swap_screen; - bool upright_screen; - bool custom_layout; - u16 custom_top_left; - u16 custom_top_top; - u16 custom_top_right; - u16 custom_top_bottom; - u16 custom_bottom_left; - u16 custom_bottom_top; - u16 custom_bottom_right; - u16 custom_bottom_bottom; - - float bg_red; - float bg_green; - float bg_blue; - - StereoRenderOption render_3d; - std::atomic factor_3d; - - bool mono_render_left_eye; - - int cardboard_screen_size; - int cardboard_x_shift; - int cardboard_y_shift; - - bool filter_mode; - std::string pp_shader_name; - - bool dump_textures; - bool custom_textures; - bool preload_textures; - - bool use_vsync_new; - - // Audio - bool audio_muted; - bool enable_dsp_lle; - bool enable_dsp_lle_multithread; - std::string sink_id; - bool enable_audio_stretching; - std::string audio_device_id; - float volume; - MicInputType mic_input_type; - std::string mic_input_device; - - // Camera - std::array camera_name; - std::array camera_config; - std::array camera_flip; - - // Debugging - bool record_frame_times; - bool use_gdbstub; - u16 gdbstub_port; - std::string log_filter; - std::unordered_map lle_modules; - - // Video Dumping - std::string output_format; - std::string format_options; - - std::string video_encoder; - std::string video_encoder_options; - u64 video_bitrate; - - std::string audio_encoder; - std::string audio_encoder_options; - u64 audio_bitrate; -} extern values; - -float Volume(); - -// a special value for Values::region_value indicating that citra will automatically select a region -// value to fit the region lockout info of the game -static constexpr int REGION_VALUE_AUTO_SELECT = -1; - -void Apply(); -void LogSettings(); - -// Input profiles -void LoadProfile(int index); -void SaveProfile(int index); -void CreateProfile(std::string name); -void DeleteProfile(int index); -void RenameCurrentProfile(std::string new_name); -} // namespace Settings diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index bd84f50d7..1092777b1 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -9,8 +9,8 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/scm_rev.h" +#include "common/settings.h" #include "core/core.h" -#include "core/settings.h" #include "core/telemetry_session.h" #include "network/network_settings.h" @@ -124,35 +124,37 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) { Telemetry::AppendOSInfo(field_collection); // Log user configuration information - AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId", Settings::values.sink_id); + AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId", Settings::values.sink_id.GetValue()); AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching", - Settings::values.enable_audio_stretching); - AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", Settings::values.use_cpu_jit); + Settings::values.enable_audio_stretching.GetValue()); + AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", + Settings::values.use_cpu_jit.GetValue()); AddField(Telemetry::FieldType::UserConfig, "Renderer_ResolutionFactor", - Settings::values.resolution_factor); - AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", Settings::values.frame_limit); - AddField(Telemetry::FieldType::UserConfig, "Renderer_UseFrameLimitAlternate", - Settings::values.use_frame_limit_alternate); - AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimitAlternate", - Settings::values.frame_limit_alternate); + Settings::values.resolution_factor.GetValue()); + AddField(Telemetry::FieldType::UserConfig, "Renderer_FrameLimit", + Settings::values.frame_limit.GetValue()); AddField(Telemetry::FieldType::UserConfig, "Renderer_UseHwRenderer", - Settings::values.use_hw_renderer); + Settings::values.use_hw_renderer.GetValue()); AddField(Telemetry::FieldType::UserConfig, "Renderer_UseHwShader", - Settings::values.use_hw_shader); + Settings::values.use_hw_shader.GetValue()); AddField(Telemetry::FieldType::UserConfig, "Renderer_ShadersAccurateMul", - Settings::values.shaders_accurate_mul); + Settings::values.shaders_accurate_mul.GetValue()); AddField(Telemetry::FieldType::UserConfig, "Renderer_UseShaderJit", - Settings::values.use_shader_jit); - AddField(Telemetry::FieldType::UserConfig, "Renderer_UseVsync", Settings::values.use_vsync_new); - AddField(Telemetry::FieldType::UserConfig, "Renderer_FilterMode", Settings::values.filter_mode); + Settings::values.use_shader_jit.GetValue()); + AddField(Telemetry::FieldType::UserConfig, "Renderer_UseVsync", + Settings::values.use_vsync_new.GetValue()); + AddField(Telemetry::FieldType::UserConfig, "Renderer_FilterMode", + Settings::values.filter_mode.GetValue()); AddField(Telemetry::FieldType::UserConfig, "Renderer_Render3d", - static_cast(Settings::values.render_3d)); + static_cast(Settings::values.render_3d.GetValue())); AddField(Telemetry::FieldType::UserConfig, "Renderer_Factor3d", - Settings::values.factor_3d.load()); + Settings::values.factor_3d.GetValue()); AddField(Telemetry::FieldType::UserConfig, "Renderer_MonoRenderLeftEye", - Settings::values.mono_render_left_eye); - AddField(Telemetry::FieldType::UserConfig, "System_IsNew3ds", Settings::values.is_new_3ds); - AddField(Telemetry::FieldType::UserConfig, "System_RegionValue", Settings::values.region_value); + Settings::values.mono_render_left_eye.GetValue()); + AddField(Telemetry::FieldType::UserConfig, "System_IsNew3ds", + Settings::values.is_new_3ds.GetValue()); + AddField(Telemetry::FieldType::UserConfig, "System_RegionValue", + Settings::values.region_value.GetValue()); } bool TelemetrySession::SubmitTestcase() { diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h index 113e200f9..297749c9f 100644 --- a/src/input_common/gcadapter/gc_poller.h +++ b/src/input_common/gcadapter/gc_poller.h @@ -5,8 +5,8 @@ #pragma once #include +#include "common/settings.h" #include "core/frontend/input.h" -#include "core/settings.h" #include "input_common/gcadapter/gc_adapter.h" #include "input_common/main.h" diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h index 0be659cd0..bb6671951 100644 --- a/src/input_common/sdl/sdl_impl.h +++ b/src/input_common/sdl/sdl_impl.h @@ -7,8 +7,8 @@ #include #include #include +#include "common/settings.h" #include "common/threadsafe_queue.h" -#include "core/settings.h" #include "input_common/sdl/sdl.h" union SDL_Event; diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp index b213a57c1..9cd6c0204 100644 --- a/src/input_common/touch_from_button.cpp +++ b/src/input_common/touch_from_button.cpp @@ -2,8 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/settings.h" #include "core/3ds.h" -#include "core/settings.h" #include "input_common/touch_from_button.h" namespace InputCommon { diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp index c1d417abb..89e114fee 100644 --- a/src/input_common/udp/udp.cpp +++ b/src/input_common/udp/udp.cpp @@ -6,8 +6,8 @@ #include #include #include "common/param_package.h" +#include "common/settings.h" #include "core/frontend/input.h" -#include "core/settings.h" #include "input_common/udp/client.h" #include "input_common/udp/udp.h" diff --git a/src/video_core/rasterizer_cache/rasterizer_cache.cpp b/src/video_core/rasterizer_cache/rasterizer_cache.cpp index 5fec2bae6..394bcca00 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache.cpp +++ b/src/video_core/rasterizer_cache/rasterizer_cache.cpp @@ -241,8 +241,8 @@ static Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& RasterizerCacheOpenGL::RasterizerCacheOpenGL() { resolution_scale_factor = VideoCore::GetResolutionScaleFactor(); - texture_filterer = std::make_unique(Settings::values.texture_filter_name, - resolution_scale_factor); + texture_filterer = std::make_unique( + Settings::values.texture_filter_name.GetValue(), resolution_scale_factor); format_reinterpreter = std::make_unique(); texture_downloader_es = std::make_unique(false); } @@ -592,7 +592,7 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( resolution_scale_factor != VideoCore::GetResolutionScaleFactor(); const bool texture_filter_changed = VideoCore::g_texture_filter_update_requested.exchange(false) && - texture_filterer->Reset(Settings::values.texture_filter_name, + texture_filterer->Reset(Settings::values.texture_filter_name.GetValue(), VideoCore::GetResolutionScaleFactor()); if (resolution_scale_changed || texture_filter_changed) { diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index f2b0757d3..1fd2950a4 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp @@ -11,10 +11,10 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/scm_rev.h" +#include "common/settings.h" #include "common/zstd_compression.h" #include "core/core.h" #include "core/hle/kernel/process.h" -#include "core/settings.h" #include "video_core/renderer_opengl/gl_shader_disk_cache.h" namespace OpenGL { diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index a10b970b5..1e1126345 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -5,6 +5,7 @@ #include #include "common/logging/log.h" #include "common/microprofile.h" +#include "common/settings.h" #include "core/core.h" #include "core/dumping/backend.h" #include "core/frontend/emu_window.h" @@ -12,7 +13,6 @@ #include "core/hw/hw.h" #include "core/hw/lcd.h" #include "core/memory.h" -#include "core/settings.h" #include "core/tracer/recorder.h" #include "video_core/debug_utils/debug_utils.h" #include "video_core/rasterizer_interface.h" @@ -381,7 +381,7 @@ void RendererOpenGL::SwapBuffers() { RenderToMailbox(main_layout, render_window.mailbox, false); #ifndef ANDROID - if (Settings::values.layout_option == Settings::LayoutOption::SeparateWindows) { + if (Settings::values.layout_option.GetValue() == Settings::LayoutOption::SeparateWindows) { ASSERT(secondary_window); const auto& secondary_layout = secondary_window->GetFramebufferLayout(); RenderToMailbox(secondary_layout, secondary_window->mailbox, false); @@ -618,8 +618,8 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color * Initializes the OpenGL state and creates persistent objects. */ void RendererOpenGL::InitOpenGLObjects() { - glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, - 0.0f); + glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(), + Settings::values.bg_blue.GetValue(), 0.0f); filter_sampler.Create(); ReloadSampler(); @@ -685,12 +685,13 @@ void RendererOpenGL::ReloadShader() { if (GLES) { shader_data += fragment_shader_precision_OES; } - if (Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph) { - if (Settings::values.pp_shader_name == "dubois (builtin)") { + + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph) { + if (Settings::values.pp_shader_name.GetValue() == "dubois (builtin)") { shader_data += fragment_shader_anaglyph; } else { - std::string shader_text = - OpenGL::GetPostProcessingShaderCode(true, Settings::values.pp_shader_name); + std::string shader_text = OpenGL::GetPostProcessingShaderCode( + true, Settings::values.pp_shader_name.GetValue()); if (shader_text.empty()) { // Should probably provide some information that the shader couldn't load shader_data += fragment_shader_anaglyph; @@ -698,13 +699,14 @@ void RendererOpenGL::ReloadShader() { shader_data += shader_text; } } - } else if (Settings::values.render_3d == Settings::StereoRenderOption::Interlaced || - Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced) { - if (Settings::values.pp_shader_name == "horizontal (builtin)") { + } else if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Interlaced || + Settings::values.render_3d.GetValue() == + Settings::StereoRenderOption::ReverseInterlaced) { + if (Settings::values.pp_shader_name.GetValue() == "horizontal (builtin)") { shader_data += fragment_shader_interlaced; } else { - std::string shader_text = - OpenGL::GetPostProcessingShaderCode(false, Settings::values.pp_shader_name); + std::string shader_text = OpenGL::GetPostProcessingShaderCode( + false, Settings::values.pp_shader_name.GetValue()); if (shader_text.empty()) { // Should probably provide some information that the shader couldn't load shader_data += fragment_shader_interlaced; @@ -713,11 +715,11 @@ void RendererOpenGL::ReloadShader() { } } } else { - if (Settings::values.pp_shader_name == "none (builtin)") { + if (Settings::values.pp_shader_name.GetValue() == "none (builtin)") { shader_data += fragment_shader; } else { - std::string shader_text = - OpenGL::GetPostProcessingShaderCode(false, Settings::values.pp_shader_name); + std::string shader_text = OpenGL::GetPostProcessingShaderCode( + false, Settings::values.pp_shader_name.GetValue()); if (shader_text.empty()) { // Should probably provide some information that the shader couldn't load shader_data += fragment_shader; @@ -731,16 +733,17 @@ void RendererOpenGL::ReloadShader() { state.Apply(); uniform_modelview_matrix = glGetUniformLocation(shader.handle, "modelview_matrix"); uniform_color_texture = glGetUniformLocation(shader.handle, "color_texture"); - if (Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph || - Settings::values.render_3d == Settings::StereoRenderOption::Interlaced || - Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced) { + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph || + Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Interlaced || + Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::ReverseInterlaced) { uniform_color_texture_r = glGetUniformLocation(shader.handle, "color_texture_r"); } - if (Settings::values.render_3d == Settings::StereoRenderOption::Interlaced || - Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced) { + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Interlaced || + Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::ReverseInterlaced) { GLuint uniform_reverse_interlaced = glGetUniformLocation(shader.handle, "reverse_interlaced"); - if (Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced) + if (Settings::values.render_3d.GetValue() == + Settings::StereoRenderOption::ReverseInterlaced) glUniform1i(uniform_reverse_interlaced, 1); else glUniform1i(uniform_reverse_interlaced, 0); @@ -958,8 +961,8 @@ void RendererOpenGL::DrawSingleScreenStereo(const ScreenInfo& screen_info_l, void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool flipped) { if (VideoCore::g_renderer_bg_color_update_requested.exchange(false)) { // Update background color before drawing - glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue, - 0.0f); + glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(), + Settings::values.bg_blue.GetValue(), 0.0f); } if (VideoCore::g_renderer_sampler_update_requested.exchange(false)) { @@ -989,9 +992,9 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f glUniform1i(uniform_color_texture, 0); const bool stereo_single_screen = - Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph || - Settings::values.render_3d == Settings::StereoRenderOption::Interlaced || - Settings::values.render_3d == Settings::StereoRenderOption::ReverseInterlaced; + Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Anaglyph || + Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Interlaced || + Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::ReverseInterlaced; // Bind a second texture for the right eye if in Anaglyph mode if (stereo_single_screen) { @@ -1001,12 +1004,13 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f glUniform1i(uniform_layer, 0); if (layout.top_screen_enabled) { if (layout.is_rotated) { - if (Settings::values.render_3d == Settings::StereoRenderOption::Off) { + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) { int eye = Settings::values.mono_render_left_eye ? 0 : 1; DrawSingleScreenRotated(screen_infos[eye], (float)top_screen.left, (float)top_screen.top, (float)top_screen.GetWidth(), (float)top_screen.GetHeight()); - } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) { + } else if (Settings::values.render_3d.GetValue() == + Settings::StereoRenderOption::SideBySide) { DrawSingleScreenRotated(screen_infos[0], (float)top_screen.left / 2, (float)top_screen.top, (float)top_screen.GetWidth() / 2, (float)top_screen.GetHeight()); @@ -1015,7 +1019,8 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f ((float)top_screen.left / 2) + ((float)layout.width / 2), (float)top_screen.top, (float)top_screen.GetWidth() / 2, (float)top_screen.GetHeight()); - } else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) { + } else if (Settings::values.render_3d.GetValue() == + Settings::StereoRenderOption::CardboardVR) { DrawSingleScreenRotated(screen_infos[0], layout.top_screen.left, layout.top_screen.top, layout.top_screen.GetWidth(), layout.top_screen.GetHeight()); @@ -1031,11 +1036,12 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f (float)top_screen.GetWidth(), (float)top_screen.GetHeight()); } } else { - if (Settings::values.render_3d == Settings::StereoRenderOption::Off) { - int eye = Settings::values.mono_render_left_eye ? 0 : 1; + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) { + int eye = Settings::values.mono_render_left_eye.GetValue() ? 0 : 1; DrawSingleScreen(screen_infos[eye], (float)top_screen.left, (float)top_screen.top, (float)top_screen.GetWidth(), (float)top_screen.GetHeight()); - } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) { + } else if (Settings::values.render_3d.GetValue() == + Settings::StereoRenderOption::SideBySide) { DrawSingleScreen(screen_infos[0], (float)top_screen.left / 2, (float)top_screen.top, (float)top_screen.GetWidth() / 2, (float)top_screen.GetHeight()); glUniform1i(uniform_layer, 1); @@ -1043,7 +1049,8 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f ((float)top_screen.left / 2) + ((float)layout.width / 2), (float)top_screen.top, (float)top_screen.GetWidth() / 2, (float)top_screen.GetHeight()); - } else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) { + } else if (Settings::values.render_3d.GetValue() == + Settings::StereoRenderOption::CardboardVR) { DrawSingleScreen(screen_infos[0], layout.top_screen.left, layout.top_screen.top, layout.top_screen.GetWidth(), layout.top_screen.GetHeight()); glUniform1i(uniform_layer, 1); @@ -1061,11 +1068,12 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f glUniform1i(uniform_layer, 0); if (layout.bottom_screen_enabled) { if (layout.is_rotated) { - if (Settings::values.render_3d == Settings::StereoRenderOption::Off) { + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) { DrawSingleScreenRotated(screen_infos[2], (float)bottom_screen.left, (float)bottom_screen.top, (float)bottom_screen.GetWidth(), (float)bottom_screen.GetHeight()); - } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) { + } else if (Settings::values.render_3d.GetValue() == + Settings::StereoRenderOption::SideBySide) { DrawSingleScreenRotated( screen_infos[2], (float)bottom_screen.left / 2, (float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2, (float)bottom_screen.GetHeight()); @@ -1074,7 +1082,8 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f screen_infos[2], ((float)bottom_screen.left / 2) + ((float)layout.width / 2), (float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2, (float)bottom_screen.GetHeight()); - } else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) { + } else if (Settings::values.render_3d.GetValue() == + Settings::StereoRenderOption::CardboardVR) { DrawSingleScreenRotated(screen_infos[2], layout.bottom_screen.left, layout.bottom_screen.top, layout.bottom_screen.GetWidth(), layout.bottom_screen.GetHeight()); @@ -1091,11 +1100,12 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f (float)bottom_screen.GetHeight()); } } else { - if (Settings::values.render_3d == Settings::StereoRenderOption::Off) { + if (Settings::values.render_3d.GetValue() == Settings::StereoRenderOption::Off) { DrawSingleScreen(screen_infos[2], (float)bottom_screen.left, (float)bottom_screen.top, (float)bottom_screen.GetWidth(), (float)bottom_screen.GetHeight()); - } else if (Settings::values.render_3d == Settings::StereoRenderOption::SideBySide) { + } else if (Settings::values.render_3d.GetValue() == + Settings::StereoRenderOption::SideBySide) { DrawSingleScreen(screen_infos[2], (float)bottom_screen.left / 2, (float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2, (float)bottom_screen.GetHeight()); @@ -1104,7 +1114,8 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout, bool f ((float)bottom_screen.left / 2) + ((float)layout.width / 2), (float)bottom_screen.top, (float)bottom_screen.GetWidth() / 2, (float)bottom_screen.GetHeight()); - } else if (Settings::values.render_3d == Settings::StereoRenderOption::CardboardVR) { + } else if (Settings::values.render_3d.GetValue() == + Settings::StereoRenderOption::CardboardVR) { DrawSingleScreen(screen_infos[2], layout.bottom_screen.left, layout.bottom_screen.top, layout.bottom_screen.GetWidth(), layout.bottom_screen.GetHeight()); diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp index 53388f020..fdf7bcc3e 100644 --- a/src/video_core/video_core.cpp +++ b/src/video_core/video_core.cpp @@ -5,7 +5,7 @@ #include #include "common/archives.h" #include "common/logging/log.h" -#include "core/settings.h" +#include "common/settings.h" #include "video_core/pica.h" #include "video_core/pica_state.h" #include "video_core/renderer_base.h" @@ -44,7 +44,7 @@ ResultStatus Init(Frontend::EmuWindow& emu_window, Frontend::EmuWindow* secondar g_memory = &memory; Pica::Init(); - OpenGL::GLES = Settings::values.use_gles; + OpenGL::GLES = Settings::values.use_gles.GetValue(); g_renderer = std::make_unique(emu_window, secondary_window); ResultStatus result = g_renderer->Init(); @@ -82,8 +82,8 @@ void RequestScreenshot(void* data, std::function callback, u16 GetResolutionScaleFactor() { if (g_hw_renderer_enabled) { - return Settings::values.resolution_factor - ? Settings::values.resolution_factor + return Settings::values.resolution_factor.GetValue() + ? Settings::values.resolution_factor.GetValue() : g_renderer->GetRenderWindow().GetFramebufferLayout().GetScalingRatio(); } else { // Software renderer always render at native resolution