// Copyright Citra Emulator Project / Azahar Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include "citra_libretro/core_settings.h" #include "citra_libretro/environment.h" #include "common/file_util.h" #include "common/settings.h" #include "core/hle/service/cfg/cfg.h" namespace BaseKeys = Settings::HKeys; namespace LibRetro { CoreSettings settings = {}; namespace config { template constexpr const char* citra_setting(HanaString key) { return (BOOST_HANA_STRING("citra_") + key).c_str(); } static constexpr const char* enabled = "enabled"; static constexpr const char* disabled = "disabled"; namespace category { static constexpr const char* cpu = "cpu"; static constexpr const char* system = "system"; static constexpr const char* audio = "audio"; static constexpr const char* graphics = "graphics"; static constexpr const char* layout = "layout"; static constexpr const char* storage = "storage"; static constexpr const char* input = "input"; } // namespace category namespace cpu { static constexpr const char* use_cpu_jit = citra_setting(BaseKeys::use_cpu_jit); static constexpr const char* cpu_clock_percentage = citra_setting(BaseKeys::cpu_clock_percentage); } // namespace cpu namespace system { static constexpr const char* is_new_3ds = citra_setting(BaseKeys::is_new_3ds); static constexpr const char* region_value = citra_setting(BaseKeys::region_value); static constexpr const char* language_value = citra_setting(BaseKeys::language_value); } // namespace system namespace audio { static constexpr const char* audio_emulation = citra_setting(BaseKeys::audio_emulation); static constexpr const char* input_type = citra_setting(BaseKeys::input_type); } // namespace audio namespace graphics { static constexpr const char* graphics_api = citra_setting(BaseKeys::graphics_api); static constexpr const char* use_hw_shader = citra_setting(BaseKeys::use_hw_shader); static constexpr const char* use_shader_jit = citra_setting(BaseKeys::use_shader_jit); static constexpr const char* shaders_accurate_mul = citra_setting(BaseKeys::shaders_accurate_mul); static constexpr const char* use_disk_shader_cache = citra_setting(BaseKeys::use_disk_shader_cache); static constexpr const char* resolution_factor = citra_setting(BaseKeys::resolution_factor); static constexpr const char* texture_filter = citra_setting(BaseKeys::texture_filter); static constexpr const char* texture_sampling = citra_setting(BaseKeys::texture_sampling); static constexpr const char* custom_textures = citra_setting(BaseKeys::custom_textures); static constexpr const char* dump_textures = citra_setting(BaseKeys::dump_textures); } // namespace graphics namespace layout { static constexpr const char* layout_option = citra_setting(BaseKeys::layout_option); static constexpr const char* swap_screen = citra_setting(BaseKeys::swap_screen); static constexpr const char* swap_screen_mode = citra_setting(BaseKeys::swap_screen_mode); static constexpr const char* large_screen_proportion = citra_setting(BaseKeys::large_screen_proportion); } // namespace layout namespace storage { static constexpr const char* use_virtual_sd = citra_setting(BaseKeys::use_virtual_sd); static constexpr const char* use_libretro_save_path = citra_setting(BaseKeys::use_libretro_save_path); } // namespace storage namespace input { static constexpr const char* analog_function = citra_setting(BaseKeys::analog_function); static constexpr const char* analog_deadzone = citra_setting(BaseKeys::analog_deadzone); static constexpr const char* enable_mouse_touchscreen = citra_setting(BaseKeys::enable_mouse_touchscreen); static constexpr const char* enable_touch_touchscreen = citra_setting(BaseKeys::enable_touch_touchscreen); static constexpr const char* enable_touch_pointer_timeout = citra_setting(BaseKeys::enable_touch_pointer_timeout); static constexpr const char* enable_motion = citra_setting(BaseKeys::enable_motion); static constexpr const char* motion_sensitivity = citra_setting(BaseKeys::motion_sensitivity); } // namespace input } // namespace config // clang-format off static constexpr retro_core_option_v2_category option_categories[] = { { config::category::cpu, "CPU", "Settings related to CPU emulation performance and accuracy." }, { config::category::system, "System", "Nintendo 3DS system configuration and region settings." }, { config::category::audio, "Audio", "Audio emulation and microphone settings." }, { config::category::graphics, "Graphics", "Graphics API, rendering, and visual enhancement settings." }, { config::category::layout, "Layout", "Screen layout and display positioning options." }, { config::category::storage, "Storage", "Save data and virtual SD card settings." }, { config::category::input, "Input", "Controller and touchscreen input configuration." }, { nullptr, nullptr, nullptr } }; // ============================================================================ // Option Definitions // ============================================================================ static constexpr retro_core_option_v2_definition option_definitions[] = { // CPU Category { config::cpu::use_cpu_jit, "Enable CPU JIT", "CPU JIT", "Enable Just-In-Time compilation for ARM CPU emulation. " "Significantly improves performance but may reduce accuracy. " "Restart required.", nullptr, config::category::cpu, { { config::enabled, "Enabled" }, { config::disabled, "Disabled" }, { nullptr, nullptr } }, config::enabled }, { config::cpu::cpu_clock_percentage, "CPU Clock Speed", "CPU Clock Speed", "Adjust the emulated 3DS CPU clock speed as a percentage of normal speed. " "Higher values may improve performance in some games but can cause issues. " "Lower values can help with games that run too fast.", nullptr, config::category::cpu, { { "25", "25%" }, { "50", "50%" }, { "75", "75%" }, { "100", "100% (Default)" }, { "125", "125%" }, { "150", "150%" }, { "175", "175%" }, { "200", "200%" }, { "225", "225%" }, { "250", "250%" }, { "275", "275%" }, { "300", "300%" }, { "325", "325%" }, { "350", "350%" }, { "375", "375%" }, { "400", "400%" }, { nullptr, nullptr } }, "100" }, // System Category { config::system::is_new_3ds, "3DS System Model", "System Model", "Select whether to emulate the original 3DS or New 3DS. " "New 3DS has additional CPU power and memory, required for some games. " "Restart required.", nullptr, config::category::system, { { "New 3DS", "New 3DS" }, { "Old 3DS", "Original 3DS" }, { nullptr, nullptr } }, "New 3DS" }, { config::system::region_value, "3DS System Region", "System Region", "Set the 3DS system region. Auto-select will choose based on the game. " "Some games are region-locked and require matching regions.", nullptr, config::category::system, { { "Auto", "Auto" }, { "Japan", "Japan" }, { "USA", "USA" }, { "Europe", "Europe" }, { "Australia", "Australia" }, { "China", "China" }, { "Korea", "Korea" }, { "Taiwan", "Taiwan" }, { nullptr, nullptr } }, "Auto" }, { config::system::language_value, "3DS System Language", "System Language", "Set the system language for the emulated 3DS. " "This affects in-game text language when supported.", nullptr, config::category::system, { { "English", "English" }, { "Japanese", "Japanese" }, { "French", "French" }, { "Spanish", "Spanish" }, { "German", "German" }, { "Italian", "Italian" }, { "Dutch", "Dutch" }, { "Portuguese", "Portuguese" }, { "Russian", "Russian" }, { "Korean", "Korean" }, { "Traditional Chinese", "Traditional Chinese" }, { "Simplified Chinese", "Simplified Chinese" }, { nullptr, nullptr } }, "english" }, // Audio Category { config::audio::audio_emulation, "Audio Emulation", "Audio Emulation", "Select audio emulation method. HLE is faster, LLE is more accurate.", nullptr, config::category::audio, { { "hle", "HLE (Fast)" }, { "lle", "LLE (Accurate)" }, { "lle_multithread", "LLE Multithreaded" }, { nullptr, nullptr } }, "hle" }, { config::audio::input_type, "Microphone Input Type", "Microphone Input", "Select how microphone input is handled for games that support it.", nullptr, config::category::audio, { { "auto", "Auto" }, { "none", "None" }, { "static_noise", "Static Noise" }, { "frontend", "Frontend" }, { nullptr, nullptr } }, "auto" }, // Graphics Category { config::graphics::graphics_api, "Graphics API", "Graphics API", "Select the graphics rendering API. Auto will choose the best available option. " "Restart required.", nullptr, config::category::graphics, { { "Auto", "Auto" }, #ifdef ENABLE_VULKAN { "Vulkan", "Vulkan" }, #endif #ifdef ENABLE_OPENGL { "OpenGL", "OpenGL" }, #endif { "Software", "Software" }, { nullptr, nullptr } }, "auto" }, { config::graphics::use_hw_shader, "Enable Hardware Shaders", "Hardware Shaders", "Use GPU hardware to accelerate shader processing. " "Significantly improves performance but may reduce accuracy.", nullptr, config::category::graphics, { { config::enabled, "Enabled" }, { config::disabled, "Disabled" }, { nullptr, nullptr } }, config::enabled }, { config::graphics::use_shader_jit, "Enable Shader JIT", "Shader JIT", "Use Just-In-Time compilation for shaders. " "Improves performance but may cause graphical issues in some games.", nullptr, config::category::graphics, { { config::enabled, "Enabled" }, { config::disabled, "Disabled" }, { nullptr, nullptr } }, config::enabled }, { config::graphics::shaders_accurate_mul, "Accurate Shader Multiplication", "Accurate Multiplication", "Use accurate multiplication in shaders. " "More accurate but can reduce performance. Only works with hardware shaders.", nullptr, config::category::graphics, { { config::enabled, "Enabled" }, { config::disabled, "Disabled" }, { nullptr, nullptr } }, config::enabled }, { config::graphics::use_disk_shader_cache, "Hardware Shader Cache", "Shader Cache", "Save compiled shaders to disk to reduce loading times on subsequent runs.", nullptr, config::category::graphics, { { config::enabled, "Enabled" }, { config::disabled, "Disabled" }, { nullptr, nullptr } }, config::enabled }, { config::graphics::resolution_factor, "Internal Resolution", "Internal Resolution", "Render the 3DS screens at a higher resolution. " "Higher values improve visual quality but significantly impact performance.", nullptr, config::category::graphics, { { "1", "1x (Native 400x240)" }, { "2", "2x (800x480)" }, { "3", "3x (1200x720)" }, { "4", "4x (1600x960)" }, { "5", "5x (2000x1200)" }, { "6", "6x (2400x1440)" }, { "7", "7x (2800x1680)" }, { "8", "8x (3200x1920)" }, { "9", "9x (3600x2160)" }, { "10", "10x (4000x2400)" }, { nullptr, nullptr } }, "1" }, { config::graphics::texture_filter, "Texture Filter", "Texture Filter", "Apply texture filtering to enhance visual quality. " "Some filters may significantly impact performance.", nullptr, config::category::graphics, { { "none", "None" }, { "Anime4K Ultrafast", "Anime4K Ultrafast" }, { "Bicubic", "Bicubic" }, { "ScaleForce", "ScaleForce" }, { "xBRZ", "xBRZ" }, { "MMPX", "MMPX" }, { nullptr, nullptr } }, "none" }, { config::graphics::texture_sampling, "Texture Sampling", "Texture Sampling", "Control how textures are sampled and filtered.", nullptr, config::category::graphics, { { "GameControlled", "Game Controlled" }, { "NearestNeighbor", "Nearest Neighbor" }, { "Linear", "Linear" }, { nullptr, nullptr } }, "GameControlled" }, { config::graphics::custom_textures, "Custom Textures", "Custom Textures", "Enable loading of custom texture packs to replace original game textures.", nullptr, config::category::graphics, { { config::enabled, "Enabled" }, { config::disabled, "Disabled" }, { nullptr, nullptr } }, config::disabled }, { config::graphics::dump_textures, "Dump Game Textures", "Dump Textures", "Save original game textures to disk for creating custom texture packs. " "May impact performance.", nullptr, config::category::graphics, { { config::enabled, "Enabled" }, { config::disabled, "Disabled" }, { nullptr, nullptr } }, config::disabled }, // Layout Category { config::layout::layout_option, "Screen Layout", "Screen Layout", "Choose how the 3DS screens are arranged in the display.", nullptr, config::category::layout, { { "default", "Default Top-Bottom" }, { "single_screen", "Single Screen Only" }, { "large_screen", "Large Screen, Small Screen" }, { "side_by_side", "Side by Side" }, { nullptr, nullptr } }, "default" }, { config::layout::swap_screen, "Prominent 3DS Screen", "Prominent Screen", "Choose which screen is displayed prominently in single screen or large screen layouts.", nullptr, config::category::layout, { { "Top", "Top Screen" }, { "Bottom", "Bottom Screen" }, { nullptr, nullptr } }, "Top" }, { config::layout::swap_screen_mode, "Screen Swap Mode", "Swap Mode", "How screen swapping behaves when using the screen swap hotkey.", nullptr, config::category::layout, { { "Toggle", "Toggle" }, { "Hold", "Hold" }, { nullptr, nullptr } }, "Toggle" }, { config::layout::large_screen_proportion, "Large Screen Proportion", "Large Screen Proportion", "How many times larger the main screen is compared to the small screen " "in the Large Screen layout.", nullptr, config::category::layout, { { "1.00", "1.00x (Equal Size)" }, { "1.25", "1.25x" }, { "1.50", "1.50x" }, { "1.75", "1.75x" }, { "2.00", "2.00x" }, { "2.25", "2.25x" }, { "2.50", "2.50x" }, { "2.75", "2.75x" }, { "3.00", "3.00x" }, { "3.25", "3.25x" }, { "3.50", "3.50x" }, { "3.75", "3.75x" }, { "4.00", "4.00x (Default)" }, { "4.25", "4.25x" }, { "4.50", "4.50x" }, { "4.75", "4.75x" }, { "5.00", "5.00x" }, { "5.25", "5.25x" }, { "5.50", "5.50x" }, { "5.75", "5.75x" }, { "6.00", "6.00x" }, { nullptr, nullptr } }, "4.00" }, // Storage Category { config::storage::use_virtual_sd, "Enable Virtual SD Card", "Virtual SD Card", "Enable virtual SD card support for homebrew and some commercial games.", nullptr, config::category::storage, { { config::enabled, "Enabled" }, { config::disabled, "Disabled" }, { nullptr, nullptr } }, config::enabled }, { config::storage::use_libretro_save_path, "Save Data Location", "Save Location", "Choose where save data and system files are stored.", nullptr, config::category::storage, { { "LibRetro Default", "LibRetro Default" }, { "Azahar Default", "Azahar Default" }, { nullptr, nullptr } }, "LibRetro Default" }, // Input Category { config::input::analog_function, "Right Analog Function", "Right Analog Function", "Configure what the right analog stick controls.", nullptr, config::category::input, { { "c_stick_and_touchscreen", "C-Stick and Touchscreen Pointer" }, { "touchscreen_pointer", "Touchscreen Pointer" }, { "c_stick", "C-Stick" }, { nullptr, nullptr } }, "c_stick_and_touchscreen" }, { config::input::analog_deadzone, "Analog Deadzone", "Analog Deadzone", "Set the deadzone percentage for analog input to reduce drift.", nullptr, config::category::input, { { "0", "0%" }, { "5", "5%" }, { "10", "10%" }, { "15", "15%" }, { "20", "20%" }, { "25", "25%" }, { "30", "30%" }, { "35", "35%" }, { nullptr, nullptr } }, "15" }, { config::input::enable_mouse_touchscreen, "Mouse Touchscreen Support", "Mouse Touchscreen", "Enable mouse input for touchscreen interactions.", nullptr, config::category::input, { { config::enabled, "Enabled" }, { config::disabled, "Disabled" }, { nullptr, nullptr } }, config::enabled }, { config::input::enable_touch_touchscreen, "Touch Device Support", "Touch Support", "Enable touch device input for touchscreen interactions.", nullptr, config::category::input, { { config::enabled, "Enabled" }, { config::disabled, "Disabled" }, { nullptr, nullptr } }, config::enabled }, { config::input::enable_touch_pointer_timeout, "Touch Pointer Timeout", "Touch Pointer Timeout", "Whether or not the touchscreen pointer should disappear during inactivity.", nullptr, config::category::input, { { config::enabled, "Enabled" }, { config::disabled, "Disabled" }, { nullptr, nullptr } }, config::enabled }, { config::input::enable_motion, "Gyroscope/Accelerometer Support", "Motion Support", "Enable gyroscope and accelerometer input for games that support motion controls.", nullptr, config::category::input, { { config::enabled, "Enabled" }, { config::disabled, "Disabled" }, { nullptr, nullptr } }, config::enabled }, { config::input::motion_sensitivity, "Motion Sensitivity", "Motion Sensitivity", "Adjust sensitivity of motion controls (gyroscope/accelerometer).", nullptr, config::category::input, { { "0.1", "10%" }, { "0.25", "25%" }, { "0.5", "50%" }, { "0.75", "75%" }, { "1.0", "100%" }, { "1.25", "125%" }, { "1.5", "150%" }, { "2.0", "200%" }, { nullptr, nullptr } }, "1.0" }, // Terminator { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, { { nullptr, nullptr } }, nullptr } }; // clang-format on static const retro_core_options_v2 options_v2 = { const_cast(option_categories), const_cast(option_definitions)}; void RegisterCoreOptions(void) { // Try v2 first, then fallback to v1 and v0 if needed unsigned version = 0; if (!LibRetro::GetCoreOptionsVersion(&version)) { version = 0; } LOG_INFO(Frontend, "Frontend reports core options version: {}", version); if (version >= 2) { if (LibRetro::SetCoreOptionsV2(&options_v2)) { LOG_INFO(Frontend, "V2 core options set successfully"); return; } } LOG_WARNING(Frontend, "V2 core options not supported, trying V1"); // Count number of options unsigned num_options = 0; while (option_definitions[num_options].key != nullptr) { num_options++; } if (version >= 1) { // Create V1 options array std::vector options_v1(num_options + 1); // Copy parameters from V2 to V1 for (unsigned i = 0; i < num_options; i++) { const auto& v2_option = option_definitions[i]; auto& v1_option = options_v1[i]; v1_option.key = v2_option.key; v1_option.desc = v2_option.desc; v1_option.info = v2_option.info; v1_option.default_value = v2_option.default_value; std::memcpy(v1_option.values, v2_option.values, sizeof(v1_option.values)); } // Null terminator std::memset(&options_v1.back(), 0, sizeof(retro_core_option_definition)); if (LibRetro::SetCoreOptionsV1(options_v1.data())) { LOG_INFO(Frontend, "V1 core options set successfully"); return; } } LOG_WARNING(Frontend, "V1 core options not supported, trying V0"); // Create V0 variables array std::vector variables(num_options + 1); std::vector values_buffer(num_options); for (unsigned i = 0; i < num_options; i++) { const auto& option = option_definitions[i]; std::string desc = option.desc ? option.desc : ""; std::string default_value = option.default_value ? option.default_value : ""; values_buffer[i] = ""; if (!desc.empty()) { // Count number of values size_t num_values = 0; size_t default_index = 0; while (option.values[num_values].value != nullptr) { if (!default_value.empty() && std::string(option.values[num_values].value) == default_value) { default_index = num_values; } num_values++; } // Build values string: "Description; default_value|other_value1|other_value2" if (num_values > 0) { values_buffer[i] = desc + "; " + option.values[default_index].value; // Add remaining values for (size_t j = 0; j < num_values; j++) { if (j != default_index) { values_buffer[i] += "|" + std::string(option.values[j].value); } } } } variables[i].key = option.key; variables[i].value = values_buffer[i].c_str(); } // Null terminator std::memset(&variables.back(), 0, sizeof(retro_variable)); // Set V0 variables if (LibRetro::SetVariables(variables.data())) { LOG_INFO(Frontend, "V0 core options set successfully"); } else { LOG_ERROR(Frontend, "Failed to set core options with any version"); } } static void ParseCpuOptions(void) { Settings::values.use_cpu_jit = LibRetro::FetchVariable(config::cpu::use_cpu_jit, config::enabled) == config::enabled; #if defined(IOS) if (!LibRetro::CanUseJIT()) Settings::values.use_cpu_jit = false; #endif auto cpu_clock = LibRetro::FetchVariable(config::cpu::cpu_clock_percentage, "100"); Settings::values.cpu_clock_percentage = std::stoi(cpu_clock); } static int GetRegionValue(const std::string& name) { if (name == "Japan") return 0; if (name == "USA") return 1; if (name == "Europe") return 2; if (name == "Australia") return 3; if (name == "China") return 4; if (name == "Korea") return 5; if (name == "Taiwan") return 6; return -1; // Auto } static Service::CFG::SystemLanguage GetLanguageValue(const std::string& name) { if (name == "Japanese") return Service::CFG::LANGUAGE_JP; if (name == "French") return Service::CFG::LANGUAGE_FR; if (name == "Spanish") return Service::CFG::LANGUAGE_ES; if (name == "German") return Service::CFG::LANGUAGE_DE; if (name == "Italian") return Service::CFG::LANGUAGE_IT; if (name == "Dutch") return Service::CFG::LANGUAGE_NL; if (name == "Portuguese") return Service::CFG::LANGUAGE_PT; if (name == "Russian") return Service::CFG::LANGUAGE_RU; if (name == "Korean") return Service::CFG::LANGUAGE_KO; if (name == "Traditional Chinese") return Service::CFG::LANGUAGE_TW; if (name == "Simplified Chinese") return Service::CFG::LANGUAGE_ZH; return Service::CFG::LANGUAGE_EN; // English default } static void ParseSystemOptions(void) { Settings::values.is_new_3ds = LibRetro::FetchVariable(config::system::is_new_3ds, "New 3DS") == "New 3DS"; Settings::values.region_value = GetRegionValue(LibRetro::FetchVariable(config::system::region_value, "Auto")); LibRetro::settings.language_value = GetLanguageValue(LibRetro::FetchVariable(config::system::language_value, "English")); } static Settings::AudioEmulation GetAudioEmulation(const std::string& name) { if (name == "lle") return Settings::AudioEmulation::LLE; if (name == "lle_multithread") return Settings::AudioEmulation::LLEMultithreaded; return Settings::AudioEmulation::HLE; // Default } static void ParseAudioOptions(void) { Settings::values.audio_emulation = GetAudioEmulation(LibRetro::FetchVariable(config::audio::audio_emulation, "hle")); auto input_type = LibRetro::FetchVariable(config::audio::input_type, "auto"); if (input_type == "none") { Settings::values.input_type = AudioCore::InputType::Null; } else if (input_type == "static_noise") { Settings::values.input_type = AudioCore::InputType::Static; } else if (input_type == "frontend") { Settings::values.input_type = AudioCore::InputType::LibRetro; } else { Settings::values.input_type = AudioCore::InputType::Auto; } } static Settings::TextureFilter GetTextureFilter(const std::string& name) { if (name == "Anime4K Ultrafast") return Settings::TextureFilter::Anime4K; if (name == "Bicubic") return Settings::TextureFilter::Bicubic; if (name == "ScaleForce") return Settings::TextureFilter::ScaleForce; if (name == "xBRZ freescale") return Settings::TextureFilter::xBRZ; if (name == "MMPX") return Settings::TextureFilter::MMPX; return Settings::TextureFilter::NoFilter; } static Settings::TextureSampling GetTextureSampling(const std::string& name) { if (name == "NearestNeighbor") return Settings::TextureSampling::NearestNeighbor; if (name == "Linear") return Settings::TextureSampling::Linear; return Settings::TextureSampling::GameControlled; } static Settings::GraphicsAPI GetGraphicsAPI(const std::string& name) { if (name == "Software") return Settings::GraphicsAPI::Software; #ifdef ENABLE_VULKAN if (name == "Vulkan") return Settings::GraphicsAPI::Vulkan; #endif #ifdef ENABLE_OPENGL if (name == "OpenGL") return Settings::GraphicsAPI::OpenGL; #endif // Auto selection return LibRetro::GetPreferredRenderer(); } static void ParseGraphicsOptions(void) { Settings::values.graphics_api = GetGraphicsAPI(LibRetro::FetchVariable(config::graphics::graphics_api, "auto")); Settings::values.use_hw_shader = LibRetro::FetchVariable(config::graphics::use_hw_shader, config::enabled) == config::enabled; Settings::values.use_shader_jit = LibRetro::FetchVariable(config::graphics::use_shader_jit, config::enabled) == config::enabled; #if defined(IOS) if (!LibRetro::CanUseJIT()) Settings::values.use_shader_jit = false; #endif Settings::values.shaders_accurate_mul = LibRetro::FetchVariable(config::graphics::shaders_accurate_mul, config::enabled) == config::enabled; Settings::values.use_disk_shader_cache = LibRetro::FetchVariable(config::graphics::use_disk_shader_cache, config::enabled) == config::enabled; auto resolution = LibRetro::FetchVariable(config::graphics::resolution_factor, "1"); Settings::values.resolution_factor = std::stoi(resolution); Settings::values.texture_filter = GetTextureFilter(LibRetro::FetchVariable(config::graphics::texture_filter, "none")); Settings::values.texture_sampling = GetTextureSampling( LibRetro::FetchVariable(config::graphics::texture_sampling, "GameControlled")); Settings::values.custom_textures = LibRetro::FetchVariable(config::graphics::custom_textures, config::disabled) == config::enabled; Settings::values.dump_textures = LibRetro::FetchVariable(config::graphics::dump_textures, config::disabled) == config::enabled; } static Settings::LayoutOption GetLayoutOption(const std::string& name) { if (name == "single_screen" || name == "Single Screen Only") return Settings::LayoutOption::SingleScreen; if (name == "large_screen" || name == "Large Screen, Small Screen") return Settings::LayoutOption::LargeScreen; if (name == "side_by_side" || name == "Side by Side") return Settings::LayoutOption::SideScreen; return Settings::LayoutOption::Default; } static void ParseLayoutOptions(void) { Settings::values.layout_option = GetLayoutOption(LibRetro::FetchVariable(config::layout::layout_option, "default")); Settings::values.swap_screen = LibRetro::FetchVariable(config::layout::swap_screen, "Top") == "Bottom"; LibRetro::settings.swap_screen_mode = LibRetro::FetchVariable(config::layout::swap_screen_mode, "Toggle"); auto large_screen_proportion = LibRetro::FetchVariable(config::layout::large_screen_proportion, "4.00"); Settings::values.large_screen_proportion = std::stof(large_screen_proportion); } static void ParseStorageOptions(void) { Settings::values.use_virtual_sd = LibRetro::FetchVariable(config::storage::use_virtual_sd, config::enabled) == config::enabled; // Configure the file storage location auto use_libretro_saves = LibRetro::FetchVariable(config::storage::use_libretro_save_path, "LibRetro Default") == "LibRetro Default"; if (use_libretro_saves) { auto target_dir = LibRetro::GetSaveDir(); if (target_dir.empty()) { LOG_INFO(Frontend, "No save dir provided; trying system dir..."); target_dir = LibRetro::GetSystemDir(); } if (!target_dir.empty()) { if (!target_dir.ends_with("/")) target_dir += "/"; target_dir += "Azahar/"; // Ensure that this new dir exists if (!FileUtil::CreateDir(target_dir)) { LOG_ERROR(Frontend, "Failed to create \"{}\". Using Azahar's default paths.", target_dir); } else { FileUtil::SetUserPath(target_dir); const auto& target_dir_result = FileUtil::GetUserPath(FileUtil::UserPath::UserDir); LOG_INFO(Frontend, "User dir set to \"{}\".", target_dir_result); } } } } static LibRetro::CStickFunction GetAnalogFunction(const std::string& name) { if (name == "c_stick" || name == "C-Stick") return LibRetro::CStickFunction::CStick; if (name == "touchscreen_pointer" || name == "Touchscreen Pointer") return LibRetro::CStickFunction::Touchscreen; return LibRetro::CStickFunction::Both; // Default } static void ParseInputOptions(void) { LibRetro::settings.analog_function = GetAnalogFunction( LibRetro::FetchVariable(config::input::analog_function, "c_stick_and_touchscreen")); if (LibRetro::settings.analog_function != LibRetro::CStickFunction::Touchscreen) { Settings::values.current_input_profile.analogs[1] = "axis:1,joystick:0,engine:libretro"; } else { Settings::values.current_input_profile.analogs[1] = ""; } auto analog_deadzone = LibRetro::FetchVariable(config::input::analog_deadzone, "15"); LibRetro::settings.analog_deadzone = static_cast(std::stoi(analog_deadzone)) / 100.0f; LibRetro::settings.enable_mouse_touchscreen = LibRetro::FetchVariable(config::input::enable_mouse_touchscreen, config::enabled) == config::enabled; LibRetro::settings.enable_touch_touchscreen = LibRetro::FetchVariable(config::input::enable_touch_touchscreen, config::enabled) == config::enabled; LibRetro::settings.enable_touch_pointer_timeout = LibRetro::FetchVariable(config::input::enable_touch_pointer_timeout, config::enabled) == config::enabled; LibRetro::settings.enable_motion = LibRetro::FetchVariable(config::input::enable_motion, config::enabled) == config::enabled; auto motion_sens = LibRetro::FetchVariable(config::input::motion_sensitivity, "1.0"); LibRetro::settings.motion_sensitivity = std::stof(motion_sens); // Configure motion device based on user settings if (LibRetro::settings.enable_motion) { Settings::values.current_input_profile.motion_device = "port:0,sensitivity:" + std::to_string(LibRetro::settings.motion_sensitivity) + ",engine:libretro"; } else { Settings::values.current_input_profile.motion_device = "engine:motion_emu"; } } void ParseCoreOptions(void) { // Override default values that aren't user-selectable and aren't correct for the core Settings::values.enable_audio_stretching = false; Settings::values.frame_limit = 0; #if defined(USING_GLES) Settings::values.use_gles = true; #else Settings::values.use_gles = false; #endif Settings::values.filter_mode = false; ParseCpuOptions(); ParseSystemOptions(); ParseAudioOptions(); ParseGraphicsOptions(); ParseLayoutOptions(); ParseStorageOptions(); ParseInputOptions(); } } // namespace LibRetro