// 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 "audio_core/input_details.h" #include "audio_core/sink_details.h" #include "common/common_types.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 LayoutOption : u32 { Default, SingleScreen, LargeScreen, SideScreen, #ifndef ANDROID SeparateWindows, #endif HybridScreen, // 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 StereoRenderOption : u32 { Off = 0, SideBySide = 1, Anaglyph = 2, Interlaced = 3, ReverseInterlaced = 4, CardboardVR = 5 }; // 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, }; enum class AudioEmulation : u32 { HLE = 0, LLE = 1, LLEMultithreaded = 2, }; enum class TextureFilter : u32 { None = 0, Anime4K = 1, Bicubic = 2, ScaleForce = 3, xBRZ = 4, MMPX = 5, }; enum class TextureSampling : u32 { GameControlled = 0, NearestNeighbor = 1, Linear = 2, }; 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& 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 }; 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"}; Setting plugin_loader_enabled{false, "plugin_loader"}; Setting allow_plugin_loader{true, "allow_plugin_loader"}; // Renderer SwitchableSetting graphics_api{GraphicsAPI::OpenGL, GraphicsAPI::Software, GraphicsAPI::Vulkan, "graphics_api"}; SwitchableSetting physical_device{0, "physical_device"}; Setting use_gles{false, "use_gles"}; Setting renderer_debug{false, "renderer_debug"}; Setting dump_command_buffers{false, "dump_command_buffers"}; SwitchableSetting spirv_shader_gen{true, "spirv_shader_gen"}; SwitchableSetting async_shader_compilation{false, "async_shader_compilation"}; SwitchableSetting async_presentation{true, "async_presentation"}; SwitchableSetting use_hw_shader{true, "use_hw_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, 0, 10, "resolution_factor"}; SwitchableSetting frame_limit{100, 0, 1000, "frame_limit"}; SwitchableSetting texture_filter{TextureFilter::None, "texture_filter"}; SwitchableSetting texture_sampling{TextureSampling::GameControlled, "texture_sampling"}; SwitchableSetting layout_option{LayoutOption::Default, "layout_option"}; SwitchableSetting swap_screen{false, "swap_screen"}; SwitchableSetting upright_screen{false, "upright_screen"}; SwitchableSetting large_screen_proportion{4.f, 1.f, 16.f, "large_screen_proportion"}; 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"}; Setting custom_second_layer_opacity{100, "custom_second_layer_opacity"}; 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"}; SwitchableSetting mono_render_option{MonoRenderOption::LeftEye, "mono_render_option"}; 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"}; SwitchableSetting anaglyph_shader_name{"dubois (builtin)", "anaglyph_shader_name"}; SwitchableSetting dump_textures{false, "dump_textures"}; SwitchableSetting custom_textures{false, "custom_textures"}; SwitchableSetting preload_textures{false, "preload_textures"}; SwitchableSetting async_custom_loading{true, "async_custom_loading"}; // Audio bool audio_muted; SwitchableSetting audio_emulation{AudioEmulation::HLE, "audio_emulation"}; SwitchableSetting enable_audio_stretching{true, "enable_audio_stretching"}; SwitchableSetting volume{1.f, 0.f, 1.f, "volume"}; Setting output_type{AudioCore::SinkType::Auto, "output_type"}; Setting output_device{"auto", "output_device"}; Setting input_type{AudioCore::InputType::Auto, "input_type"}; Setting input_device{"auto", "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 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