| | |
| | |
| | |
| |
|
| | #include <iomanip> |
| | #include <memory> |
| | #include <sstream> |
| | #include <type_traits> |
| | #include <INIReader.h> |
| | #include <SDL.h> |
| | #include "citra/config.h" |
| | #include "citra/default_ini.h" |
| | #include "common/file_util.h" |
| | #include "common/logging/backend.h" |
| | #include "common/logging/log.h" |
| | #include "common/settings.h" |
| | #include "core/hle/service/service.h" |
| | #include "input_common/main.h" |
| | #include "input_common/udp/client.h" |
| | #include "network/network_settings.h" |
| |
|
| | Config::Config() { |
| | |
| | sdl2_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "sdl2-config.ini"; |
| | sdl2_config = std::make_unique<INIReader>(sdl2_config_loc); |
| |
|
| | Reload(); |
| | } |
| |
|
| | Config::~Config() = default; |
| |
|
| | bool Config::LoadINI(const std::string& default_contents, bool retry) { |
| | const std::string& location = this->sdl2_config_loc; |
| | if (sdl2_config->ParseError() < 0) { |
| | if (retry) { |
| | LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location); |
| | FileUtil::CreateFullPath(location); |
| | FileUtil::WriteStringToFile(true, location, default_contents); |
| | sdl2_config = std::make_unique<INIReader>(location); |
| |
|
| | return LoadINI(default_contents, false); |
| | } |
| | LOG_ERROR(Config, "Failed."); |
| | return false; |
| | } |
| | LOG_INFO(Config, "Successfully loaded {}", location); |
| | return true; |
| | } |
| |
|
| | static const std::array<int, Settings::NativeButton::NumButtons> default_buttons = { |
| | SDL_SCANCODE_A, SDL_SCANCODE_S, SDL_SCANCODE_Z, SDL_SCANCODE_X, SDL_SCANCODE_T, SDL_SCANCODE_G, |
| | SDL_SCANCODE_F, SDL_SCANCODE_H, SDL_SCANCODE_Q, SDL_SCANCODE_W, SDL_SCANCODE_M, SDL_SCANCODE_N, |
| | SDL_SCANCODE_O, SDL_SCANCODE_P, SDL_SCANCODE_1, SDL_SCANCODE_2, SDL_SCANCODE_B, |
| | }; |
| |
|
| | static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs{{ |
| | { |
| | SDL_SCANCODE_UP, |
| | SDL_SCANCODE_DOWN, |
| | SDL_SCANCODE_LEFT, |
| | SDL_SCANCODE_RIGHT, |
| | SDL_SCANCODE_D, |
| | }, |
| | { |
| | SDL_SCANCODE_I, |
| | SDL_SCANCODE_K, |
| | SDL_SCANCODE_J, |
| | SDL_SCANCODE_L, |
| | SDL_SCANCODE_D, |
| | }, |
| | }}; |
| |
|
| | template <> |
| | void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) { |
| | std::string setting_value = sdl2_config->Get(group, setting.GetLabel(), setting.GetDefault()); |
| | if (setting_value.empty()) { |
| | setting_value = setting.GetDefault(); |
| | } |
| | setting = std::move(setting_value); |
| | } |
| |
|
| | template <> |
| | void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) { |
| | setting = sdl2_config->GetBoolean(group, setting.GetLabel(), setting.GetDefault()); |
| | } |
| |
|
| | template <typename Type, bool ranged> |
| | void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) { |
| | if constexpr (std::is_floating_point_v<Type>) { |
| | setting = static_cast<Type>( |
| | sdl2_config->GetReal(group, setting.GetLabel(), setting.GetDefault())); |
| | } else { |
| | setting = static_cast<Type>(sdl2_config->GetInteger( |
| | group, setting.GetLabel(), static_cast<long>(setting.GetDefault()))); |
| | } |
| | } |
| |
|
| | void Config::ReadValues() { |
| | |
| | |
| | for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) { |
| | std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]); |
| | Settings::values.current_input_profile.buttons[i] = |
| | sdl2_config->GetString("Controls", Settings::NativeButton::mapping[i], default_param); |
| | if (Settings::values.current_input_profile.buttons[i].empty()) |
| | Settings::values.current_input_profile.buttons[i] = default_param; |
| | } |
| |
|
| | for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) { |
| | std::string default_param = InputCommon::GenerateAnalogParamFromKeys( |
| | default_analogs[i][0], default_analogs[i][1], default_analogs[i][2], |
| | default_analogs[i][3], default_analogs[i][4], 0.5f); |
| | Settings::values.current_input_profile.analogs[i] = |
| | sdl2_config->GetString("Controls", Settings::NativeAnalog::mapping[i], default_param); |
| | if (Settings::values.current_input_profile.analogs[i].empty()) |
| | Settings::values.current_input_profile.analogs[i] = default_param; |
| | } |
| |
|
| | Settings::values.current_input_profile.motion_device = sdl2_config->GetString( |
| | "Controls", "motion_device", |
| | "engine:motion_emu,update_period:100,sensitivity:0.01,tilt_clamp:90.0"); |
| | Settings::values.current_input_profile.touch_device = |
| | sdl2_config->GetString("Controls", "touch_device", "engine:emu_window"); |
| | Settings::values.current_input_profile.udp_input_address = sdl2_config->GetString( |
| | "Controls", "udp_input_address", InputCommon::CemuhookUDP::DEFAULT_ADDR); |
| | Settings::values.current_input_profile.udp_input_port = |
| | static_cast<u16>(sdl2_config->GetInteger("Controls", "udp_input_port", |
| | InputCommon::CemuhookUDP::DEFAULT_PORT)); |
| |
|
| | |
| | ReadSetting("Core", Settings::values.use_cpu_jit); |
| | ReadSetting("Core", Settings::values.cpu_clock_percentage); |
| |
|
| | |
| | ReadSetting("Renderer", Settings::values.graphics_api); |
| | ReadSetting("Renderer", Settings::values.physical_device); |
| | ReadSetting("Renderer", Settings::values.spirv_shader_gen); |
| | ReadSetting("Renderer", Settings::values.async_shader_compilation); |
| | ReadSetting("Renderer", Settings::values.async_presentation); |
| | ReadSetting("Renderer", Settings::values.use_gles); |
| | ReadSetting("Renderer", Settings::values.use_hw_shader); |
| | ReadSetting("Renderer", Settings::values.shaders_accurate_mul); |
| | ReadSetting("Renderer", Settings::values.use_shader_jit); |
| | ReadSetting("Renderer", Settings::values.resolution_factor); |
| | ReadSetting("Renderer", Settings::values.use_disk_shader_cache); |
| | ReadSetting("Renderer", Settings::values.frame_limit); |
| | ReadSetting("Renderer", Settings::values.use_vsync_new); |
| | ReadSetting("Renderer", Settings::values.texture_filter); |
| | ReadSetting("Renderer", Settings::values.texture_sampling); |
| |
|
| | ReadSetting("Renderer", Settings::values.mono_render_option); |
| | ReadSetting("Renderer", Settings::values.render_3d); |
| | ReadSetting("Renderer", Settings::values.factor_3d); |
| | ReadSetting("Renderer", Settings::values.pp_shader_name); |
| | ReadSetting("Renderer", Settings::values.anaglyph_shader_name); |
| | ReadSetting("Renderer", Settings::values.filter_mode); |
| |
|
| | ReadSetting("Renderer", Settings::values.bg_red); |
| | ReadSetting("Renderer", Settings::values.bg_green); |
| | ReadSetting("Renderer", Settings::values.bg_blue); |
| |
|
| | |
| | ReadSetting("Layout", Settings::values.layout_option); |
| | ReadSetting("Layout", Settings::values.swap_screen); |
| | ReadSetting("Layout", Settings::values.upright_screen); |
| | ReadSetting("Layout", Settings::values.large_screen_proportion); |
| | ReadSetting("Layout", Settings::values.custom_layout); |
| | ReadSetting("Layout", Settings::values.custom_top_left); |
| | ReadSetting("Layout", Settings::values.custom_top_top); |
| | ReadSetting("Layout", Settings::values.custom_top_right); |
| | ReadSetting("Layout", Settings::values.custom_top_bottom); |
| | ReadSetting("Layout", Settings::values.custom_bottom_left); |
| | ReadSetting("Layout", Settings::values.custom_bottom_top); |
| | ReadSetting("Layout", Settings::values.custom_bottom_right); |
| | ReadSetting("Layout", Settings::values.custom_bottom_bottom); |
| | ReadSetting("Layout", Settings::values.custom_second_layer_opacity); |
| |
|
| | |
| | ReadSetting("Utility", Settings::values.dump_textures); |
| | ReadSetting("Utility", Settings::values.custom_textures); |
| | ReadSetting("Utility", Settings::values.preload_textures); |
| | ReadSetting("Utility", Settings::values.async_custom_loading); |
| |
|
| | |
| | ReadSetting("Audio", Settings::values.audio_emulation); |
| | ReadSetting("Audio", Settings::values.enable_audio_stretching); |
| | ReadSetting("Audio", Settings::values.volume); |
| | ReadSetting("Audio", Settings::values.output_type); |
| | ReadSetting("Audio", Settings::values.output_device); |
| | ReadSetting("Audio", Settings::values.input_type); |
| | ReadSetting("Audio", Settings::values.input_device); |
| |
|
| | |
| | ReadSetting("Data Storage", Settings::values.use_virtual_sd); |
| | ReadSetting("Data Storage", Settings::values.use_custom_storage); |
| |
|
| | if (Settings::values.use_custom_storage) { |
| | FileUtil::UpdateUserPath(FileUtil::UserPath::NANDDir, |
| | sdl2_config->GetString("Data Storage", "nand_directory", "")); |
| | FileUtil::UpdateUserPath(FileUtil::UserPath::SDMCDir, |
| | sdl2_config->GetString("Data Storage", "sdmc_directory", "")); |
| | } |
| |
|
| | |
| | ReadSetting("System", Settings::values.is_new_3ds); |
| | ReadSetting("System", Settings::values.lle_applets); |
| | ReadSetting("System", Settings::values.region_value); |
| | ReadSetting("System", Settings::values.init_clock); |
| | { |
| | std::tm t; |
| | t.tm_sec = 1; |
| | t.tm_min = 0; |
| | t.tm_hour = 0; |
| | t.tm_mday = 1; |
| | t.tm_mon = 0; |
| | t.tm_year = 100; |
| | t.tm_isdst = 0; |
| | std::istringstream string_stream( |
| | sdl2_config->GetString("System", "init_time", "2000-01-01 00:00:01")); |
| | string_stream >> std::get_time(&t, "%Y-%m-%d %H:%M:%S"); |
| | if (string_stream.fail()) { |
| | LOG_ERROR(Config, "Failed To parse init_time. Using 2000-01-01 00:00:01"); |
| | } |
| | Settings::values.init_time = |
| | std::chrono::duration_cast<std::chrono::seconds>( |
| | std::chrono::system_clock::from_time_t(std::mktime(&t)).time_since_epoch()) |
| | .count(); |
| | } |
| | ReadSetting("System", Settings::values.init_ticks_type); |
| | ReadSetting("System", Settings::values.init_ticks_override); |
| | ReadSetting("System", Settings::values.plugin_loader_enabled); |
| | ReadSetting("System", Settings::values.allow_plugin_loader); |
| |
|
| | { |
| | constexpr const char* default_init_time_offset = "0 00:00:00"; |
| |
|
| | std::string offset_string = |
| | sdl2_config->GetString("System", "init_time_offset", default_init_time_offset); |
| |
|
| | std::size_t sep_index = offset_string.find(' '); |
| |
|
| | if (sep_index == std::string::npos) { |
| | LOG_ERROR(Config, "Failed to parse init_time_offset. Using 0 00:00:00"); |
| | offset_string = default_init_time_offset; |
| |
|
| | sep_index = offset_string.find(' '); |
| | } |
| |
|
| | std::string day_string = offset_string.substr(0, sep_index); |
| | long long days = 0; |
| |
|
| | try { |
| | days = std::stoll(day_string); |
| | } catch (std::logic_error&) { |
| | LOG_ERROR(Config, "Failed to parse days in init_time_offset. Using 0"); |
| | days = 0; |
| | } |
| |
|
| | long long days_in_seconds = days * 86400; |
| |
|
| | std::tm t; |
| | t.tm_sec = 0; |
| | t.tm_min = 0; |
| | t.tm_hour = 0; |
| | t.tm_mday = 1; |
| | t.tm_mon = 0; |
| | t.tm_year = 100; |
| | t.tm_isdst = 0; |
| |
|
| | std::istringstream string_stream(offset_string.substr(sep_index + 1)); |
| | string_stream >> std::get_time(&t, "%H:%M:%S"); |
| |
|
| | if (string_stream.fail()) { |
| | LOG_ERROR(Config, |
| | "Failed to parse hours, minutes and seconds in init_time_offset. 00:00:00"); |
| | } |
| |
|
| | auto time_offset = |
| | std::chrono::system_clock::from_time_t(std::mktime(&t)).time_since_epoch(); |
| |
|
| | auto secs = std::chrono::duration_cast<std::chrono::seconds>(time_offset).count(); |
| |
|
| | Settings::values.init_time_offset = static_cast<long long>(secs) + days_in_seconds; |
| | } |
| |
|
| | |
| | using namespace Service::CAM; |
| | Settings::values.camera_name[OuterRightCamera] = |
| | sdl2_config->GetString("Camera", "camera_outer_right_name", "blank"); |
| | Settings::values.camera_config[OuterRightCamera] = |
| | sdl2_config->GetString("Camera", "camera_outer_right_config", ""); |
| | Settings::values.camera_flip[OuterRightCamera] = |
| | sdl2_config->GetInteger("Camera", "camera_outer_right_flip", 0); |
| | Settings::values.camera_name[InnerCamera] = |
| | sdl2_config->GetString("Camera", "camera_inner_name", "blank"); |
| | Settings::values.camera_config[InnerCamera] = |
| | sdl2_config->GetString("Camera", "camera_inner_config", ""); |
| | Settings::values.camera_flip[InnerCamera] = |
| | sdl2_config->GetInteger("Camera", "camera_inner_flip", 0); |
| | Settings::values.camera_name[OuterLeftCamera] = |
| | sdl2_config->GetString("Camera", "camera_outer_left_name", "blank"); |
| | Settings::values.camera_config[OuterLeftCamera] = |
| | sdl2_config->GetString("Camera", "camera_outer_left_config", ""); |
| | Settings::values.camera_flip[OuterLeftCamera] = |
| | sdl2_config->GetInteger("Camera", "camera_outer_left_flip", 0); |
| |
|
| | |
| | ReadSetting("Miscellaneous", Settings::values.log_filter); |
| | ReadSetting("Miscellaneous", Settings::values.log_regex_filter); |
| |
|
| | |
| | |
| | Common::Log::Filter filter; |
| | filter.ParseFilterString(Settings::values.log_filter.GetValue()); |
| | Common::Log::SetGlobalFilter(filter); |
| | Common::Log::SetRegexFilter(Settings::values.log_regex_filter.GetValue()); |
| |
|
| | |
| | Settings::values.record_frame_times = |
| | sdl2_config->GetBoolean("Debugging", "record_frame_times", false); |
| | ReadSetting("Debugging", Settings::values.renderer_debug); |
| | ReadSetting("Debugging", Settings::values.use_gdbstub); |
| | ReadSetting("Debugging", Settings::values.gdbstub_port); |
| |
|
| | for (const auto& service_module : Service::service_module_map) { |
| | bool use_lle = sdl2_config->GetBoolean("Debugging", "LLE\\" + service_module.name, false); |
| | Settings::values.lle_modules.emplace(service_module.name, use_lle); |
| | } |
| |
|
| | |
| | NetSettings::values.enable_telemetry = |
| | sdl2_config->GetBoolean("WebService", "enable_telemetry", false); |
| | NetSettings::values.web_api_url = |
| | sdl2_config->GetString("WebService", "web_api_url", "https://api.citra-emu.org"); |
| | NetSettings::values.citra_username = sdl2_config->GetString("WebService", "citra_username", ""); |
| | NetSettings::values.citra_token = sdl2_config->GetString("WebService", "citra_token", ""); |
| |
|
| | |
| | Settings::values.output_format = |
| | sdl2_config->GetString("Video Dumping", "output_format", "webm"); |
| | Settings::values.format_options = sdl2_config->GetString("Video Dumping", "format_options", ""); |
| |
|
| | Settings::values.video_encoder = |
| | sdl2_config->GetString("Video Dumping", "video_encoder", "libvpx-vp9"); |
| |
|
| | |
| | |
| | std::string default_video_options; |
| | if (Settings::values.video_encoder == "libvpx-vp9") { |
| | default_video_options = |
| | "quality:realtime,speed:6,tile-columns:4,frame-parallel:1,threads:8,row-mt:1"; |
| | } |
| | Settings::values.video_encoder_options = |
| | sdl2_config->GetString("Video Dumping", "video_encoder_options", default_video_options); |
| | Settings::values.video_bitrate = |
| | sdl2_config->GetInteger("Video Dumping", "video_bitrate", 2500000); |
| |
|
| | Settings::values.audio_encoder = |
| | sdl2_config->GetString("Video Dumping", "audio_encoder", "libvorbis"); |
| | Settings::values.audio_encoder_options = |
| | sdl2_config->GetString("Video Dumping", "audio_encoder_options", ""); |
| | Settings::values.audio_bitrate = |
| | sdl2_config->GetInteger("Video Dumping", "audio_bitrate", 64000); |
| | } |
| |
|
| | void Config::Reload() { |
| | LoadINI(DefaultINI::sdl2_config_file); |
| | ReadValues(); |
| | } |
| |
|