// Copyright Citra Emulator Project / Azahar 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 "audio_core/input_details.h" #include "audio_core/sink_details.h" #include "common/common_types.h" #include "common/setting_keys.h" #include "common/string_util.h" #include "core/hle/service/cam/cam_params.h" namespace Settings { enum class GraphicsAPI { Software = 0, OpenGL = 1, Vulkan = 2, }; enum class InitClock : u32 { SystemTime = 0, FixedTime = 1, }; enum class InitTicks : u32 { Random = 0, Fixed = 1, }; /** Defines the layout option for desktop and mobile landscape */ enum class LayoutOption : u32 { // Shouldn't these have set numbers to prevent last two from // shifting? -OS Default, SingleScreen, LargeScreen, SideScreen, #ifndef ANDROID SeparateWindows, #endif HybridScreen, CustomLayout, }; /** Defines the layout option for mobile portrait */ enum class PortraitLayoutOption : u32 { // formerly mobile portrait PortraitTopFullWidth, PortraitCustomLayout, PortraitOriginal }; enum class SecondaryDisplayLayout : u32 { None, TopScreenOnly, BottomScreenOnly, SideBySide }; /** Defines where the small screen will appear relative to the large screen * when in Large Screen mode */ enum class SmallScreenPosition : u32 { TopRight, MiddleRight, BottomRight, TopLeft, MiddleLeft, BottomLeft, AboveLarge, BelowLarge }; enum class StereoRenderOption : u32 { Off = 0, SideBySide = 1, SideBySideFull = 2, Anaglyph = 3, Interlaced = 4, ReverseInterlaced = 5, CardboardVR = 6 }; // Which eye to render when 3d is off. 800px wide mode could be added here in the future, when // implemented enum class MonoRenderOption : u32 { LeftEye = 0, RightEye = 1, }; // on android, which displays to render stereo mode to enum class StereoWhichDisplay : u32 { None = 0, // equivalent to StereoRenderOption = Off Both = 1, PrimaryOnly = 2, SecondaryOnly = 3 }; enum class AudioEmulation : u32 { HLE = 0, LLE = 1, LLEMultithreaded = 2, }; enum class TextureFilter : u32 { NoFilter = 0, Anime4K = 1, Bicubic = 2, ScaleForce = 3, xBRZ = 4, MMPX = 5, }; enum class TextureSampling : u32 { GameControlled = 0, NearestNeighbor = 1, Linear = 2, }; enum class AspectRatio : u32 { Default = 0, R16_9 = 1, R4_3 = 2, R21_9 = 3, R16_10 = 4, Stretch = 5, }; namespace NativeButton { enum Values { A, B, X, Y, Up, Down, Left, Right, L, R, Start, Select, Debug, Gpio14, ZL, ZR, Home, Power, NumButtons, }; constexpr int BUTTON_HID_BEGIN = A; constexpr int BUTTON_IR_BEGIN = ZL; constexpr int BUTTON_NS_BEGIN = Power; 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", "button_power", }}; } // 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_view& 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_view& name) requires(ranged) : value{default_val}, default_value{default_val}, maximum{max_val}, minimum{min_val}, label{std::string(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_view& 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_view& 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 }; struct InputProfile { std::string name; std::array buttons; std::array analogs; std::string motion_device; std::string touch_device; std::string controller_touch_device; bool use_touchpad; 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; Setting use_artic_base_controller{false, Keys::use_artic_base_controller}; SwitchableSetting enable_gamemode{true, Keys::enable_gamemode}; // Core Setting use_cpu_jit{true, Keys::use_cpu_jit}; SwitchableSetting cpu_clock_percentage{100, 5, 400, Keys::cpu_clock_percentage}; SwitchableSetting is_new_3ds{true, Keys::is_new_3ds}; SwitchableSetting lle_applets{true, Keys::lle_applets}; SwitchableSetting deterministic_async_operations{false, Keys::deterministic_async_operations}; SwitchableSetting enable_required_online_lle_modules{ false, Keys::enable_required_online_lle_modules}; // Data Storage Setting use_virtual_sd{true, Keys::use_virtual_sd}; Setting use_custom_storage{false, Keys::use_custom_storage}; Setting compress_cia_installs{false, Keys::compress_cia_installs}; // System SwitchableSetting region_value{REGION_VALUE_AUTO_SELECT, Keys::region_value}; Setting init_clock{InitClock::SystemTime, Keys::init_clock}; Setting init_time{946681277ULL, Keys::init_time}; Setting init_time_offset{0, Keys::init_time_offset}; Setting init_ticks_type{InitTicks::Random, Keys::init_ticks_type}; Setting init_ticks_override{0, Keys::init_ticks_override}; Setting plugin_loader_enabled{false, Keys::plugin_loader}; Setting allow_plugin_loader{true, Keys::allow_plugin_loader}; Setting steps_per_hour{0, Keys::steps_per_hour}; Setting apply_region_free_patch{true, Keys::apply_region_free_patch}; // Renderer // clang-format off SwitchableSetting graphics_api{ #if defined(ANDROID) && defined(ENABLE_VULKAN) // Prefer Vulkan on Android, OpenGL on everything else GraphicsAPI::Vulkan, #elif defined(ENABLE_OPENGL) GraphicsAPI::OpenGL, #elif defined(ENABLE_VULKAN) GraphicsAPI::Vulkan, #elif defined(ENABLE_SOFTWARE_RENDERER) GraphicsAPI::Software, #else // TODO: Add a null renderer backend for this, perhaps. #error "At least one renderer must be enabled." #endif GraphicsAPI::Software, GraphicsAPI::Vulkan, Keys::graphics_api}; // clang-format on SwitchableSetting physical_device{0, Keys::physical_device}; Setting use_gles{false, Keys::use_gles}; Setting renderer_debug{false, Keys::renderer_debug}; Setting dump_command_buffers{false, Keys::dump_command_buffers}; SwitchableSetting spirv_shader_gen{true, Keys::spirv_shader_gen}; SwitchableSetting disable_spirv_optimizer{true, Keys::disable_spirv_optimizer}; SwitchableSetting async_shader_compilation{false, Keys::async_shader_compilation}; SwitchableSetting async_presentation{true, Keys::async_presentation}; SwitchableSetting use_hw_shader{true, Keys::use_hw_shader}; SwitchableSetting use_disk_shader_cache{true, Keys::use_disk_shader_cache}; SwitchableSetting shaders_accurate_mul{true, Keys::shaders_accurate_mul}; #ifdef ANDROID // TODO: Fuck this -OS SwitchableSetting use_vsync{false, Keys::use_vsync}; #else SwitchableSetting use_vsync{true, Keys::use_vsync}; #endif SwitchableSetting use_display_refresh_rate_detection{ true, Keys::use_display_refresh_rate_detection}; Setting use_shader_jit{true, Keys::use_shader_jit}; SwitchableSetting resolution_factor{1, 0, 10, Keys::resolution_factor}; SwitchableSetting use_integer_scaling{false, Keys::use_integer_scaling}; SwitchableSetting frame_limit{100, 0, 1000, Keys::frame_limit}; SwitchableSetting turbo_limit{200, 0, 1000, Keys::turbo_limit}; SwitchableSetting texture_filter{TextureFilter::NoFilter, Keys::texture_filter}; SwitchableSetting texture_sampling{TextureSampling::GameControlled, Keys::texture_sampling}; SwitchableSetting delay_game_render_thread_us{0, 0, 16000, Keys::delay_game_render_thread_us}; SwitchableSetting layout_option{LayoutOption::Default, Keys::layout_option}; SwitchableSetting swap_screen{false, Keys::swap_screen}; SwitchableSetting upright_screen{false, Keys::upright_screen}; SwitchableSetting secondary_display_layout{ SecondaryDisplayLayout::None, Keys::secondary_display_layout}; SwitchableSetting> layouts_to_cycle{ {LayoutOption::Default, LayoutOption::SingleScreen, LayoutOption::LargeScreen, LayoutOption::SideScreen, #ifndef ANDROID LayoutOption::SeparateWindows, #endif LayoutOption::HybridScreen, LayoutOption::CustomLayout}, Keys::layouts_to_cycle}; SwitchableSetting large_screen_proportion{4.f, 1.f, 16.f, Keys::large_screen_proportion}; SwitchableSetting screen_gap{0, Keys::screen_gap}; SwitchableSetting small_screen_position{SmallScreenPosition::BottomRight, Keys::small_screen_position}; Setting custom_top_x{0, Keys::custom_top_x}; Setting custom_top_y{0, Keys::custom_top_y}; Setting custom_top_width{800, Keys::custom_top_width}; Setting custom_top_height{480, Keys::custom_top_height}; Setting custom_bottom_x{80, Keys::custom_bottom_x}; Setting custom_bottom_y{500, Keys::custom_bottom_y}; Setting custom_bottom_width{640, Keys::custom_bottom_width}; Setting custom_bottom_height{480, Keys::custom_bottom_height}; Setting custom_second_layer_opacity{100, Keys::custom_second_layer_opacity}; SwitchableSetting aspect_ratio{AspectRatio::Default, Keys::aspect_ratio}; SwitchableSetting screen_top_stretch{false, Keys::screen_top_stretch}; Setting screen_top_leftright_padding{0, Keys::screen_top_leftright_padding}; Setting screen_top_topbottom_padding{0, Keys::screen_top_topbottom_padding}; SwitchableSetting screen_bottom_stretch{false, Keys::screen_bottom_stretch}; Setting screen_bottom_leftright_padding{0, Keys::screen_bottom_leftright_padding}; Setting screen_bottom_topbottom_padding{0, Keys::screen_bottom_topbottom_padding}; SwitchableSetting portrait_layout_option{ PortraitLayoutOption::PortraitTopFullWidth, Keys::portrait_layout_option}; Setting custom_portrait_top_x{0, Keys::custom_portrait_top_x}; Setting custom_portrait_top_y{0, Keys::custom_portrait_top_y}; Setting custom_portrait_top_width{800, Keys::custom_portrait_top_width}; Setting custom_portrait_top_height{480, Keys::custom_portrait_top_height}; Setting custom_portrait_bottom_x{80, Keys::custom_portrait_bottom_x}; Setting custom_portrait_bottom_y{500, Keys::custom_portrait_bottom_y}; Setting custom_portrait_bottom_width{640, Keys::custom_portrait_bottom_width}; Setting custom_portrait_bottom_height{480, Keys::custom_portrait_bottom_height}; SwitchableSetting bg_red{0.f, Keys::bg_red}; SwitchableSetting bg_green{0.f, Keys::bg_green}; SwitchableSetting bg_blue{0.f, Keys::bg_blue}; SwitchableSetting render_3d{StereoRenderOption::Off, Keys::render_3d}; SwitchableSetting factor_3d{0, Keys::factor_3d}; SwitchableSetting swap_eyes_3d{false, Keys::swap_eyes_3d}; SwitchableSetting render_3d_which_display{StereoWhichDisplay::None, Keys::render_3d_which_display}; SwitchableSetting mono_render_option{MonoRenderOption::LeftEye, Keys::mono_render_option}; Setting cardboard_screen_size{85, Keys::cardboard_screen_size}; Setting cardboard_x_shift{0, Keys::cardboard_x_shift}; Setting cardboard_y_shift{0, Keys::cardboard_y_shift}; SwitchableSetting filter_mode{true, Keys::filter_mode}; SwitchableSetting pp_shader_name{"None (builtin)", Keys::pp_shader_name}; SwitchableSetting anaglyph_shader_name{"Dubois (builtin)", Keys::anaglyph_shader_name}; SwitchableSetting dump_textures{false, Keys::dump_textures}; SwitchableSetting custom_textures{false, Keys::custom_textures}; SwitchableSetting preload_textures{false, Keys::preload_textures}; SwitchableSetting async_custom_loading{true, Keys::async_custom_loading}; SwitchableSetting disable_right_eye_render{false, Keys::disable_right_eye_render}; // Audio bool audio_muted; SwitchableSetting audio_emulation{AudioEmulation::HLE, Keys::audio_emulation}; SwitchableSetting enable_audio_stretching{true, Keys::enable_audio_stretching}; SwitchableSetting enable_realtime_audio{false, Keys::enable_realtime_audio}; SwitchableSetting volume{1.f, 0.f, 1.f, Keys::volume}; Setting output_type{AudioCore::SinkType::Auto, Keys::output_type}; Setting output_device{"Auto", Keys::output_device}; Setting input_type{AudioCore::InputType::Auto, Keys::input_type}; Setting input_device{"Auto", Keys::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 delay_start_for_lle_modules{true, Keys::delay_start_for_lle_modules}; Setting use_gdbstub{false, Keys::use_gdbstub}; Setting gdbstub_port{24689, Keys::gdbstub_port}; Setting instant_debug_log{false, Keys::instant_debug_log}; Setting enable_rpc_server{false, Keys::enable_rpc_server}; Setting toggle_unique_data_console_type{false, Keys::toggle_unique_data_console_type}; // Miscellaneous Setting log_filter{"*:Info", Keys::log_filter}; Setting log_regex_filter{"", Keys::log_regex_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 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); extern bool is_temporary_frame_limit; extern double temporary_frame_limit; static inline void ResetTemporaryFrameLimit() { is_temporary_frame_limit = false; temporary_frame_limit = 0; } static inline double GetFrameLimit() { return is_temporary_frame_limit ? temporary_frame_limit : values.frame_limit.GetValue(); } } // namespace Settings