duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

settings.cpp (86228B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #include "settings.h"
      5 #include "achievements.h"
      6 #include "controller.h"
      7 #include "host.h"
      8 #include "system.h"
      9 
     10 #include "util/gpu_device.h"
     11 #include "util/imgui_manager.h"
     12 #include "util/input_manager.h"
     13 #include "util/media_capture.h"
     14 
     15 #include "common/assert.h"
     16 #include "common/file_system.h"
     17 #include "common/log.h"
     18 #include "common/path.h"
     19 #include "common/string_util.h"
     20 
     21 #include <algorithm>
     22 #include <array>
     23 #include <cctype>
     24 #include <numeric>
     25 
     26 Log_SetChannel(Settings);
     27 
     28 Settings g_settings;
     29 
     30 const char* SettingInfo::StringDefaultValue() const
     31 {
     32   return default_value ? default_value : "";
     33 }
     34 
     35 bool SettingInfo::BooleanDefaultValue() const
     36 {
     37   return default_value ? StringUtil::FromChars<bool>(default_value).value_or(false) : false;
     38 }
     39 
     40 s32 SettingInfo::IntegerDefaultValue() const
     41 {
     42   return default_value ? StringUtil::FromChars<s32>(default_value).value_or(0) : 0;
     43 }
     44 
     45 s32 SettingInfo::IntegerMinValue() const
     46 {
     47   static constexpr s32 fallback_value = std::numeric_limits<s32>::min();
     48   return min_value ? StringUtil::FromChars<s32>(min_value).value_or(fallback_value) : fallback_value;
     49 }
     50 
     51 s32 SettingInfo::IntegerMaxValue() const
     52 {
     53   static constexpr s32 fallback_value = std::numeric_limits<s32>::max();
     54   return max_value ? StringUtil::FromChars<s32>(max_value).value_or(fallback_value) : fallback_value;
     55 }
     56 
     57 s32 SettingInfo::IntegerStepValue() const
     58 {
     59   static constexpr s32 fallback_value = 1;
     60   return step_value ? StringUtil::FromChars<s32>(step_value).value_or(fallback_value) : fallback_value;
     61 }
     62 
     63 float SettingInfo::FloatDefaultValue() const
     64 {
     65   return default_value ? StringUtil::FromChars<float>(default_value).value_or(0.0f) : 0.0f;
     66 }
     67 
     68 float SettingInfo::FloatMinValue() const
     69 {
     70   static constexpr float fallback_value = std::numeric_limits<float>::min();
     71   return min_value ? StringUtil::FromChars<float>(min_value).value_or(fallback_value) : fallback_value;
     72 }
     73 
     74 float SettingInfo::FloatMaxValue() const
     75 {
     76   static constexpr float fallback_value = std::numeric_limits<float>::max();
     77   return max_value ? StringUtil::FromChars<float>(max_value).value_or(fallback_value) : fallback_value;
     78 }
     79 
     80 float SettingInfo::FloatStepValue() const
     81 {
     82   static constexpr float fallback_value = 0.1f;
     83   return step_value ? StringUtil::FromChars<float>(step_value).value_or(fallback_value) : fallback_value;
     84 }
     85 
     86 #if defined(_WIN32)
     87 const MediaCaptureBackend Settings::DEFAULT_MEDIA_CAPTURE_BACKEND = MediaCaptureBackend::MediaFoundation;
     88 #elif !defined(__ANDROID__)
     89 const MediaCaptureBackend Settings::DEFAULT_MEDIA_CAPTURE_BACKEND = MediaCaptureBackend::FFmpeg;
     90 #endif
     91 
     92 Settings::Settings()
     93 {
     94   controller_types[0] = DEFAULT_CONTROLLER_1_TYPE;
     95   memory_card_types[0] = DEFAULT_MEMORY_CARD_1_TYPE;
     96   for (u32 i = 1; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
     97   {
     98     controller_types[i] = DEFAULT_CONTROLLER_2_TYPE;
     99     memory_card_types[i] = DEFAULT_MEMORY_CARD_2_TYPE;
    100   }
    101 }
    102 
    103 bool Settings::HasAnyPerGameMemoryCards() const
    104 {
    105   return std::any_of(memory_card_types.begin(), memory_card_types.end(), [](MemoryCardType t) {
    106     return (t == MemoryCardType::PerGame || t == MemoryCardType::PerGameTitle);
    107   });
    108 }
    109 
    110 void Settings::CPUOverclockPercentToFraction(u32 percent, u32* numerator, u32* denominator)
    111 {
    112   const u32 percent_gcd = std::gcd(percent, 100);
    113   *numerator = percent / percent_gcd;
    114   *denominator = 100u / percent_gcd;
    115 }
    116 
    117 u32 Settings::CPUOverclockFractionToPercent(u32 numerator, u32 denominator)
    118 {
    119   return (numerator * 100u) / denominator;
    120 }
    121 
    122 void Settings::SetCPUOverclockPercent(u32 percent)
    123 {
    124   CPUOverclockPercentToFraction(percent, &cpu_overclock_numerator, &cpu_overclock_denominator);
    125 }
    126 
    127 u32 Settings::GetCPUOverclockPercent() const
    128 {
    129   return CPUOverclockFractionToPercent(cpu_overclock_numerator, cpu_overclock_denominator);
    130 }
    131 
    132 void Settings::UpdateOverclockActive()
    133 {
    134   cpu_overclock_active = (cpu_overclock_enable && (cpu_overclock_numerator != 1 || cpu_overclock_denominator != 1));
    135 }
    136 
    137 void Settings::Load(SettingsInterface& si, SettingsInterface& controller_si)
    138 {
    139   region =
    140     ParseConsoleRegionName(
    141       si.GetStringValue("Console", "Region", Settings::GetConsoleRegionName(Settings::DEFAULT_CONSOLE_REGION)).c_str())
    142       .value_or(DEFAULT_CONSOLE_REGION);
    143   enable_8mb_ram = si.GetBoolValue("Console", "Enable8MBRAM", false);
    144 
    145   emulation_speed = si.GetFloatValue("Main", "EmulationSpeed", 1.0f);
    146   fast_forward_speed = si.GetFloatValue("Main", "FastForwardSpeed", 0.0f);
    147   turbo_speed = si.GetFloatValue("Main", "TurboSpeed", 0.0f);
    148   sync_to_host_refresh_rate = si.GetBoolValue("Main", "SyncToHostRefreshRate", false);
    149   increase_timer_resolution = si.GetBoolValue("Main", "IncreaseTimerResolution", true);
    150   inhibit_screensaver = si.GetBoolValue("Main", "InhibitScreensaver", true);
    151   start_paused = si.GetBoolValue("Main", "StartPaused", false);
    152   start_fullscreen = si.GetBoolValue("Main", "StartFullscreen", false);
    153   pause_on_focus_loss = si.GetBoolValue("Main", "PauseOnFocusLoss", false);
    154   pause_on_controller_disconnection = si.GetBoolValue("Main", "PauseOnControllerDisconnection", false);
    155   save_state_on_exit = si.GetBoolValue("Main", "SaveStateOnExit", true);
    156   create_save_state_backups = si.GetBoolValue("Main", "CreateSaveStateBackups", DEFAULT_SAVE_STATE_BACKUPS);
    157   confim_power_off = si.GetBoolValue("Main", "ConfirmPowerOff", true);
    158   load_devices_from_save_states = si.GetBoolValue("Main", "LoadDevicesFromSaveStates", false);
    159   apply_compatibility_settings = si.GetBoolValue("Main", "ApplyCompatibilitySettings", true);
    160   apply_game_settings = si.GetBoolValue("Main", "ApplyGameSettings", true);
    161   enable_cheats = si.GetBoolValue("Console", "EnableCheats", false);
    162   disable_all_enhancements = si.GetBoolValue("Main", "DisableAllEnhancements", false);
    163   enable_discord_presence = si.GetBoolValue("Main", "EnableDiscordPresence", false);
    164   rewind_enable = si.GetBoolValue("Main", "RewindEnable", false);
    165   rewind_save_frequency = si.GetFloatValue("Main", "RewindFrequency", 10.0f);
    166   rewind_save_slots = static_cast<u32>(si.GetIntValue("Main", "RewindSaveSlots", 10));
    167   runahead_frames = static_cast<u32>(si.GetIntValue("Main", "RunaheadFrameCount", 0));
    168 
    169   pine_enable = si.GetBoolValue("PINE", "Enabled", false);
    170   pine_slot = static_cast<u16>(
    171     std::min<u32>(si.GetUIntValue("PINE", "Slot", DEFAULT_PINE_SLOT), std::numeric_limits<u16>::max()));
    172 
    173   cpu_execution_mode =
    174     ParseCPUExecutionMode(
    175       si.GetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(DEFAULT_CPU_EXECUTION_MODE)).c_str())
    176       .value_or(DEFAULT_CPU_EXECUTION_MODE);
    177   cpu_overclock_numerator = std::max(si.GetIntValue("CPU", "OverclockNumerator", 1), 1);
    178   cpu_overclock_denominator = std::max(si.GetIntValue("CPU", "OverclockDenominator", 1), 1);
    179   cpu_overclock_enable = si.GetBoolValue("CPU", "OverclockEnable", false);
    180   UpdateOverclockActive();
    181   cpu_recompiler_memory_exceptions = si.GetBoolValue("CPU", "RecompilerMemoryExceptions", false);
    182   cpu_recompiler_block_linking = si.GetBoolValue("CPU", "RecompilerBlockLinking", true);
    183   cpu_recompiler_icache = si.GetBoolValue("CPU", "RecompilerICache", false);
    184   cpu_fastmem_mode = ParseCPUFastmemMode(
    185                        si.GetStringValue("CPU", "FastmemMode", GetCPUFastmemModeName(DEFAULT_CPU_FASTMEM_MODE)).c_str())
    186                        .value_or(DEFAULT_CPU_FASTMEM_MODE);
    187 
    188   gpu_renderer = ParseRendererName(si.GetStringValue("GPU", "Renderer", GetRendererName(DEFAULT_GPU_RENDERER)).c_str())
    189                    .value_or(DEFAULT_GPU_RENDERER);
    190   gpu_adapter = si.GetStringValue("GPU", "Adapter", "");
    191   gpu_resolution_scale = static_cast<u8>(si.GetIntValue("GPU", "ResolutionScale", 1));
    192   gpu_multisamples = static_cast<u8>(si.GetIntValue("GPU", "Multisamples", 1));
    193   gpu_use_debug_device = si.GetBoolValue("GPU", "UseDebugDevice", false);
    194   gpu_disable_shader_cache = si.GetBoolValue("GPU", "DisableShaderCache", false);
    195   gpu_disable_dual_source_blend = si.GetBoolValue("GPU", "DisableDualSourceBlend", false);
    196   gpu_disable_framebuffer_fetch = si.GetBoolValue("GPU", "DisableFramebufferFetch", false);
    197   gpu_disable_texture_buffers = si.GetBoolValue("GPU", "DisableTextureBuffers", false);
    198   gpu_disable_texture_copy_to_self = si.GetBoolValue("GPU", "DisableTextureCopyToSelf", false);
    199   gpu_disable_memory_import = si.GetBoolValue("GPU", "DisableMemoryImport", false);
    200   gpu_disable_raster_order_views = si.GetBoolValue("GPU", "DisableRasterOrderViews", false);
    201   gpu_per_sample_shading = si.GetBoolValue("GPU", "PerSampleShading", false);
    202   gpu_use_thread = si.GetBoolValue("GPU", "UseThread", true);
    203   gpu_use_software_renderer_for_readbacks = si.GetBoolValue("GPU", "UseSoftwareRendererForReadbacks", false);
    204   gpu_threaded_presentation = si.GetBoolValue("GPU", "ThreadedPresentation", DEFAULT_THREADED_PRESENTATION);
    205   gpu_true_color = si.GetBoolValue("GPU", "TrueColor", true);
    206   gpu_debanding = si.GetBoolValue("GPU", "Debanding", false);
    207   gpu_scaled_dithering = si.GetBoolValue("GPU", "ScaledDithering", true);
    208   gpu_force_round_texcoords = si.GetBoolValue("GPU", "ForceRoundTextureCoordinates", false);
    209   gpu_accurate_blending = si.GetBoolValue("GPU", "AccurateBlending", false);
    210   gpu_texture_filter =
    211     ParseTextureFilterName(
    212       si.GetStringValue("GPU", "TextureFilter", GetTextureFilterName(DEFAULT_GPU_TEXTURE_FILTER)).c_str())
    213       .value_or(DEFAULT_GPU_TEXTURE_FILTER);
    214   gpu_sprite_texture_filter =
    215     ParseTextureFilterName(
    216       si.GetStringValue("GPU", "SpriteTextureFilter", GetTextureFilterName(gpu_texture_filter)).c_str())
    217       .value_or(gpu_texture_filter);
    218   gpu_line_detect_mode =
    219     ParseLineDetectModeName(
    220       si.GetStringValue("GPU", "LineDetectMode", GetLineDetectModeName(DEFAULT_GPU_LINE_DETECT_MODE)).c_str())
    221       .value_or(DEFAULT_GPU_LINE_DETECT_MODE);
    222   gpu_downsample_mode =
    223     ParseDownsampleModeName(
    224       si.GetStringValue("GPU", "DownsampleMode", GetDownsampleModeName(DEFAULT_GPU_DOWNSAMPLE_MODE)).c_str())
    225       .value_or(DEFAULT_GPU_DOWNSAMPLE_MODE);
    226   gpu_downsample_scale = static_cast<u8>(si.GetUIntValue("GPU", "DownsampleScale", 1));
    227   gpu_wireframe_mode =
    228     ParseGPUWireframeMode(
    229       si.GetStringValue("GPU", "WireframeMode", GetGPUWireframeModeName(DEFAULT_GPU_WIREFRAME_MODE)).c_str())
    230       .value_or(DEFAULT_GPU_WIREFRAME_MODE);
    231   gpu_disable_interlacing = si.GetBoolValue("GPU", "DisableInterlacing", true);
    232   gpu_force_ntsc_timings = si.GetBoolValue("GPU", "ForceNTSCTimings", false);
    233   gpu_widescreen_hack = si.GetBoolValue("GPU", "WidescreenHack", false);
    234   display_24bit_chroma_smoothing = si.GetBoolValue("GPU", "ChromaSmoothing24Bit", false);
    235   gpu_pgxp_enable = si.GetBoolValue("GPU", "PGXPEnable", false);
    236   gpu_pgxp_culling = si.GetBoolValue("GPU", "PGXPCulling", true);
    237   gpu_pgxp_texture_correction = si.GetBoolValue("GPU", "PGXPTextureCorrection", true);
    238   gpu_pgxp_color_correction = si.GetBoolValue("GPU", "PGXPColorCorrection", false);
    239   gpu_pgxp_vertex_cache = si.GetBoolValue("GPU", "PGXPVertexCache", false);
    240   gpu_pgxp_cpu = si.GetBoolValue("GPU", "PGXPCPU", false);
    241   gpu_pgxp_preserve_proj_fp = si.GetBoolValue("GPU", "PGXPPreserveProjFP", false);
    242   gpu_pgxp_tolerance = si.GetFloatValue("GPU", "PGXPTolerance", -1.0f);
    243   gpu_pgxp_depth_buffer = si.GetBoolValue("GPU", "PGXPDepthBuffer", false);
    244   gpu_pgxp_disable_2d = si.GetBoolValue("GPU", "PGXPDisableOn2DPolygons", false);
    245   SetPGXPDepthClearThreshold(si.GetFloatValue("GPU", "PGXPDepthClearThreshold", DEFAULT_GPU_PGXP_DEPTH_THRESHOLD));
    246 
    247   display_deinterlacing_mode =
    248     ParseDisplayDeinterlacingMode(si.GetStringValue("Display", "DeinterlacingMode",
    249                                                     GetDisplayDeinterlacingModeName(DEFAULT_DISPLAY_DEINTERLACING_MODE))
    250                                     .c_str())
    251       .value_or(DEFAULT_DISPLAY_DEINTERLACING_MODE);
    252   display_crop_mode =
    253     ParseDisplayCropMode(
    254       si.GetStringValue("Display", "CropMode", GetDisplayCropModeName(DEFAULT_DISPLAY_CROP_MODE)).c_str())
    255       .value_or(DEFAULT_DISPLAY_CROP_MODE);
    256   display_aspect_ratio =
    257     ParseDisplayAspectRatio(
    258       si.GetStringValue("Display", "AspectRatio", GetDisplayAspectRatioName(DEFAULT_DISPLAY_ASPECT_RATIO)).c_str())
    259       .value_or(DEFAULT_DISPLAY_ASPECT_RATIO);
    260   display_aspect_ratio_custom_numerator = static_cast<u16>(
    261     std::clamp<int>(si.GetIntValue("Display", "CustomAspectRatioNumerator", 4), 1, std::numeric_limits<u16>::max()));
    262   display_aspect_ratio_custom_denominator = static_cast<u16>(
    263     std::clamp<int>(si.GetIntValue("Display", "CustomAspectRatioDenominator", 3), 1, std::numeric_limits<u16>::max()));
    264   display_alignment =
    265     ParseDisplayAlignment(
    266       si.GetStringValue("Display", "Alignment", GetDisplayAlignmentName(DEFAULT_DISPLAY_ALIGNMENT)).c_str())
    267       .value_or(DEFAULT_DISPLAY_ALIGNMENT);
    268   display_rotation =
    269     ParseDisplayRotation(
    270       si.GetStringValue("Display", "Rotation", GetDisplayRotationName(DEFAULT_DISPLAY_ROTATION)).c_str())
    271       .value_or(DEFAULT_DISPLAY_ROTATION);
    272   display_scaling =
    273     ParseDisplayScaling(si.GetStringValue("Display", "Scaling", GetDisplayScalingName(DEFAULT_DISPLAY_SCALING)).c_str())
    274       .value_or(DEFAULT_DISPLAY_SCALING);
    275   display_exclusive_fullscreen_control =
    276     ParseDisplayExclusiveFullscreenControl(
    277       si.GetStringValue("Display", "ExclusiveFullscreenControl",
    278                         GetDisplayExclusiveFullscreenControlName(DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL))
    279         .c_str())
    280       .value_or(DEFAULT_DISPLAY_EXCLUSIVE_FULLSCREEN_CONTROL);
    281   display_screenshot_mode =
    282     ParseDisplayScreenshotMode(
    283       si.GetStringValue("Display", "ScreenshotMode", GetDisplayScreenshotModeName(DEFAULT_DISPLAY_SCREENSHOT_MODE))
    284         .c_str())
    285       .value_or(DEFAULT_DISPLAY_SCREENSHOT_MODE);
    286   display_screenshot_format =
    287     ParseDisplayScreenshotFormat(si.GetStringValue("Display", "ScreenshotFormat",
    288                                                    GetDisplayScreenshotFormatName(DEFAULT_DISPLAY_SCREENSHOT_FORMAT))
    289                                    .c_str())
    290       .value_or(DEFAULT_DISPLAY_SCREENSHOT_FORMAT);
    291   display_screenshot_quality = static_cast<u8>(
    292     std::clamp<u32>(si.GetUIntValue("Display", "ScreenshotQuality", DEFAULT_DISPLAY_SCREENSHOT_QUALITY), 1, 100));
    293   display_optimal_frame_pacing = si.GetBoolValue("Display", "OptimalFramePacing", false);
    294   display_pre_frame_sleep = si.GetBoolValue("Display", "PreFrameSleep", false);
    295   display_pre_frame_sleep_buffer =
    296     si.GetFloatValue("Display", "PreFrameSleepBuffer", DEFAULT_DISPLAY_PRE_FRAME_SLEEP_BUFFER);
    297   display_skip_presenting_duplicate_frames = si.GetBoolValue("Display", "SkipPresentingDuplicateFrames", false);
    298   display_vsync = si.GetBoolValue("Display", "VSync", false);
    299   display_disable_mailbox_presentation = si.GetBoolValue("Display", "DisableMailboxPresentation", false);
    300   display_force_4_3_for_24bit = si.GetBoolValue("Display", "Force4_3For24Bit", false);
    301   display_active_start_offset = static_cast<s16>(si.GetIntValue("Display", "ActiveStartOffset", 0));
    302   display_active_end_offset = static_cast<s16>(si.GetIntValue("Display", "ActiveEndOffset", 0));
    303   display_line_start_offset = static_cast<s8>(si.GetIntValue("Display", "LineStartOffset", 0));
    304   display_line_end_offset = static_cast<s8>(si.GetIntValue("Display", "LineEndOffset", 0));
    305   display_show_osd_messages = si.GetBoolValue("Display", "ShowOSDMessages", true);
    306   display_show_fps = si.GetBoolValue("Display", "ShowFPS", false);
    307   display_show_speed = si.GetBoolValue("Display", "ShowSpeed", false);
    308   display_show_gpu_stats = si.GetBoolValue("Display", "ShowGPUStatistics", false);
    309   display_show_resolution = si.GetBoolValue("Display", "ShowResolution", false);
    310   display_show_latency_stats = si.GetBoolValue("Display", "ShowLatencyStatistics", false);
    311   display_show_cpu_usage = si.GetBoolValue("Display", "ShowCPU", false);
    312   display_show_gpu_usage = si.GetBoolValue("Display", "ShowGPU", false);
    313   display_show_frame_times = si.GetBoolValue("Display", "ShowFrameTimes", false);
    314   display_show_status_indicators = si.GetBoolValue("Display", "ShowStatusIndicators", true);
    315   display_show_inputs = si.GetBoolValue("Display", "ShowInputs", false);
    316   display_show_enhancements = si.GetBoolValue("Display", "ShowEnhancements", false);
    317   display_stretch_vertically = si.GetBoolValue("Display", "StretchVertically", false);
    318   display_osd_scale = si.GetFloatValue("Display", "OSDScale", DEFAULT_OSD_SCALE);
    319 
    320   save_state_compression = ParseSaveStateCompressionModeName(
    321                              si.GetStringValue("Main", "SaveStateCompression",
    322                                                GetSaveStateCompressionModeName(DEFAULT_SAVE_STATE_COMPRESSION_MODE))
    323                                .c_str())
    324                              .value_or(DEFAULT_SAVE_STATE_COMPRESSION_MODE);
    325 
    326   cdrom_readahead_sectors =
    327     static_cast<u8>(si.GetIntValue("CDROM", "ReadaheadSectors", DEFAULT_CDROM_READAHEAD_SECTORS));
    328   cdrom_mechacon_version =
    329     ParseCDROMMechVersionName(
    330       si.GetStringValue("CDROM", "MechaconVersion", GetCDROMMechVersionName(DEFAULT_CDROM_MECHACON_VERSION)).c_str())
    331       .value_or(DEFAULT_CDROM_MECHACON_VERSION);
    332   cdrom_region_check = si.GetBoolValue("CDROM", "RegionCheck", false);
    333   cdrom_load_image_to_ram = si.GetBoolValue("CDROM", "LoadImageToRAM", false);
    334   cdrom_load_image_patches = si.GetBoolValue("CDROM", "LoadImagePatches", false);
    335   cdrom_mute_cd_audio = si.GetBoolValue("CDROM", "MuteCDAudio", false);
    336   cdrom_read_speedup = si.GetIntValue("CDROM", "ReadSpeedup", 1);
    337   cdrom_seek_speedup = si.GetIntValue("CDROM", "SeekSpeedup", 1);
    338 
    339   audio_backend =
    340     AudioStream::ParseBackendName(
    341       si.GetStringValue("Audio", "Backend", AudioStream::GetBackendName(AudioStream::DEFAULT_BACKEND)).c_str())
    342       .value_or(AudioStream::DEFAULT_BACKEND);
    343   audio_driver = si.GetStringValue("Audio", "Driver");
    344   audio_output_device = si.GetStringValue("Audio", "OutputDevice");
    345   audio_stream_parameters.Load(si, "Audio");
    346   audio_output_volume = si.GetUIntValue("Audio", "OutputVolume", 100);
    347   audio_fast_forward_volume = si.GetUIntValue("Audio", "FastForwardVolume", 100);
    348 
    349   audio_output_muted = si.GetBoolValue("Audio", "OutputMuted", false);
    350 
    351   use_old_mdec_routines = si.GetBoolValue("Hacks", "UseOldMDECRoutines", false);
    352   export_shared_memory = si.GetBoolValue("Hacks", "ExportSharedMemory", false);
    353   pcdrv_enable = si.GetBoolValue("PCDrv", "Enabled", false);
    354   pcdrv_enable_writes = si.GetBoolValue("PCDrv", "EnableWrites", false);
    355   pcdrv_root = si.GetStringValue("PCDrv", "Root");
    356 
    357   dma_max_slice_ticks = si.GetIntValue("Hacks", "DMAMaxSliceTicks", DEFAULT_DMA_MAX_SLICE_TICKS);
    358   dma_halt_ticks = si.GetIntValue("Hacks", "DMAHaltTicks", DEFAULT_DMA_HALT_TICKS);
    359   gpu_fifo_size = static_cast<u32>(si.GetIntValue("Hacks", "GPUFIFOSize", DEFAULT_GPU_FIFO_SIZE));
    360   gpu_max_run_ahead = si.GetIntValue("Hacks", "GPUMaxRunAhead", DEFAULT_GPU_MAX_RUN_AHEAD);
    361 
    362   bios_tty_logging = si.GetBoolValue("BIOS", "TTYLogging", false);
    363   bios_patch_fast_boot = si.GetBoolValue("BIOS", "PatchFastBoot", DEFAULT_FAST_BOOT_VALUE);
    364 
    365   multitap_mode =
    366     ParseMultitapModeName(
    367       controller_si.GetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(DEFAULT_MULTITAP_MODE))
    368         .c_str())
    369       .value_or(DEFAULT_MULTITAP_MODE);
    370 
    371   const std::array<bool, 2> mtap_enabled = {{IsPort1MultitapEnabled(), IsPort2MultitapEnabled()}};
    372   for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
    373   {
    374     // Ignore types when multitap not enabled
    375     const auto [port, slot] = Controller::ConvertPadToPortAndSlot(i);
    376     if (Controller::PadIsMultitapSlot(slot) && !mtap_enabled[port])
    377     {
    378       controller_types[i] = ControllerType::None;
    379       continue;
    380     }
    381 
    382     const ControllerType default_type = (i == 0) ? DEFAULT_CONTROLLER_1_TYPE : DEFAULT_CONTROLLER_2_TYPE;
    383     const Controller::ControllerInfo* cinfo = Controller::GetControllerInfo(controller_si.GetTinyStringValue(
    384       Controller::GetSettingsSection(i).c_str(), "Type", Controller::GetControllerInfo(default_type)->name));
    385     controller_types[i] = cinfo ? cinfo->type : default_type;
    386   }
    387 
    388   memory_card_types[0] =
    389     ParseMemoryCardTypeName(
    390       si.GetStringValue("MemoryCards", "Card1Type", GetMemoryCardTypeName(DEFAULT_MEMORY_CARD_1_TYPE)).c_str())
    391       .value_or(DEFAULT_MEMORY_CARD_1_TYPE);
    392   memory_card_types[1] =
    393     ParseMemoryCardTypeName(
    394       si.GetStringValue("MemoryCards", "Card2Type", GetMemoryCardTypeName(DEFAULT_MEMORY_CARD_2_TYPE)).c_str())
    395       .value_or(DEFAULT_MEMORY_CARD_2_TYPE);
    396   memory_card_paths[0] = si.GetStringValue("MemoryCards", "Card1Path", "");
    397   memory_card_paths[1] = si.GetStringValue("MemoryCards", "Card2Path", "");
    398   memory_card_use_playlist_title = si.GetBoolValue("MemoryCards", "UsePlaylistTitle", true);
    399 
    400   achievements_enabled = si.GetBoolValue("Cheevos", "Enabled", false);
    401   achievements_hardcore_mode = si.GetBoolValue("Cheevos", "ChallengeMode", false);
    402   achievements_notifications = si.GetBoolValue("Cheevos", "Notifications", true);
    403   achievements_leaderboard_notifications = si.GetBoolValue("Cheevos", "LeaderboardNotifications", true);
    404   achievements_sound_effects = si.GetBoolValue("Cheevos", "SoundEffects", true);
    405   achievements_overlays = si.GetBoolValue("Cheevos", "Overlays", true);
    406   achievements_encore_mode = si.GetBoolValue("Cheevos", "EncoreMode", false);
    407   achievements_spectator_mode = si.GetBoolValue("Cheevos", "SpectatorMode", false);
    408   achievements_unofficial_test_mode = si.GetBoolValue("Cheevos", "UnofficialTestMode", false);
    409   achievements_use_first_disc_from_playlist = si.GetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", true);
    410   achievements_use_raintegration = si.GetBoolValue("Cheevos", "UseRAIntegration", false);
    411   achievements_notification_duration =
    412     si.GetIntValue("Cheevos", "NotificationsDuration", DEFAULT_ACHIEVEMENT_NOTIFICATION_TIME);
    413   achievements_leaderboard_duration =
    414     si.GetIntValue("Cheevos", "LeaderboardsDuration", DEFAULT_LEADERBOARD_NOTIFICATION_TIME);
    415 
    416   log_level = ParseLogLevelName(si.GetStringValue("Logging", "LogLevel", GetLogLevelName(DEFAULT_LOG_LEVEL)).c_str())
    417                 .value_or(DEFAULT_LOG_LEVEL);
    418   log_filter = si.GetStringValue("Logging", "LogFilter", "");
    419   log_timestamps = si.GetBoolValue("Logging", "LogTimestamps", true);
    420   log_to_console = si.GetBoolValue("Logging", "LogToConsole", DEFAULT_LOG_TO_CONSOLE);
    421   log_to_debug = si.GetBoolValue("Logging", "LogToDebug", false);
    422   log_to_window = si.GetBoolValue("Logging", "LogToWindow", false);
    423   log_to_file = si.GetBoolValue("Logging", "LogToFile", false);
    424 
    425   debugging.show_vram = si.GetBoolValue("Debug", "ShowVRAM");
    426   debugging.dump_cpu_to_vram_copies = si.GetBoolValue("Debug", "DumpCPUToVRAMCopies");
    427   debugging.dump_vram_to_cpu_copies = si.GetBoolValue("Debug", "DumpVRAMToCPUCopies");
    428   debugging.enable_gdb_server = si.GetBoolValue("Debug", "EnableGDBServer");
    429   debugging.gdb_server_port = static_cast<u16>(si.GetIntValue("Debug", "GDBServerPort"));
    430   debugging.show_gpu_state = si.GetBoolValue("Debug", "ShowGPUState");
    431   debugging.show_cdrom_state = si.GetBoolValue("Debug", "ShowCDROMState");
    432   debugging.show_spu_state = si.GetBoolValue("Debug", "ShowSPUState");
    433   debugging.show_timers_state = si.GetBoolValue("Debug", "ShowTimersState");
    434   debugging.show_mdec_state = si.GetBoolValue("Debug", "ShowMDECState");
    435   debugging.show_dma_state = si.GetBoolValue("Debug", "ShowDMAState");
    436 
    437   texture_replacements.enable_vram_write_replacements =
    438     si.GetBoolValue("TextureReplacements", "EnableVRAMWriteReplacements", false);
    439   texture_replacements.preload_textures = si.GetBoolValue("TextureReplacements", "PreloadTextures", false);
    440   texture_replacements.dump_vram_writes = si.GetBoolValue("TextureReplacements", "DumpVRAMWrites", false);
    441   texture_replacements.dump_vram_write_force_alpha_channel =
    442     si.GetBoolValue("TextureReplacements", "DumpVRAMWriteForceAlphaChannel", true);
    443   texture_replacements.dump_vram_write_width_threshold =
    444     si.GetIntValue("TextureReplacements", "DumpVRAMWriteWidthThreshold", 128);
    445   texture_replacements.dump_vram_write_height_threshold =
    446     si.GetIntValue("TextureReplacements", "DumpVRAMWriteHeightThreshold", 128);
    447 
    448 #ifdef __ANDROID__
    449   // No expansion due to license incompatibility.
    450   audio_expansion_mode = AudioExpansionMode::Disabled;
    451 
    452   // Android users are incredibly silly and don't understand that stretch is in the aspect ratio list...
    453   if (si.GetBoolValue("Display", "Stretch", false))
    454     display_aspect_ratio = DisplayAspectRatio::MatchWindow;
    455 #endif
    456 }
    457 
    458 void Settings::Save(SettingsInterface& si, bool ignore_base) const
    459 {
    460   si.SetStringValue("Console", "Region", GetConsoleRegionName(region));
    461   si.SetBoolValue("Console", "Enable8MBRAM", enable_8mb_ram);
    462 
    463   si.SetFloatValue("Main", "EmulationSpeed", emulation_speed);
    464   si.SetFloatValue("Main", "FastForwardSpeed", fast_forward_speed);
    465   si.SetFloatValue("Main", "TurboSpeed", turbo_speed);
    466 
    467   if (!ignore_base)
    468   {
    469     si.SetBoolValue("Main", "SyncToHostRefreshRate", sync_to_host_refresh_rate);
    470     si.SetBoolValue("Main", "IncreaseTimerResolution", increase_timer_resolution);
    471     si.SetBoolValue("Main", "InhibitScreensaver", inhibit_screensaver);
    472     si.SetBoolValue("Main", "StartPaused", start_paused);
    473     si.SetBoolValue("Main", "StartFullscreen", start_fullscreen);
    474     si.SetBoolValue("Main", "PauseOnFocusLoss", pause_on_focus_loss);
    475     si.SetBoolValue("Main", "PauseOnControllerDisconnection", pause_on_controller_disconnection);
    476     si.SetBoolValue("Main", "SaveStateOnExit", save_state_on_exit);
    477     si.SetBoolValue("Main", "CreateSaveStateBackups", create_save_state_backups);
    478     si.SetStringValue("Main", "SaveStateCompression", GetSaveStateCompressionModeName(save_state_compression));
    479     si.SetBoolValue("Main", "ConfirmPowerOff", confim_power_off);
    480     si.SetBoolValue("Main", "EnableDiscordPresence", enable_discord_presence);
    481   }
    482 
    483   si.SetBoolValue("Main", "LoadDevicesFromSaveStates", load_devices_from_save_states);
    484   si.SetBoolValue("Console", "EnableCheats", enable_cheats);
    485   si.SetBoolValue("Main", "DisableAllEnhancements", disable_all_enhancements);
    486   si.SetBoolValue("Main", "RewindEnable", rewind_enable);
    487   si.SetFloatValue("Main", "RewindFrequency", rewind_save_frequency);
    488   si.SetIntValue("Main", "RewindSaveSlots", rewind_save_slots);
    489   si.SetIntValue("Main", "RunaheadFrameCount", runahead_frames);
    490 
    491   si.SetBoolValue("PINE", "Enabled", pine_enable);
    492   si.SetUIntValue("PINE", "Slot", pine_slot);
    493 
    494   si.SetStringValue("CPU", "ExecutionMode", GetCPUExecutionModeName(cpu_execution_mode));
    495   si.SetBoolValue("CPU", "OverclockEnable", cpu_overclock_enable);
    496   si.SetIntValue("CPU", "OverclockNumerator", cpu_overclock_numerator);
    497   si.SetIntValue("CPU", "OverclockDenominator", cpu_overclock_denominator);
    498   si.SetBoolValue("CPU", "RecompilerMemoryExceptions", cpu_recompiler_memory_exceptions);
    499   si.SetBoolValue("CPU", "RecompilerBlockLinking", cpu_recompiler_block_linking);
    500   si.SetBoolValue("CPU", "RecompilerICache", cpu_recompiler_icache);
    501   si.SetStringValue("CPU", "FastmemMode", GetCPUFastmemModeName(cpu_fastmem_mode));
    502 
    503   si.SetStringValue("GPU", "Renderer", GetRendererName(gpu_renderer));
    504   si.SetStringValue("GPU", "Adapter", gpu_adapter.c_str());
    505   si.SetIntValue("GPU", "ResolutionScale", static_cast<long>(gpu_resolution_scale));
    506   si.SetIntValue("GPU", "Multisamples", static_cast<long>(gpu_multisamples));
    507 
    508   if (!ignore_base)
    509   {
    510     si.SetBoolValue("GPU", "UseDebugDevice", gpu_use_debug_device);
    511     si.SetBoolValue("GPU", "DisableShaderCache", gpu_disable_shader_cache);
    512     si.SetBoolValue("GPU", "DisableDualSourceBlend", gpu_disable_dual_source_blend);
    513     si.SetBoolValue("GPU", "DisableFramebufferFetch", gpu_disable_framebuffer_fetch);
    514     si.SetBoolValue("GPU", "DisableTextureBuffers", gpu_disable_texture_buffers);
    515     si.SetBoolValue("GPU", "DisableTextureCopyToSelf", gpu_disable_texture_copy_to_self);
    516     si.SetBoolValue("GPU", "DisableMemoryImport", gpu_disable_memory_import);
    517     si.SetBoolValue("GPU", "DisableRasterOrderViews", gpu_disable_raster_order_views);
    518   }
    519 
    520   si.SetBoolValue("GPU", "PerSampleShading", gpu_per_sample_shading);
    521   si.SetBoolValue("GPU", "UseThread", gpu_use_thread);
    522   si.SetBoolValue("GPU", "ThreadedPresentation", gpu_threaded_presentation);
    523   si.SetBoolValue("GPU", "UseSoftwareRendererForReadbacks", gpu_use_software_renderer_for_readbacks);
    524   si.SetBoolValue("GPU", "TrueColor", gpu_true_color);
    525   si.SetBoolValue("GPU", "Debanding", gpu_debanding);
    526   si.SetBoolValue("GPU", "ScaledDithering", gpu_scaled_dithering);
    527   si.SetBoolValue("GPU", "ForceRoundTextureCoordinates", gpu_force_round_texcoords);
    528   si.SetBoolValue("GPU", "AccurateBlending", gpu_accurate_blending);
    529   si.SetStringValue("GPU", "TextureFilter", GetTextureFilterName(gpu_texture_filter));
    530   si.SetStringValue(
    531     "GPU", "SpriteTextureFilter",
    532     (gpu_sprite_texture_filter != gpu_texture_filter) ? GetTextureFilterName(gpu_sprite_texture_filter) : "");
    533   si.SetStringValue("GPU", "LineDetectMode", GetLineDetectModeName(gpu_line_detect_mode));
    534   si.SetStringValue("GPU", "DownsampleMode", GetDownsampleModeName(gpu_downsample_mode));
    535   si.SetUIntValue("GPU", "DownsampleScale", gpu_downsample_scale);
    536   si.SetStringValue("GPU", "WireframeMode", GetGPUWireframeModeName(gpu_wireframe_mode));
    537   si.SetBoolValue("GPU", "DisableInterlacing", gpu_disable_interlacing);
    538   si.SetBoolValue("GPU", "ForceNTSCTimings", gpu_force_ntsc_timings);
    539   si.SetBoolValue("GPU", "WidescreenHack", gpu_widescreen_hack);
    540   si.SetBoolValue("GPU", "ChromaSmoothing24Bit", display_24bit_chroma_smoothing);
    541   si.SetBoolValue("GPU", "PGXPEnable", gpu_pgxp_enable);
    542   si.SetBoolValue("GPU", "PGXPCulling", gpu_pgxp_culling);
    543   si.SetBoolValue("GPU", "PGXPTextureCorrection", gpu_pgxp_texture_correction);
    544   si.SetBoolValue("GPU", "PGXPColorCorrection", gpu_pgxp_color_correction);
    545   si.SetBoolValue("GPU", "PGXPVertexCache", gpu_pgxp_vertex_cache);
    546   si.SetBoolValue("GPU", "PGXPCPU", gpu_pgxp_cpu);
    547   si.SetBoolValue("GPU", "PGXPPreserveProjFP", gpu_pgxp_preserve_proj_fp);
    548   si.SetFloatValue("GPU", "PGXPTolerance", gpu_pgxp_tolerance);
    549   si.SetBoolValue("GPU", "PGXPDepthBuffer", gpu_pgxp_depth_buffer);
    550   si.SetBoolValue("GPU", "PGXPDisableOn2DPolygons", gpu_pgxp_disable_2d);
    551   si.SetFloatValue("GPU", "PGXPDepthClearThreshold", GetPGXPDepthClearThreshold());
    552 
    553   si.SetStringValue("Display", "DeinterlacingMode", GetDisplayDeinterlacingModeName(display_deinterlacing_mode));
    554   si.SetStringValue("Display", "CropMode", GetDisplayCropModeName(display_crop_mode));
    555   si.SetIntValue("Display", "ActiveStartOffset", display_active_start_offset);
    556   si.SetIntValue("Display", "ActiveEndOffset", display_active_end_offset);
    557   si.SetIntValue("Display", "LineStartOffset", display_line_start_offset);
    558   si.SetIntValue("Display", "LineEndOffset", display_line_end_offset);
    559   si.SetBoolValue("Display", "Force4_3For24Bit", display_force_4_3_for_24bit);
    560   si.SetStringValue("Display", "AspectRatio", GetDisplayAspectRatioName(display_aspect_ratio));
    561   si.SetStringValue("Display", "Alignment", GetDisplayAlignmentName(display_alignment));
    562   si.SetStringValue("Display", "Rotation", GetDisplayRotationName(display_rotation));
    563   si.SetStringValue("Display", "Scaling", GetDisplayScalingName(display_scaling));
    564   si.SetBoolValue("Display", "OptimalFramePacing", display_optimal_frame_pacing);
    565   si.SetBoolValue("Display", "PreFrameSleep", display_pre_frame_sleep);
    566   si.SetBoolValue("Display", "SkipPresentingDuplicateFrames", display_skip_presenting_duplicate_frames);
    567   si.SetFloatValue("Display", "PreFrameSleepBuffer", display_pre_frame_sleep_buffer);
    568   si.SetBoolValue("Display", "VSync", display_vsync);
    569   si.SetBoolValue("Display", "DisableMailboxPresentation", display_disable_mailbox_presentation);
    570   si.SetStringValue("Display", "ExclusiveFullscreenControl",
    571                     GetDisplayExclusiveFullscreenControlName(display_exclusive_fullscreen_control));
    572   si.SetStringValue("Display", "ScreenshotMode", GetDisplayScreenshotModeName(display_screenshot_mode));
    573   si.SetStringValue("Display", "ScreenshotFormat", GetDisplayScreenshotFormatName(display_screenshot_format));
    574   si.SetUIntValue("Display", "ScreenshotQuality", display_screenshot_quality);
    575   si.SetIntValue("Display", "CustomAspectRatioNumerator", display_aspect_ratio_custom_numerator);
    576   si.GetIntValue("Display", "CustomAspectRatioDenominator", display_aspect_ratio_custom_denominator);
    577   if (!ignore_base)
    578   {
    579     si.SetBoolValue("Display", "ShowOSDMessages", display_show_osd_messages);
    580     si.SetBoolValue("Display", "ShowFPS", display_show_fps);
    581     si.SetBoolValue("Display", "ShowSpeed", display_show_speed);
    582     si.SetBoolValue("Display", "ShowResolution", display_show_resolution);
    583     si.SetBoolValue("Display", "ShowLatencyStatistics", display_show_latency_stats);
    584     si.SetBoolValue("Display", "ShowGPUStatistics", display_show_gpu_stats);
    585     si.SetBoolValue("Display", "ShowCPU", display_show_cpu_usage);
    586     si.SetBoolValue("Display", "ShowGPU", display_show_gpu_usage);
    587     si.SetBoolValue("Display", "ShowFrameTimes", display_show_frame_times);
    588     si.SetBoolValue("Display", "ShowStatusIndicators", display_show_status_indicators);
    589     si.SetBoolValue("Display", "ShowInputs", display_show_inputs);
    590     si.SetBoolValue("Display", "ShowEnhancements", display_show_enhancements);
    591     si.SetFloatValue("Display", "OSDScale", display_osd_scale);
    592   }
    593 
    594   si.SetBoolValue("Display", "StretchVertically", display_stretch_vertically);
    595 
    596   si.SetIntValue("CDROM", "ReadaheadSectors", cdrom_readahead_sectors);
    597   si.SetStringValue("CDROM", "MechaconVersion", GetCDROMMechVersionName(cdrom_mechacon_version));
    598   si.SetBoolValue("CDROM", "RegionCheck", cdrom_region_check);
    599   si.SetBoolValue("CDROM", "LoadImageToRAM", cdrom_load_image_to_ram);
    600   si.SetBoolValue("CDROM", "LoadImagePatches", cdrom_load_image_patches);
    601   si.SetBoolValue("CDROM", "MuteCDAudio", cdrom_mute_cd_audio);
    602   si.SetIntValue("CDROM", "ReadSpeedup", cdrom_read_speedup);
    603   si.SetIntValue("CDROM", "SeekSpeedup", cdrom_seek_speedup);
    604 
    605   si.SetStringValue("Audio", "Backend", AudioStream::GetBackendName(audio_backend));
    606   si.SetStringValue("Audio", "Driver", audio_driver.c_str());
    607   si.SetStringValue("Audio", "OutputDevice", audio_output_device.c_str());
    608   audio_stream_parameters.Save(si, "Audio");
    609   si.SetUIntValue("Audio", "OutputVolume", audio_output_volume);
    610   si.SetUIntValue("Audio", "FastForwardVolume", audio_fast_forward_volume);
    611   si.SetBoolValue("Audio", "OutputMuted", audio_output_muted);
    612 
    613   si.SetBoolValue("Hacks", "UseOldMDECRoutines", use_old_mdec_routines);
    614   si.SetBoolValue("Hacks", "ExportSharedMemory", export_shared_memory);
    615 
    616   if (!ignore_base)
    617   {
    618     si.SetIntValue("Hacks", "DMAMaxSliceTicks", dma_max_slice_ticks);
    619     si.SetIntValue("Hacks", "DMAHaltTicks", dma_halt_ticks);
    620     si.SetIntValue("Hacks", "GPUFIFOSize", gpu_fifo_size);
    621     si.SetIntValue("Hacks", "GPUMaxRunAhead", gpu_max_run_ahead);
    622   }
    623 
    624   si.SetBoolValue("PCDrv", "Enabled", pcdrv_enable);
    625   si.SetBoolValue("PCDrv", "EnableWrites", pcdrv_enable_writes);
    626   si.SetStringValue("PCDrv", "Root", pcdrv_root.c_str());
    627 
    628   si.SetBoolValue("BIOS", "TTYLogging", bios_tty_logging);
    629   si.SetBoolValue("BIOS", "PatchFastBoot", bios_patch_fast_boot);
    630 
    631   for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
    632   {
    633     const Controller::ControllerInfo* cinfo = Controller::GetControllerInfo(controller_types[i]);
    634     DebugAssert(cinfo);
    635     si.SetStringValue(Controller::GetSettingsSection(i).c_str(), "Type", cinfo->name);
    636   }
    637 
    638   si.SetStringValue("MemoryCards", "Card1Type", GetMemoryCardTypeName(memory_card_types[0]));
    639   si.SetStringValue("MemoryCards", "Card2Type", GetMemoryCardTypeName(memory_card_types[1]));
    640   if (!memory_card_paths[0].empty())
    641     si.SetStringValue("MemoryCards", "Card1Path", memory_card_paths[0].c_str());
    642   else
    643     si.DeleteValue("MemoryCards", "Card1Path");
    644 
    645   if (!memory_card_paths[1].empty())
    646     si.SetStringValue("MemoryCards", "Card2Path", memory_card_paths[1].c_str());
    647   else
    648     si.DeleteValue("MemoryCards", "Card2Path");
    649 
    650   si.SetBoolValue("MemoryCards", "UsePlaylistTitle", memory_card_use_playlist_title);
    651 
    652   si.SetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(multitap_mode));
    653 
    654   si.SetBoolValue("Cheevos", "Enabled", achievements_enabled);
    655   si.SetBoolValue("Cheevos", "ChallengeMode", achievements_hardcore_mode);
    656   si.SetBoolValue("Cheevos", "Notifications", achievements_notifications);
    657   si.SetBoolValue("Cheevos", "LeaderboardNotifications", achievements_leaderboard_notifications);
    658   si.SetBoolValue("Cheevos", "SoundEffects", achievements_sound_effects);
    659   si.SetBoolValue("Cheevos", "Overlays", achievements_overlays);
    660   si.SetBoolValue("Cheevos", "EncoreMode", achievements_encore_mode);
    661   si.SetBoolValue("Cheevos", "SpectatorMode", achievements_spectator_mode);
    662   si.SetBoolValue("Cheevos", "UnofficialTestMode", achievements_unofficial_test_mode);
    663   si.SetBoolValue("Cheevos", "UseFirstDiscFromPlaylist", achievements_use_first_disc_from_playlist);
    664   si.SetBoolValue("Cheevos", "UseRAIntegration", achievements_use_raintegration);
    665   si.SetIntValue("Cheevos", "NotificationsDuration", achievements_notification_duration);
    666   si.SetIntValue("Cheevos", "LeaderboardsDuration", achievements_leaderboard_duration);
    667 
    668   if (!ignore_base)
    669   {
    670     si.SetStringValue("Logging", "LogLevel", GetLogLevelName(log_level));
    671     si.SetStringValue("Logging", "LogFilter", log_filter.c_str());
    672     si.SetBoolValue("Logging", "LogTimestamps", log_timestamps);
    673     si.SetBoolValue("Logging", "LogToConsole", log_to_console);
    674     si.SetBoolValue("Logging", "LogToDebug", log_to_debug);
    675     si.SetBoolValue("Logging", "LogToWindow", log_to_window);
    676     si.SetBoolValue("Logging", "LogToFile", log_to_file);
    677 
    678     si.SetBoolValue("Debug", "ShowVRAM", debugging.show_vram);
    679     si.SetBoolValue("Debug", "DumpCPUToVRAMCopies", debugging.dump_cpu_to_vram_copies);
    680     si.SetBoolValue("Debug", "DumpVRAMToCPUCopies", debugging.dump_vram_to_cpu_copies);
    681     si.SetBoolValue("Debug", "ShowGPUState", debugging.show_gpu_state);
    682     si.SetBoolValue("Debug", "ShowCDROMState", debugging.show_cdrom_state);
    683     si.SetBoolValue("Debug", "ShowSPUState", debugging.show_spu_state);
    684     si.SetBoolValue("Debug", "ShowTimersState", debugging.show_timers_state);
    685     si.SetBoolValue("Debug", "ShowMDECState", debugging.show_mdec_state);
    686     si.SetBoolValue("Debug", "ShowDMAState", debugging.show_dma_state);
    687   }
    688 
    689   si.SetBoolValue("TextureReplacements", "EnableVRAMWriteReplacements",
    690                   texture_replacements.enable_vram_write_replacements);
    691   si.SetBoolValue("TextureReplacements", "PreloadTextures", texture_replacements.preload_textures);
    692   si.SetBoolValue("TextureReplacements", "DumpVRAMWrites", texture_replacements.dump_vram_writes);
    693   si.SetBoolValue("TextureReplacements", "DumpVRAMWriteForceAlphaChannel",
    694                   texture_replacements.dump_vram_write_force_alpha_channel);
    695   si.SetIntValue("TextureReplacements", "DumpVRAMWriteWidthThreshold",
    696                  texture_replacements.dump_vram_write_width_threshold);
    697   si.SetIntValue("TextureReplacements", "DumpVRAMWriteHeightThreshold",
    698                  texture_replacements.dump_vram_write_height_threshold);
    699 }
    700 
    701 void Settings::Clear(SettingsInterface& si)
    702 {
    703   si.ClearSection("Main");
    704   si.ClearSection("Console");
    705   si.ClearSection("CPU");
    706   si.ClearSection("GPU");
    707   si.ClearSection("Display");
    708   si.ClearSection("CDROM");
    709   si.ClearSection("Audio");
    710   si.ClearSection("Hacks");
    711   si.ClearSection("PCDrv");
    712   si.ClearSection("BIOS");
    713 
    714   for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
    715     si.ClearSection(Controller::GetSettingsSection(i).c_str());
    716 
    717   si.ClearSection("MemoryCards");
    718 
    719   // Can't wipe out this section, because input profiles.
    720   si.DeleteValue("ControllerPorts", "MultitapMode");
    721 
    722   si.ClearSection("Cheevos");
    723   si.ClearSection("Logging");
    724   si.ClearSection("Debug");
    725   si.ClearSection("TextureReplacements");
    726 }
    727 
    728 void Settings::FixIncompatibleSettings(bool display_osd_messages)
    729 {
    730   if (g_settings.disable_all_enhancements)
    731   {
    732     g_settings.cpu_overclock_enable = false;
    733     g_settings.cpu_overclock_active = false;
    734     g_settings.enable_8mb_ram = false;
    735     g_settings.enable_cheats = false;
    736     g_settings.gpu_resolution_scale = 1;
    737     g_settings.gpu_multisamples = 1;
    738     g_settings.gpu_per_sample_shading = false;
    739     g_settings.gpu_true_color = false;
    740     g_settings.gpu_debanding = false;
    741     g_settings.gpu_scaled_dithering = false;
    742     g_settings.gpu_force_round_texcoords = false;
    743     g_settings.gpu_texture_filter = GPUTextureFilter::Nearest;
    744     g_settings.gpu_sprite_texture_filter = GPUTextureFilter::Nearest;
    745     g_settings.gpu_line_detect_mode = GPULineDetectMode::Disabled;
    746     g_settings.gpu_disable_interlacing = false;
    747     g_settings.gpu_force_ntsc_timings = false;
    748     g_settings.gpu_widescreen_hack = false;
    749     g_settings.gpu_pgxp_enable = false;
    750     g_settings.display_24bit_chroma_smoothing = false;
    751     g_settings.cdrom_read_speedup = 1;
    752     g_settings.cdrom_seek_speedup = 1;
    753     g_settings.cdrom_mute_cd_audio = false;
    754     g_settings.texture_replacements.enable_vram_write_replacements = false;
    755     g_settings.use_old_mdec_routines = false;
    756     g_settings.pcdrv_enable = false;
    757     g_settings.bios_patch_fast_boot = false;
    758   }
    759 
    760   if (g_settings.pcdrv_enable && g_settings.pcdrv_root.empty())
    761   {
    762     Host::AddKeyedOSDMessage("pcdrv_disabled_no_root",
    763                              TRANSLATE_STR("OSDMessage", "Disabling PCDrv because no root directory is specified."),
    764                              Host::OSD_WARNING_DURATION);
    765     g_settings.pcdrv_enable = false;
    766   }
    767 
    768   if (g_settings.gpu_pgxp_enable)
    769   {
    770     if (g_settings.gpu_renderer == GPURenderer::Software)
    771     {
    772       if (display_osd_messages)
    773       {
    774         Host::AddKeyedOSDMessage(
    775           "pgxp_disabled_sw",
    776           TRANSLATE_STR("OSDMessage", "PGXP is incompatible with the software renderer, disabling PGXP."), 10.0f);
    777       }
    778       g_settings.gpu_pgxp_enable = false;
    779     }
    780   }
    781   else
    782   {
    783     g_settings.gpu_pgxp_culling = false;
    784     g_settings.gpu_pgxp_texture_correction = false;
    785     g_settings.gpu_pgxp_color_correction = false;
    786     g_settings.gpu_pgxp_vertex_cache = false;
    787     g_settings.gpu_pgxp_cpu = false;
    788     g_settings.gpu_pgxp_preserve_proj_fp = false;
    789     g_settings.gpu_pgxp_depth_buffer = false;
    790     g_settings.gpu_pgxp_disable_2d = false;
    791   }
    792 
    793 #ifndef ENABLE_MMAP_FASTMEM
    794   if (g_settings.cpu_fastmem_mode == CPUFastmemMode::MMap)
    795   {
    796     WARNING_LOG("mmap fastmem is not available on this platform, using LUT instead.");
    797     g_settings.cpu_fastmem_mode = CPUFastmemMode::LUT;
    798   }
    799 #endif
    800 
    801 #if defined(__ANDROID__) && defined(__arm__) && !defined(__aarch64__) && !defined(_M_ARM64)
    802   if (g_settings.rewind_enable)
    803   {
    804     Host::AddKeyedOSDMessage("rewind_disabled_android",
    805                              TRANSLATE_STR("OSDMessage", "Rewind is not supported on 32-bit ARM for Android."), 30.0f);
    806     g_settings.rewind_enable = false;
    807   }
    808   if (g_settings.IsRunaheadEnabled())
    809   {
    810     Host::AddKeyedOSDMessage("rewind_disabled_android",
    811                              TRANSLATE_STR("OSDMessage", "Runahead is not supported on 32-bit ARM for Android."),
    812                              30.0f);
    813     g_settings.runahead_frames = 0;
    814   }
    815 #endif
    816 
    817   if (g_settings.IsRunaheadEnabled() && g_settings.rewind_enable)
    818   {
    819     Host::AddKeyedOSDMessage("rewind_disabled",
    820                              TRANSLATE_STR("OSDMessage", "Rewind is disabled because runahead is enabled."),
    821                              Host::OSD_WARNING_DURATION);
    822     g_settings.rewind_enable = false;
    823   }
    824 
    825   if (g_settings.IsRunaheadEnabled())
    826   {
    827     // Block linking is good for performance, but hurts when regularly loading (i.e. runahead), since everything has to
    828     // be unlinked. Which would be thousands of blocks.
    829     if (g_settings.cpu_recompiler_block_linking)
    830     {
    831       WARNING_LOG("Disabling block linking due to runahead.");
    832       g_settings.cpu_recompiler_block_linking = false;
    833     }
    834   }
    835 
    836   // if challenge mode is enabled, disable things like rewind since they use save states
    837   if (Achievements::IsHardcoreModeActive())
    838   {
    839     g_settings.emulation_speed =
    840       (g_settings.emulation_speed != 0.0f) ? std::max(g_settings.emulation_speed, 1.0f) : 0.0f;
    841     g_settings.fast_forward_speed =
    842       (g_settings.fast_forward_speed != 0.0f) ? std::max(g_settings.fast_forward_speed, 1.0f) : 0.0f;
    843     g_settings.turbo_speed = (g_settings.turbo_speed != 0.0f) ? std::max(g_settings.turbo_speed, 1.0f) : 0.0f;
    844     g_settings.rewind_enable = false;
    845     g_settings.enable_cheats = false;
    846     if (g_settings.cpu_overclock_enable && g_settings.GetCPUOverclockPercent() < 100)
    847     {
    848       g_settings.cpu_overclock_enable = false;
    849       g_settings.UpdateOverclockActive();
    850     }
    851     g_settings.debugging.enable_gdb_server = false;
    852     g_settings.debugging.show_vram = false;
    853     g_settings.debugging.show_gpu_state = false;
    854     g_settings.debugging.show_cdrom_state = false;
    855     g_settings.debugging.show_spu_state = false;
    856     g_settings.debugging.show_timers_state = false;
    857     g_settings.debugging.show_mdec_state = false;
    858     g_settings.debugging.show_dma_state = false;
    859     g_settings.debugging.dump_cpu_to_vram_copies = false;
    860     g_settings.debugging.dump_vram_to_cpu_copies = false;
    861   }
    862 }
    863 
    864 void Settings::UpdateLogSettings()
    865 {
    866   Log::SetLogLevel(log_level);
    867   Log::SetLogFilter(log_filter);
    868   Log::SetConsoleOutputParams(log_to_console, log_timestamps);
    869   Log::SetDebugOutputParams(log_to_debug);
    870 
    871   if (log_to_file)
    872   {
    873     Log::SetFileOutputParams(log_to_file, Path::Combine(EmuFolders::DataRoot, "duckstation.log").c_str(),
    874                              log_timestamps);
    875   }
    876   else
    877   {
    878     Log::SetFileOutputParams(false, nullptr);
    879   }
    880 }
    881 
    882 void Settings::SetDefaultControllerConfig(SettingsInterface& si)
    883 {
    884   // Global Settings
    885   si.SetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(Settings::DEFAULT_MULTITAP_MODE));
    886   si.SetFloatValue("ControllerPorts", "PointerXScale", 8.0f);
    887   si.SetFloatValue("ControllerPorts", "PointerYScale", 8.0f);
    888   si.SetBoolValue("ControllerPorts", "PointerXInvert", false);
    889   si.SetBoolValue("ControllerPorts", "PointerYInvert", false);
    890 
    891   // Default pad types and parameters.
    892   for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
    893   {
    894     const std::string section(Controller::GetSettingsSection(i));
    895     si.ClearSection(section.c_str());
    896     si.SetStringValue(section.c_str(), "Type", Controller::GetDefaultPadType(i));
    897   }
    898 
    899 #ifndef __ANDROID__
    900   // Use the automapper to set this up.
    901   InputManager::MapController(si, 0, InputManager::GetGenericBindingMapping("Keyboard"));
    902 #endif
    903 }
    904 
    905 static constexpr const std::array s_log_level_names = {
    906   "None", "Error", "Warning", "Info", "Verbose", "Dev", "Debug", "Trace",
    907 };
    908 static constexpr const std::array s_log_level_display_names = {
    909   TRANSLATE_NOOP("LogLevel", "None"),    TRANSLATE_NOOP("LogLevel", "Error"),
    910   TRANSLATE_NOOP("LogLevel", "Warning"), TRANSLATE_NOOP("LogLevel", "Information"),
    911   TRANSLATE_NOOP("LogLevel", "Verbose"), TRANSLATE_NOOP("LogLevel", "Developer"),
    912   TRANSLATE_NOOP("LogLevel", "Debug"),   TRANSLATE_NOOP("LogLevel", "Trace"),
    913 };
    914 
    915 std::optional<LOGLEVEL> Settings::ParseLogLevelName(const char* str)
    916 {
    917   int index = 0;
    918   for (const char* name : s_log_level_names)
    919   {
    920     if (StringUtil::Strcasecmp(name, str) == 0)
    921       return static_cast<LOGLEVEL>(index);
    922 
    923     index++;
    924   }
    925 
    926   return std::nullopt;
    927 }
    928 
    929 const char* Settings::GetLogLevelName(LOGLEVEL level)
    930 {
    931   return s_log_level_names[static_cast<int>(level)];
    932 }
    933 
    934 const char* Settings::GetLogLevelDisplayName(LOGLEVEL level)
    935 {
    936   return Host::TranslateToCString("LogLevel", s_log_level_display_names[static_cast<int>(level)]);
    937 }
    938 
    939 static constexpr const std::array s_console_region_names = {"Auto", "NTSC-J", "NTSC-U", "PAL"};
    940 static constexpr const std::array s_console_region_display_names = {
    941   TRANSLATE_NOOP("ConsoleRegion", "Auto-Detect"), TRANSLATE_NOOP("ConsoleRegion", "NTSC-J (Japan)"),
    942   TRANSLATE_NOOP("ConsoleRegion", "NTSC-U/C (US, Canada)"), TRANSLATE_NOOP("ConsoleRegion", "PAL (Europe, Australia)")};
    943 
    944 std::optional<ConsoleRegion> Settings::ParseConsoleRegionName(const char* str)
    945 {
    946   int index = 0;
    947   for (const char* name : s_console_region_names)
    948   {
    949     if (StringUtil::Strcasecmp(name, str) == 0)
    950       return static_cast<ConsoleRegion>(index);
    951 
    952     index++;
    953   }
    954 
    955   return std::nullopt;
    956 }
    957 
    958 const char* Settings::GetConsoleRegionName(ConsoleRegion region)
    959 {
    960   return s_console_region_names[static_cast<int>(region)];
    961 }
    962 
    963 const char* Settings::GetConsoleRegionDisplayName(ConsoleRegion region)
    964 {
    965   return Host::TranslateToCString("ConsoleRegion", s_console_region_display_names[static_cast<int>(region)]);
    966 }
    967 
    968 static constexpr const std::array s_disc_region_names = {"NTSC-J", "NTSC-U", "PAL", "Other", "Non-PS1"};
    969 static constexpr const std::array s_disc_region_display_names = {
    970   TRANSLATE_NOOP("DiscRegion", "NTSC-J (Japan)"), TRANSLATE_NOOP("DiscRegion", "NTSC-U/C (US, Canada)"),
    971   TRANSLATE_NOOP("DiscRegion", "PAL (Europe, Australia)"), TRANSLATE_NOOP("DiscRegion", "Other"),
    972   TRANSLATE_NOOP("DiscRegion", "Non-PS1")};
    973 
    974 std::optional<DiscRegion> Settings::ParseDiscRegionName(const char* str)
    975 {
    976   int index = 0;
    977   for (const char* name : s_disc_region_names)
    978   {
    979     if (StringUtil::Strcasecmp(name, str) == 0)
    980       return static_cast<DiscRegion>(index);
    981 
    982     index++;
    983   }
    984 
    985   return std::nullopt;
    986 }
    987 
    988 const char* Settings::GetDiscRegionName(DiscRegion region)
    989 {
    990   return s_disc_region_names[static_cast<int>(region)];
    991 }
    992 
    993 const char* Settings::GetDiscRegionDisplayName(DiscRegion region)
    994 {
    995   return Host::TranslateToCString("DiscRegion", s_disc_region_display_names[static_cast<int>(region)]);
    996 }
    997 
    998 static constexpr const std::array s_cpu_execution_mode_names = {"Interpreter", "CachedInterpreter", "Recompiler",
    999                                                                 "NewRec"};
   1000 static constexpr const std::array s_cpu_execution_mode_display_names = {
   1001   TRANSLATE_NOOP("CPUExecutionMode", "Interpreter (Slowest)"),
   1002   TRANSLATE_NOOP("CPUExecutionMode", "Cached Interpreter (Faster)"),
   1003   TRANSLATE_NOOP("CPUExecutionMode", "Recompiler (Fastest)"),
   1004   TRANSLATE_NOOP("CPUExecutionMode", "New Recompiler (Experimental)")};
   1005 
   1006 std::optional<CPUExecutionMode> Settings::ParseCPUExecutionMode(const char* str)
   1007 {
   1008   u8 index = 0;
   1009   for (const char* name : s_cpu_execution_mode_names)
   1010   {
   1011     if (StringUtil::Strcasecmp(name, str) == 0)
   1012       return static_cast<CPUExecutionMode>(index);
   1013 
   1014     index++;
   1015   }
   1016 
   1017   return std::nullopt;
   1018 }
   1019 
   1020 const char* Settings::GetCPUExecutionModeName(CPUExecutionMode mode)
   1021 {
   1022   return s_cpu_execution_mode_names[static_cast<u8>(mode)];
   1023 }
   1024 
   1025 const char* Settings::GetCPUExecutionModeDisplayName(CPUExecutionMode mode)
   1026 {
   1027   return Host::TranslateToCString("CPUExecutionMode", s_cpu_execution_mode_display_names[static_cast<u8>(mode)]);
   1028 }
   1029 
   1030 static constexpr const std::array s_cpu_fastmem_mode_names = {"Disabled", "MMap", "LUT"};
   1031 static constexpr const std::array s_cpu_fastmem_mode_display_names = {
   1032   TRANSLATE_NOOP("CPUFastmemMode", "Disabled (Slowest)"),
   1033   TRANSLATE_NOOP("CPUFastmemMode", "MMap (Hardware, Fastest, 64-Bit Only)"),
   1034   TRANSLATE_NOOP("CPUFastmemMode", "LUT (Faster)")};
   1035 
   1036 std::optional<CPUFastmemMode> Settings::ParseCPUFastmemMode(const char* str)
   1037 {
   1038   u8 index = 0;
   1039   for (const char* name : s_cpu_fastmem_mode_names)
   1040   {
   1041     if (StringUtil::Strcasecmp(name, str) == 0)
   1042       return static_cast<CPUFastmemMode>(index);
   1043 
   1044     index++;
   1045   }
   1046 
   1047   return std::nullopt;
   1048 }
   1049 
   1050 const char* Settings::GetCPUFastmemModeName(CPUFastmemMode mode)
   1051 {
   1052   return s_cpu_fastmem_mode_names[static_cast<u8>(mode)];
   1053 }
   1054 
   1055 const char* Settings::GetCPUFastmemModeDisplayName(CPUFastmemMode mode)
   1056 {
   1057   return Host::TranslateToCString("CPUFastmemMode", s_cpu_fastmem_mode_display_names[static_cast<u8>(mode)]);
   1058 }
   1059 
   1060 static constexpr const std::array s_gpu_renderer_names = {
   1061   "Automatic",
   1062 #ifdef _WIN32
   1063   "D3D11",     "D3D12",
   1064 #endif
   1065 #ifdef __APPLE__
   1066   "Metal",
   1067 #endif
   1068 #ifdef ENABLE_VULKAN
   1069   "Vulkan",
   1070 #endif
   1071 #ifdef ENABLE_OPENGL
   1072   "OpenGL",
   1073 #endif
   1074   "Software",
   1075 };
   1076 static constexpr const std::array s_gpu_renderer_display_names = {
   1077   TRANSLATE_NOOP("GPURenderer", "Automatic"),
   1078 #ifdef _WIN32
   1079   TRANSLATE_NOOP("GPURenderer", "Direct3D 11"), TRANSLATE_NOOP("GPURenderer", "Direct3D 12"),
   1080 #endif
   1081 #ifdef __APPLE__
   1082   TRANSLATE_NOOP("GPURenderer", "Metal"),
   1083 #endif
   1084 #ifdef ENABLE_VULKAN
   1085   TRANSLATE_NOOP("GPURenderer", "Vulkan"),
   1086 #endif
   1087 #ifdef ENABLE_OPENGL
   1088   TRANSLATE_NOOP("GPURenderer", "OpenGL"),
   1089 #endif
   1090   TRANSLATE_NOOP("GPURenderer", "Software"),
   1091 };
   1092 
   1093 std::optional<GPURenderer> Settings::ParseRendererName(const char* str)
   1094 {
   1095   int index = 0;
   1096   for (const char* name : s_gpu_renderer_names)
   1097   {
   1098     if (StringUtil::Strcasecmp(name, str) == 0)
   1099       return static_cast<GPURenderer>(index);
   1100 
   1101     index++;
   1102   }
   1103 
   1104   return std::nullopt;
   1105 }
   1106 
   1107 const char* Settings::GetRendererName(GPURenderer renderer)
   1108 {
   1109   return s_gpu_renderer_names[static_cast<int>(renderer)];
   1110 }
   1111 
   1112 const char* Settings::GetRendererDisplayName(GPURenderer renderer)
   1113 {
   1114   return Host::TranslateToCString("GPURenderer", s_gpu_renderer_display_names[static_cast<int>(renderer)]);
   1115 }
   1116 
   1117 RenderAPI Settings::GetRenderAPIForRenderer(GPURenderer renderer)
   1118 {
   1119   switch (renderer)
   1120   {
   1121 #ifdef _WIN32
   1122     case GPURenderer::HardwareD3D11:
   1123       return RenderAPI::D3D11;
   1124     case GPURenderer::HardwareD3D12:
   1125       return RenderAPI::D3D12;
   1126 #endif
   1127 #ifdef __APPLE__
   1128       return RenderAPI::Metal;
   1129 #endif
   1130 #ifdef ENABLE_VULKAN
   1131     case GPURenderer::HardwareVulkan:
   1132       return RenderAPI::Vulkan;
   1133 #endif
   1134 #ifdef ENABLE_OPENGL
   1135     case GPURenderer::HardwareOpenGL:
   1136       return RenderAPI::OpenGL;
   1137 #endif
   1138     case GPURenderer::Software:
   1139     case GPURenderer::Automatic:
   1140     default:
   1141       return GPUDevice::GetPreferredAPI();
   1142   }
   1143 }
   1144 
   1145 GPURenderer Settings::GetRendererForRenderAPI(RenderAPI api)
   1146 {
   1147   switch (api)
   1148   {
   1149 #ifdef _WIN32
   1150     case RenderAPI::D3D11:
   1151       return GPURenderer::HardwareD3D11;
   1152 
   1153     case RenderAPI::D3D12:
   1154       return GPURenderer::HardwareD3D12;
   1155 #endif
   1156 
   1157 #ifdef __APPLE__
   1158     case RenderAPI::Metal:
   1159       return GPURenderer::HardwareMetal;
   1160 #endif
   1161 
   1162 #ifdef ENABLE_VULKAN
   1163     case RenderAPI::Vulkan:
   1164       return GPURenderer::HardwareVulkan;
   1165 #endif
   1166 
   1167 #ifdef ENABLE_OPENGL
   1168     case RenderAPI::OpenGL:
   1169     case RenderAPI::OpenGLES:
   1170       return GPURenderer::HardwareOpenGL;
   1171 #endif
   1172 
   1173     default:
   1174       return GPURenderer::Automatic;
   1175   }
   1176 }
   1177 
   1178 GPURenderer Settings::GetAutomaticRenderer()
   1179 {
   1180   return GetRendererForRenderAPI(GPUDevice::GetPreferredAPI());
   1181 }
   1182 
   1183 static constexpr const std::array s_texture_filter_names = {
   1184   "Nearest", "Bilinear", "BilinearBinAlpha", "JINC2", "JINC2BinAlpha", "xBR", "xBRBinAlpha",
   1185 };
   1186 static constexpr const std::array s_texture_filter_display_names = {
   1187   TRANSLATE_NOOP("GPUTextureFilter", "Nearest-Neighbor"),
   1188   TRANSLATE_NOOP("GPUTextureFilter", "Bilinear"),
   1189   TRANSLATE_NOOP("GPUTextureFilter", "Bilinear (No Edge Blending)"),
   1190   TRANSLATE_NOOP("GPUTextureFilter", "JINC2 (Slow)"),
   1191   TRANSLATE_NOOP("GPUTextureFilter", "JINC2 (Slow, No Edge Blending)"),
   1192   TRANSLATE_NOOP("GPUTextureFilter", "xBR (Very Slow)"),
   1193   TRANSLATE_NOOP("GPUTextureFilter", "xBR (Very Slow, No Edge Blending)"),
   1194 };
   1195 
   1196 std::optional<GPUTextureFilter> Settings::ParseTextureFilterName(const char* str)
   1197 {
   1198   int index = 0;
   1199   for (const char* name : s_texture_filter_names)
   1200   {
   1201     if (StringUtil::Strcasecmp(name, str) == 0)
   1202       return static_cast<GPUTextureFilter>(index);
   1203 
   1204     index++;
   1205   }
   1206 
   1207   return std::nullopt;
   1208 }
   1209 
   1210 const char* Settings::GetTextureFilterName(GPUTextureFilter filter)
   1211 {
   1212   return s_texture_filter_names[static_cast<size_t>(filter)];
   1213 }
   1214 
   1215 const char* Settings::GetTextureFilterDisplayName(GPUTextureFilter filter)
   1216 {
   1217   return Host::TranslateToCString("GPUTextureFilter", s_texture_filter_display_names[static_cast<int>(filter)]);
   1218 }
   1219 
   1220 static constexpr const std::array s_line_detect_mode_names = {
   1221   "Disabled",
   1222   "Quads",
   1223   "BasicTriangles",
   1224   "AggressiveTriangles",
   1225 };
   1226 static constexpr const std::array s_line_detect_mode_detect_names = {
   1227   TRANSLATE_NOOP("GPULineDetectMode", "Disabled"),
   1228   TRANSLATE_NOOP("GPULineDetectMode", "Quads"),
   1229   TRANSLATE_NOOP("GPULineDetectMode", "Triangles (Basic)"),
   1230   TRANSLATE_NOOP("GPULineDetectMode", "Triangles (Aggressive)"),
   1231 };
   1232 
   1233 std::optional<GPULineDetectMode> Settings::ParseLineDetectModeName(const char* str)
   1234 {
   1235   int index = 0;
   1236   for (const char* name : s_line_detect_mode_names)
   1237   {
   1238     if (StringUtil::Strcasecmp(name, str) == 0)
   1239       return static_cast<GPULineDetectMode>(index);
   1240 
   1241     index++;
   1242   }
   1243 
   1244   return std::nullopt;
   1245 }
   1246 
   1247 const char* Settings::GetLineDetectModeName(GPULineDetectMode mode)
   1248 {
   1249   return s_line_detect_mode_names[static_cast<size_t>(mode)];
   1250 }
   1251 
   1252 const char* Settings::GetLineDetectModeDisplayName(GPULineDetectMode mode)
   1253 {
   1254   return Host::TranslateToCString("GPULineDetectMode", s_line_detect_mode_detect_names[static_cast<size_t>(mode)]);
   1255 }
   1256 
   1257 static constexpr const std::array s_downsample_mode_names = {"Disabled", "Box", "Adaptive"};
   1258 static constexpr const std::array s_downsample_mode_display_names = {
   1259   TRANSLATE_NOOP("GPUDownsampleMode", "Disabled"),
   1260   TRANSLATE_NOOP("GPUDownsampleMode", "Box (Downsample 3D/Smooth All)"),
   1261   TRANSLATE_NOOP("GPUDownsampleMode", "Adaptive (Preserve 3D/Smooth 2D)")};
   1262 
   1263 std::optional<GPUDownsampleMode> Settings::ParseDownsampleModeName(const char* str)
   1264 {
   1265   int index = 0;
   1266   for (const char* name : s_downsample_mode_names)
   1267   {
   1268     if (StringUtil::Strcasecmp(name, str) == 0)
   1269       return static_cast<GPUDownsampleMode>(index);
   1270 
   1271     index++;
   1272   }
   1273 
   1274   return std::nullopt;
   1275 }
   1276 
   1277 const char* Settings::GetDownsampleModeName(GPUDownsampleMode mode)
   1278 {
   1279   return s_downsample_mode_names[static_cast<int>(mode)];
   1280 }
   1281 
   1282 const char* Settings::GetDownsampleModeDisplayName(GPUDownsampleMode mode)
   1283 {
   1284   return Host::TranslateToCString("GPUDownsampleMode", s_downsample_mode_display_names[static_cast<int>(mode)]);
   1285 }
   1286 
   1287 static constexpr const std::array s_wireframe_mode_names = {"Disabled", "OverlayWireframe", "OnlyWireframe"};
   1288 static constexpr const std::array s_wireframe_mode_display_names = {
   1289   TRANSLATE_NOOP("GPUWireframeMode", "Disabled"), TRANSLATE_NOOP("GPUWireframeMode", "Overlay Wireframe"),
   1290   TRANSLATE_NOOP("GPUWireframeMode", "Only Wireframe")};
   1291 
   1292 std::optional<GPUWireframeMode> Settings::ParseGPUWireframeMode(const char* str)
   1293 {
   1294   int index = 0;
   1295   for (const char* name : s_wireframe_mode_names)
   1296   {
   1297     if (StringUtil::Strcasecmp(name, str) == 0)
   1298       return static_cast<GPUWireframeMode>(index);
   1299 
   1300     index++;
   1301   }
   1302 
   1303   return std::nullopt;
   1304 }
   1305 
   1306 const char* Settings::GetGPUWireframeModeName(GPUWireframeMode mode)
   1307 {
   1308   return s_wireframe_mode_names[static_cast<int>(mode)];
   1309 }
   1310 
   1311 const char* Settings::GetGPUWireframeModeDisplayName(GPUWireframeMode mode)
   1312 {
   1313   return Host::TranslateToCString("GPUWireframeMode", s_wireframe_mode_display_names[static_cast<int>(mode)]);
   1314 }
   1315 
   1316 static constexpr const std::array s_display_deinterlacing_mode_names = {
   1317   "Disabled",
   1318   "Weave",
   1319   "Blend",
   1320   "Adaptive",
   1321 };
   1322 static constexpr const std::array s_display_deinterlacing_mode_display_names = {
   1323   TRANSLATE_NOOP("DisplayDeinterlacingMode", "Disabled (Flickering)"),
   1324   TRANSLATE_NOOP("DisplayDeinterlacingMode", "Weave (Combing)"),
   1325   TRANSLATE_NOOP("DisplayDeinterlacingMode", "Blend (Blur)"),
   1326   TRANSLATE_NOOP("DisplayDeinterlacingMode", "Adaptive (FastMAD)"),
   1327 };
   1328 
   1329 std::optional<DisplayDeinterlacingMode> Settings::ParseDisplayDeinterlacingMode(const char* str)
   1330 {
   1331   int index = 0;
   1332   for (const char* name : s_display_deinterlacing_mode_names)
   1333   {
   1334     if (StringUtil::Strcasecmp(name, str) == 0)
   1335       return static_cast<DisplayDeinterlacingMode>(index);
   1336 
   1337     index++;
   1338   }
   1339 
   1340   return std::nullopt;
   1341 }
   1342 
   1343 const char* Settings::GetDisplayDeinterlacingModeName(DisplayDeinterlacingMode mode)
   1344 {
   1345   return s_display_deinterlacing_mode_names[static_cast<int>(mode)];
   1346 }
   1347 
   1348 const char* Settings::GetDisplayDeinterlacingModeDisplayName(DisplayDeinterlacingMode mode)
   1349 {
   1350   return Host::TranslateToCString("DisplayDeinterlacingMode",
   1351                                   s_display_deinterlacing_mode_display_names[static_cast<int>(mode)]);
   1352 }
   1353 
   1354 static constexpr const std::array s_display_crop_mode_names = {"None", "Overscan", "Borders"};
   1355 static constexpr const std::array s_display_crop_mode_display_names = {
   1356   TRANSLATE_NOOP("DisplayCropMode", "None"), TRANSLATE_NOOP("DisplayCropMode", "Only Overscan Area"),
   1357   TRANSLATE_NOOP("DisplayCropMode", "All Borders")};
   1358 
   1359 std::optional<DisplayCropMode> Settings::ParseDisplayCropMode(const char* str)
   1360 {
   1361   int index = 0;
   1362   for (const char* name : s_display_crop_mode_names)
   1363   {
   1364     if (StringUtil::Strcasecmp(name, str) == 0)
   1365       return static_cast<DisplayCropMode>(index);
   1366 
   1367     index++;
   1368   }
   1369 
   1370   return std::nullopt;
   1371 }
   1372 
   1373 const char* Settings::GetDisplayCropModeName(DisplayCropMode crop_mode)
   1374 {
   1375   return s_display_crop_mode_names[static_cast<int>(crop_mode)];
   1376 }
   1377 
   1378 const char* Settings::GetDisplayCropModeDisplayName(DisplayCropMode crop_mode)
   1379 {
   1380   return Host::TranslateToCString("DisplayCropMode", s_display_crop_mode_display_names[static_cast<int>(crop_mode)]);
   1381 }
   1382 
   1383 static constexpr const std::array s_display_aspect_ratio_names = {
   1384 #ifndef __ANDROID__
   1385   TRANSLATE_NOOP("DisplayAspectRatio", "Auto (Game Native)"),
   1386   TRANSLATE_NOOP("DisplayAspectRatio", "Stretch To Fill"),
   1387   TRANSLATE_NOOP("DisplayAspectRatio", "Custom"),
   1388 #else
   1389   "Auto (Game Native)",
   1390   "Auto (Match Window)",
   1391   "Custom",
   1392 #endif
   1393   "4:3",
   1394   "16:9",
   1395   "19:9",
   1396   "20:9",
   1397   "PAR 1:1"};
   1398 static constexpr const std::array s_display_aspect_ratio_values = {
   1399   -1.0f, -1.0f, -1.0f, 4.0f / 3.0f, 16.0f / 9.0f, 19.0f / 9.0f, 20.0f / 9.0f, -1.0f};
   1400 
   1401 std::optional<DisplayAspectRatio> Settings::ParseDisplayAspectRatio(const char* str)
   1402 {
   1403   int index = 0;
   1404   for (const char* name : s_display_aspect_ratio_names)
   1405   {
   1406     if (StringUtil::Strcasecmp(name, str) == 0)
   1407       return static_cast<DisplayAspectRatio>(index);
   1408 
   1409     index++;
   1410   }
   1411 
   1412   return std::nullopt;
   1413 }
   1414 
   1415 const char* Settings::GetDisplayAspectRatioName(DisplayAspectRatio ar)
   1416 {
   1417   return s_display_aspect_ratio_names[static_cast<int>(ar)];
   1418 }
   1419 
   1420 const char* Settings::GetDisplayAspectRatioDisplayName(DisplayAspectRatio ar)
   1421 {
   1422   return Host::TranslateToCString("DisplayAspectRatio", s_display_aspect_ratio_names[static_cast<int>(ar)]);
   1423 }
   1424 
   1425 float Settings::GetDisplayAspectRatioValue() const
   1426 {
   1427   switch (display_aspect_ratio)
   1428   {
   1429     case DisplayAspectRatio::MatchWindow:
   1430     {
   1431       if (!g_gpu_device)
   1432         return s_display_aspect_ratio_values[static_cast<int>(DEFAULT_DISPLAY_ASPECT_RATIO)];
   1433 
   1434       return static_cast<float>(g_gpu_device->GetWindowWidth()) / static_cast<float>(g_gpu_device->GetWindowHeight());
   1435     }
   1436 
   1437     case DisplayAspectRatio::Custom:
   1438     {
   1439       return static_cast<float>(display_aspect_ratio_custom_numerator) /
   1440              static_cast<float>(display_aspect_ratio_custom_denominator);
   1441     }
   1442 
   1443     default:
   1444     {
   1445       return s_display_aspect_ratio_values[static_cast<int>(display_aspect_ratio)];
   1446     }
   1447   }
   1448 }
   1449 
   1450 static constexpr const std::array s_display_alignment_names = {"LeftOrTop", "Center", "RightOrBottom"};
   1451 static constexpr const std::array s_display_alignment_display_names = {
   1452   TRANSLATE_NOOP("DisplayAlignment", "Left / Top"), TRANSLATE_NOOP("DisplayAlignment", "Center"),
   1453   TRANSLATE_NOOP("DisplayAlignment", "Right / Bottom")};
   1454 
   1455 std::optional<DisplayAlignment> Settings::ParseDisplayAlignment(const char* str)
   1456 {
   1457   int index = 0;
   1458   for (const char* name : s_display_alignment_names)
   1459   {
   1460     if (StringUtil::Strcasecmp(name, str) == 0)
   1461       return static_cast<DisplayAlignment>(index);
   1462 
   1463     index++;
   1464   }
   1465 
   1466   return std::nullopt;
   1467 }
   1468 
   1469 const char* Settings::GetDisplayAlignmentName(DisplayAlignment alignment)
   1470 {
   1471   return s_display_alignment_names[static_cast<int>(alignment)];
   1472 }
   1473 
   1474 const char* Settings::GetDisplayAlignmentDisplayName(DisplayAlignment alignment)
   1475 {
   1476   return Host::TranslateToCString("DisplayAlignment", s_display_alignment_display_names[static_cast<int>(alignment)]);
   1477 }
   1478 
   1479 static constexpr const std::array s_display_rotation_names = {"Normal", "Rotate90", "Rotate180", "Rotate270"};
   1480 static constexpr const std::array s_display_rotation_display_names = {
   1481   TRANSLATE_NOOP("Settings", "No Rotation"),
   1482   TRANSLATE_NOOP("Settings", "Rotate 90° (Clockwise)"),
   1483   TRANSLATE_NOOP("Settings", "Rotate 180° (Vertical Flip)"),
   1484   TRANSLATE_NOOP("Settings", "Rotate 270° (Clockwise)"),
   1485 };
   1486 
   1487 std::optional<DisplayRotation> Settings::ParseDisplayRotation(const char* str)
   1488 {
   1489   int index = 0;
   1490   for (const char* name : s_display_rotation_names)
   1491   {
   1492     if (StringUtil::Strcasecmp(name, str) == 0)
   1493       return static_cast<DisplayRotation>(index);
   1494 
   1495     index++;
   1496   }
   1497 
   1498   return std::nullopt;
   1499 }
   1500 
   1501 const char* Settings::GetDisplayRotationName(DisplayRotation rotation)
   1502 {
   1503   return s_display_rotation_names[static_cast<int>(rotation)];
   1504 }
   1505 
   1506 const char* Settings::GetDisplayRotationDisplayName(DisplayRotation rotation)
   1507 {
   1508   return Host::TranslateToCString("Settings", s_display_rotation_display_names[static_cast<size_t>(rotation)]);
   1509 }
   1510 
   1511 static constexpr const std::array s_display_scaling_names = {
   1512   "Nearest", "NearestInteger", "BilinearSmooth", "BilinearSharp", "BilinearInteger",
   1513 };
   1514 static constexpr const std::array s_display_scaling_display_names = {
   1515   TRANSLATE_NOOP("DisplayScalingMode", "Nearest-Neighbor"),
   1516   TRANSLATE_NOOP("DisplayScalingMode", "Nearest-Neighbor (Integer)"),
   1517   TRANSLATE_NOOP("DisplayScalingMode", "Bilinear (Smooth)"),
   1518   TRANSLATE_NOOP("DisplayScalingMode", "Bilinear (Sharp)"),
   1519   TRANSLATE_NOOP("DisplayScalingMode", "Bilinear (Integer)"),
   1520 };
   1521 
   1522 std::optional<DisplayScalingMode> Settings::ParseDisplayScaling(const char* str)
   1523 {
   1524   int index = 0;
   1525   for (const char* name : s_display_scaling_names)
   1526   {
   1527     if (StringUtil::Strcasecmp(name, str) == 0)
   1528       return static_cast<DisplayScalingMode>(index);
   1529 
   1530     index++;
   1531   }
   1532 
   1533   return std::nullopt;
   1534 }
   1535 
   1536 const char* Settings::GetDisplayScalingName(DisplayScalingMode mode)
   1537 {
   1538   return s_display_scaling_names[static_cast<int>(mode)];
   1539 }
   1540 
   1541 const char* Settings::GetDisplayScalingDisplayName(DisplayScalingMode mode)
   1542 {
   1543   return Host::TranslateToCString("DisplayScalingMode", s_display_scaling_display_names[static_cast<int>(mode)]);
   1544 }
   1545 
   1546 static constexpr const std::array s_display_exclusive_fullscreen_mode_names = {
   1547   "Automatic",
   1548   "Disallowed",
   1549   "Allowed",
   1550 };
   1551 static constexpr const std::array s_display_exclusive_fullscreen_mode_display_names = {
   1552   TRANSLATE_NOOP("Settings", "Automatic"),
   1553   TRANSLATE_NOOP("Settings", "Disallowed"),
   1554   TRANSLATE_NOOP("Settings", "Allowed"),
   1555 };
   1556 
   1557 std::optional<DisplayExclusiveFullscreenControl> Settings::ParseDisplayExclusiveFullscreenControl(const char* str)
   1558 {
   1559   int index = 0;
   1560   for (const char* name : s_display_exclusive_fullscreen_mode_names)
   1561   {
   1562     if (StringUtil::Strcasecmp(name, str) == 0)
   1563       return static_cast<DisplayExclusiveFullscreenControl>(index);
   1564 
   1565     index++;
   1566   }
   1567 
   1568   return std::nullopt;
   1569 }
   1570 
   1571 const char* Settings::GetDisplayExclusiveFullscreenControlName(DisplayExclusiveFullscreenControl mode)
   1572 {
   1573   return s_display_exclusive_fullscreen_mode_names[static_cast<int>(mode)];
   1574 }
   1575 
   1576 const char* Settings::GetDisplayExclusiveFullscreenControlDisplayName(DisplayExclusiveFullscreenControl mode)
   1577 {
   1578   return Host::TranslateToCString("Settings",
   1579                                   s_display_exclusive_fullscreen_mode_display_names[static_cast<int>(mode)]);
   1580 }
   1581 
   1582 static constexpr const std::array s_display_screenshot_mode_names = {
   1583   "ScreenResolution",
   1584   "InternalResolution",
   1585   "UncorrectedInternalResolution",
   1586 };
   1587 static constexpr const std::array s_display_screenshot_mode_display_names = {
   1588   TRANSLATE_NOOP("Settings", "Screen Resolution"),
   1589   TRANSLATE_NOOP("Settings", "Internal Resolution"),
   1590   TRANSLATE_NOOP("Settings", "Internal Resolution (Aspect Uncorrected)"),
   1591 };
   1592 
   1593 std::optional<DisplayScreenshotMode> Settings::ParseDisplayScreenshotMode(const char* str)
   1594 {
   1595   int index = 0;
   1596   for (const char* name : s_display_screenshot_mode_names)
   1597   {
   1598     if (StringUtil::Strcasecmp(name, str) == 0)
   1599       return static_cast<DisplayScreenshotMode>(index);
   1600 
   1601     index++;
   1602   }
   1603 
   1604   return std::nullopt;
   1605 }
   1606 
   1607 const char* Settings::GetDisplayScreenshotModeName(DisplayScreenshotMode mode)
   1608 {
   1609   return s_display_screenshot_mode_names[static_cast<size_t>(mode)];
   1610 }
   1611 
   1612 const char* Settings::GetDisplayScreenshotModeDisplayName(DisplayScreenshotMode mode)
   1613 {
   1614   return Host::TranslateToCString("Settings", s_display_screenshot_mode_display_names[static_cast<size_t>(mode)]);
   1615 }
   1616 
   1617 static constexpr const std::array s_display_screenshot_format_names = {
   1618   "PNG",
   1619   "JPEG",
   1620   "WebP",
   1621 };
   1622 static constexpr const std::array s_display_screenshot_format_display_names = {
   1623   TRANSLATE_NOOP("Settings", "PNG"),
   1624   TRANSLATE_NOOP("Settings", "JPEG"),
   1625   TRANSLATE_NOOP("Settings", "WebP"),
   1626 };
   1627 static constexpr const std::array s_display_screenshot_format_extensions = {
   1628   "png",
   1629   "jpg",
   1630   "webp",
   1631 };
   1632 
   1633 std::optional<DisplayScreenshotFormat> Settings::ParseDisplayScreenshotFormat(const char* str)
   1634 {
   1635   int index = 0;
   1636   for (const char* name : s_display_screenshot_format_names)
   1637   {
   1638     if (StringUtil::Strcasecmp(name, str) == 0)
   1639       return static_cast<DisplayScreenshotFormat>(index);
   1640 
   1641     index++;
   1642   }
   1643 
   1644   return std::nullopt;
   1645 }
   1646 
   1647 const char* Settings::GetDisplayScreenshotFormatName(DisplayScreenshotFormat format)
   1648 {
   1649   return s_display_screenshot_format_names[static_cast<size_t>(format)];
   1650 }
   1651 
   1652 const char* Settings::GetDisplayScreenshotFormatDisplayName(DisplayScreenshotFormat mode)
   1653 {
   1654   return Host::TranslateToCString("Settings", s_display_screenshot_format_display_names[static_cast<size_t>(mode)]);
   1655 }
   1656 
   1657 const char* Settings::GetDisplayScreenshotFormatExtension(DisplayScreenshotFormat format)
   1658 {
   1659   return s_display_screenshot_format_extensions[static_cast<size_t>(format)];
   1660 }
   1661 
   1662 static constexpr const std::array s_memory_card_type_names = {"None",         "Shared",           "PerGame",
   1663                                                               "PerGameTitle", "PerGameFileTitle", "NonPersistent"};
   1664 static constexpr const std::array s_memory_card_type_display_names = {
   1665   TRANSLATE_NOOP("MemoryCardType", "No Memory Card"),
   1666   TRANSLATE_NOOP("MemoryCardType", "Shared Between All Games"),
   1667   TRANSLATE_NOOP("MemoryCardType", "Separate Card Per Game (Serial)"),
   1668   TRANSLATE_NOOP("MemoryCardType", "Separate Card Per Game (Title)"),
   1669   TRANSLATE_NOOP("MemoryCardType", "Separate Card Per Game (File Title)"),
   1670   TRANSLATE_NOOP("MemoryCardType", "Non-Persistent Card (Do Not Save)")};
   1671 
   1672 std::optional<MemoryCardType> Settings::ParseMemoryCardTypeName(const char* str)
   1673 {
   1674   int index = 0;
   1675   for (const char* name : s_memory_card_type_names)
   1676   {
   1677     if (StringUtil::Strcasecmp(name, str) == 0)
   1678       return static_cast<MemoryCardType>(index);
   1679 
   1680     index++;
   1681   }
   1682 
   1683   return std::nullopt;
   1684 }
   1685 
   1686 const char* Settings::GetMemoryCardTypeName(MemoryCardType type)
   1687 {
   1688   return s_memory_card_type_names[static_cast<int>(type)];
   1689 }
   1690 
   1691 const char* Settings::GetMemoryCardTypeDisplayName(MemoryCardType type)
   1692 {
   1693   return Host::TranslateToCString("MemoryCardType", s_memory_card_type_display_names[static_cast<int>(type)]);
   1694 }
   1695 
   1696 std::string Settings::GetDefaultSharedMemoryCardName(u32 slot)
   1697 {
   1698   return fmt::format("shared_card_{}.mcd", slot + 1);
   1699 }
   1700 
   1701 std::string Settings::GetSharedMemoryCardPath(u32 slot) const
   1702 {
   1703   std::string ret;
   1704 
   1705   if (memory_card_paths[slot].empty())
   1706     ret = Path::Combine(EmuFolders::MemoryCards, GetDefaultSharedMemoryCardName(slot));
   1707   else if (!Path::IsAbsolute(memory_card_paths[slot]))
   1708     ret = Path::Combine(EmuFolders::MemoryCards, memory_card_paths[slot]);
   1709   else
   1710     ret = memory_card_paths[slot];
   1711 
   1712   return ret;
   1713 }
   1714 
   1715 std::string Settings::GetGameMemoryCardPath(std::string_view serial, u32 slot)
   1716 {
   1717   return Path::Combine(EmuFolders::MemoryCards, fmt::format("{}_{}.mcd", serial, slot + 1));
   1718 }
   1719 
   1720 static constexpr const std::array s_multitap_enable_mode_names = {"Disabled", "Port1Only", "Port2Only", "BothPorts"};
   1721 static constexpr const std::array s_multitap_enable_mode_display_names = {
   1722   TRANSLATE_NOOP("MultitapMode", "Disabled"), TRANSLATE_NOOP("MultitapMode", "Enable on Port 1 Only"),
   1723   TRANSLATE_NOOP("MultitapMode", "Enable on Port 2 Only"), TRANSLATE_NOOP("MultitapMode", "Enable on Ports 1 and 2")};
   1724 
   1725 std::optional<MultitapMode> Settings::ParseMultitapModeName(const char* str)
   1726 {
   1727   u32 index = 0;
   1728   for (const char* name : s_multitap_enable_mode_names)
   1729   {
   1730     if (StringUtil::Strcasecmp(name, str) == 0)
   1731       return static_cast<MultitapMode>(index);
   1732 
   1733     index++;
   1734   }
   1735 
   1736   return std::nullopt;
   1737 }
   1738 
   1739 const char* Settings::GetMultitapModeName(MultitapMode mode)
   1740 {
   1741   return s_multitap_enable_mode_names[static_cast<size_t>(mode)];
   1742 }
   1743 
   1744 const char* Settings::GetMultitapModeDisplayName(MultitapMode mode)
   1745 {
   1746   return Host::TranslateToCString("MultitapMode", s_multitap_enable_mode_display_names[static_cast<size_t>(mode)]);
   1747 }
   1748 
   1749 static constexpr const std::array s_mechacon_version_names = {"VC0A", "VC0B", "VC1A", "VC1B", "VD1",  "VC2", "VC1",
   1750                                                               "VC2J", "VC2A", "VC2B", "VC3A", "VC3B", "VC3C"};
   1751 static constexpr const std::array s_mechacon_version_display_names = {
   1752   "94/09/19 (VC0A)", "94/11/18 (VC0B)", "95/05/16 (VC1A)", "95/07/24 (VC1B)", "95/07/24 (VD1)",
   1753   "96/08/15 (VC2)",  "96/08/18 (VC1)",  "96/09/12 (VC2J)", "97/01/10 (VC2A)", "97/08/14 (VC2B)",
   1754   "98/06/10 (VC3A)", "99/02/01 (VC3B)", "01/03/06 (VC3C)"};
   1755 
   1756 std::optional<CDROMMechaconVersion> Settings::ParseCDROMMechVersionName(const char* str)
   1757 {
   1758   u32 index = 0;
   1759   for (const char* name : s_mechacon_version_names)
   1760   {
   1761     if (StringUtil::Strcasecmp(name, str) == 0)
   1762       return static_cast<CDROMMechaconVersion>(index);
   1763 
   1764     index++;
   1765   }
   1766 
   1767   return std::nullopt;
   1768 }
   1769 
   1770 const char* Settings::GetCDROMMechVersionName(CDROMMechaconVersion mode)
   1771 {
   1772   return s_mechacon_version_names[static_cast<u32>(mode)];
   1773 }
   1774 
   1775 const char* Settings::GetCDROMMechVersionDisplayName(CDROMMechaconVersion mode)
   1776 {
   1777   return s_mechacon_version_display_names[static_cast<size_t>(mode)];
   1778 }
   1779 
   1780 static constexpr const std::array s_save_state_compression_mode_names = {
   1781   "Uncompressed", "DeflateLow", "DeflateDefault", "DeflateHigh", "ZstLow", "ZstDefault", "ZstHigh",
   1782 };
   1783 static constexpr const std::array s_save_state_compression_mode_display_names = {
   1784   TRANSLATE_NOOP("Settings", "Uncompressed"),      TRANSLATE_NOOP("Settings", "Deflate (Low)"),
   1785   TRANSLATE_NOOP("Settings", "Deflate (Default)"), TRANSLATE_NOOP("Settings", "Deflate (High)"),
   1786   TRANSLATE_NOOP("Settings", "Zstandard (Low)"),   TRANSLATE_NOOP("Settings", "Zstandard (Default)"),
   1787   TRANSLATE_NOOP("Settings", "Zstandard (High)"),
   1788 };
   1789 static_assert(s_save_state_compression_mode_names.size() == static_cast<size_t>(SaveStateCompressionMode::Count));
   1790 static_assert(s_save_state_compression_mode_display_names.size() ==
   1791               static_cast<size_t>(SaveStateCompressionMode::Count));
   1792 
   1793 std::optional<SaveStateCompressionMode> Settings::ParseSaveStateCompressionModeName(const char* str)
   1794 {
   1795   u32 index = 0;
   1796   for (const char* name : s_save_state_compression_mode_names)
   1797   {
   1798     if (StringUtil::Strcasecmp(name, str) == 0)
   1799       return static_cast<SaveStateCompressionMode>(index);
   1800 
   1801     index++;
   1802   }
   1803 
   1804   return std::nullopt;
   1805 }
   1806 
   1807 const char* Settings::GetSaveStateCompressionModeName(SaveStateCompressionMode mode)
   1808 {
   1809   return s_save_state_compression_mode_names[static_cast<size_t>(mode)];
   1810 }
   1811 
   1812 const char* Settings::GetSaveStateCompressionModeDisplayName(SaveStateCompressionMode mode)
   1813 {
   1814   return Host::TranslateToCString("Settings", s_save_state_compression_mode_display_names[static_cast<size_t>(mode)]);
   1815 }
   1816 
   1817 std::string EmuFolders::AppRoot;
   1818 std::string EmuFolders::DataRoot;
   1819 std::string EmuFolders::Bios;
   1820 std::string EmuFolders::Cache;
   1821 std::string EmuFolders::Cheats;
   1822 std::string EmuFolders::Covers;
   1823 std::string EmuFolders::Dumps;
   1824 std::string EmuFolders::GameIcons;
   1825 std::string EmuFolders::GameSettings;
   1826 std::string EmuFolders::InputProfiles;
   1827 std::string EmuFolders::MemoryCards;
   1828 std::string EmuFolders::Resources;
   1829 std::string EmuFolders::SaveStates;
   1830 std::string EmuFolders::Screenshots;
   1831 std::string EmuFolders::Shaders;
   1832 std::string EmuFolders::Textures;
   1833 std::string EmuFolders::UserResources;
   1834 std::string EmuFolders::Videos;
   1835 
   1836 void EmuFolders::SetDefaults()
   1837 {
   1838   Bios = Path::Combine(DataRoot, "bios");
   1839   Cache = Path::Combine(DataRoot, "cache");
   1840   Cheats = Path::Combine(DataRoot, "cheats");
   1841   Covers = Path::Combine(DataRoot, "covers");
   1842   Dumps = Path::Combine(DataRoot, "dump");
   1843   GameIcons = Path::Combine(DataRoot, "gameicons");
   1844   GameSettings = Path::Combine(DataRoot, "gamesettings");
   1845   InputProfiles = Path::Combine(DataRoot, "inputprofiles");
   1846   MemoryCards = Path::Combine(DataRoot, "memcards");
   1847   SaveStates = Path::Combine(DataRoot, "savestates");
   1848   Screenshots = Path::Combine(DataRoot, "screenshots");
   1849   Shaders = Path::Combine(DataRoot, "shaders");
   1850   Textures = Path::Combine(DataRoot, "textures");
   1851   UserResources = Path::Combine(DataRoot, "resources");
   1852   Videos = Path::Combine(DataRoot, "videos");
   1853 }
   1854 
   1855 static std::string LoadPathFromSettings(SettingsInterface& si, const std::string& root, const char* section,
   1856                                         const char* name, const char* def)
   1857 {
   1858   std::string value = si.GetStringValue(section, name, def);
   1859   if (value.empty())
   1860     value = def;
   1861   if (!Path::IsAbsolute(value))
   1862     value = Path::Combine(root, value);
   1863   value = Path::RealPath(value);
   1864   return value;
   1865 }
   1866 
   1867 void EmuFolders::LoadConfig(SettingsInterface& si)
   1868 {
   1869   Bios = LoadPathFromSettings(si, DataRoot, "BIOS", "SearchDirectory", "bios");
   1870   Cache = LoadPathFromSettings(si, DataRoot, "Folders", "Cache", "cache");
   1871   Cheats = LoadPathFromSettings(si, DataRoot, "Folders", "Cheats", "cheats");
   1872   Covers = LoadPathFromSettings(si, DataRoot, "Folders", "Covers", "covers");
   1873   Dumps = LoadPathFromSettings(si, DataRoot, "Folders", "Dumps", "dump");
   1874   GameIcons = LoadPathFromSettings(si, DataRoot, "Folders", "GameIcons", "gameicons");
   1875   GameSettings = LoadPathFromSettings(si, DataRoot, "Folders", "GameSettings", "gamesettings");
   1876   InputProfiles = LoadPathFromSettings(si, DataRoot, "Folders", "InputProfiles", "inputprofiles");
   1877   MemoryCards = LoadPathFromSettings(si, DataRoot, "MemoryCards", "Directory", "memcards");
   1878   SaveStates = LoadPathFromSettings(si, DataRoot, "Folders", "SaveStates", "savestates");
   1879   Screenshots = LoadPathFromSettings(si, DataRoot, "Folders", "Screenshots", "screenshots");
   1880   Shaders = LoadPathFromSettings(si, DataRoot, "Folders", "Shaders", "shaders");
   1881   Textures = LoadPathFromSettings(si, DataRoot, "Folders", "Textures", "textures");
   1882   UserResources = LoadPathFromSettings(si, DataRoot, "Folders", "UserResources", "resources");
   1883   Videos = LoadPathFromSettings(si, DataRoot, "Folders", "Videos", "videos");
   1884 
   1885   DEV_LOG("BIOS Directory: {}", Bios);
   1886   DEV_LOG("Cache Directory: {}", Cache);
   1887   DEV_LOG("Cheats Directory: {}", Cheats);
   1888   DEV_LOG("Covers Directory: {}", Covers);
   1889   DEV_LOG("Dumps Directory: {}", Dumps);
   1890   DEV_LOG("Game Icons Directory: {}", GameIcons);
   1891   DEV_LOG("Game Settings Directory: {}", GameSettings);
   1892   DEV_LOG("Input Profile Directory: {}", InputProfiles);
   1893   DEV_LOG("MemoryCards Directory: {}", MemoryCards);
   1894   DEV_LOG("Resources Directory: {}", Resources);
   1895   DEV_LOG("SaveStates Directory: {}", SaveStates);
   1896   DEV_LOG("Screenshots Directory: {}", Screenshots);
   1897   DEV_LOG("Shaders Directory: {}", Shaders);
   1898   DEV_LOG("Textures Directory: {}", Textures);
   1899   DEV_LOG("User Resources Directory: {}", UserResources);
   1900   DEV_LOG("Videos Directory: {}", Videos);
   1901 }
   1902 
   1903 void EmuFolders::Save(SettingsInterface& si)
   1904 {
   1905   // convert back to relative
   1906   si.SetStringValue("BIOS", "SearchDirectory", Path::MakeRelative(Bios, DataRoot).c_str());
   1907   si.SetStringValue("Folders", "Cache", Path::MakeRelative(Cache, DataRoot).c_str());
   1908   si.SetStringValue("Folders", "Cheats", Path::MakeRelative(Cheats, DataRoot).c_str());
   1909   si.SetStringValue("Folders", "Covers", Path::MakeRelative(Covers, DataRoot).c_str());
   1910   si.SetStringValue("Folders", "Dumps", Path::MakeRelative(Dumps, DataRoot).c_str());
   1911   si.SetStringValue("Folders", "GameIcons", Path::MakeRelative(GameIcons, DataRoot).c_str());
   1912   si.SetStringValue("Folders", "GameSettings", Path::MakeRelative(GameSettings, DataRoot).c_str());
   1913   si.SetStringValue("Folders", "InputProfiles", Path::MakeRelative(InputProfiles, DataRoot).c_str());
   1914   si.SetStringValue("MemoryCards", "Directory", Path::MakeRelative(MemoryCards, DataRoot).c_str());
   1915   si.SetStringValue("Folders", "SaveStates", Path::MakeRelative(SaveStates, DataRoot).c_str());
   1916   si.SetStringValue("Folders", "Screenshots", Path::MakeRelative(Screenshots, DataRoot).c_str());
   1917   si.SetStringValue("Folders", "Shaders", Path::MakeRelative(Shaders, DataRoot).c_str());
   1918   si.SetStringValue("Folders", "Textures", Path::MakeRelative(Textures, DataRoot).c_str());
   1919   si.SetStringValue("Folders", "UserResources", Path::MakeRelative(UserResources, DataRoot).c_str());
   1920   si.SetStringValue("Folders", "Videos", Path::MakeRelative(Videos, DataRoot).c_str());
   1921 }
   1922 
   1923 void EmuFolders::Update()
   1924 {
   1925   const std::string old_gamesettings(EmuFolders::GameSettings);
   1926   const std::string old_inputprofiles(EmuFolders::InputProfiles);
   1927   const std::string old_memorycards(EmuFolders::MemoryCards);
   1928 
   1929   // have to manually grab the lock here, because of the ReloadGameSettings() below.
   1930   {
   1931     auto lock = Host::GetSettingsLock();
   1932     LoadConfig(*Host::Internal::GetBaseSettingsLayer());
   1933     EnsureFoldersExist();
   1934   }
   1935 
   1936   if (old_gamesettings != EmuFolders::GameSettings || old_inputprofiles != EmuFolders::InputProfiles)
   1937     System::ReloadGameSettings(false);
   1938 
   1939   if (System::IsValid() && old_memorycards != EmuFolders::MemoryCards)
   1940     System::UpdateMemoryCardTypes();
   1941 }
   1942 
   1943 bool EmuFolders::EnsureFoldersExist()
   1944 {
   1945   bool result = FileSystem::EnsureDirectoryExists(Bios.c_str(), false);
   1946   result = FileSystem::EnsureDirectoryExists(Cache.c_str(), false) && result;
   1947   result = FileSystem::EnsureDirectoryExists(Path::Combine(Cache, "achievement_images").c_str(), false) && result;
   1948   result = FileSystem::EnsureDirectoryExists(Cheats.c_str(), false) && result;
   1949   result = FileSystem::EnsureDirectoryExists(Covers.c_str(), false) && result;
   1950   result = FileSystem::EnsureDirectoryExists(Dumps.c_str(), false) && result;
   1951   result = FileSystem::EnsureDirectoryExists(Path::Combine(Dumps, "audio").c_str(), false) && result;
   1952   result = FileSystem::EnsureDirectoryExists(Path::Combine(Dumps, "textures").c_str(), false) && result;
   1953   result = FileSystem::EnsureDirectoryExists(GameIcons.c_str(), false) && result;
   1954   result = FileSystem::EnsureDirectoryExists(GameSettings.c_str(), false) && result;
   1955   result = FileSystem::EnsureDirectoryExists(InputProfiles.c_str(), false) && result;
   1956   result = FileSystem::EnsureDirectoryExists(MemoryCards.c_str(), false) && result;
   1957   result = FileSystem::EnsureDirectoryExists(SaveStates.c_str(), false) && result;
   1958   result = FileSystem::EnsureDirectoryExists(Screenshots.c_str(), false) && result;
   1959   result = FileSystem::EnsureDirectoryExists(Shaders.c_str(), false) && result;
   1960   result = FileSystem::EnsureDirectoryExists(Path::Combine(Shaders, "reshade").c_str(), false) && result;
   1961   result = FileSystem::EnsureDirectoryExists(
   1962              Path::Combine(Shaders, "reshade" FS_OSPATH_SEPARATOR_STR "Shaders").c_str(), false) &&
   1963            result;
   1964   result = FileSystem::EnsureDirectoryExists(
   1965              Path::Combine(Shaders, "reshade" FS_OSPATH_SEPARATOR_STR "Textures").c_str(), false) &&
   1966            result;
   1967   result = FileSystem::EnsureDirectoryExists(Textures.c_str(), false) && result;
   1968   result = FileSystem::EnsureDirectoryExists(UserResources.c_str(), false) && result;
   1969   result = FileSystem::EnsureDirectoryExists(Videos.c_str(), false) && result;
   1970   return result;
   1971 }
   1972 
   1973 std::string EmuFolders::GetOverridableResourcePath(std::string_view name)
   1974 {
   1975   std::string upath = Path::Combine(UserResources, name);
   1976   if (FileSystem::FileExists(upath.c_str()))
   1977   {
   1978     if (UserResources != Resources)
   1979       WARNING_LOG("Using user-provided resource file {}", name);
   1980   }
   1981   else
   1982   {
   1983     upath = Path::Combine(Resources, name);
   1984   }
   1985 
   1986   return upath;
   1987 }
   1988 
   1989 static const char* s_log_filters[] = {
   1990   "Achievements",
   1991   "AnalogController",
   1992   "AnalogJoystick",
   1993   "AudioStream",
   1994   "AutoUpdaterDialog",
   1995   "BIOS",
   1996   "Bus",
   1997   "ByteStream",
   1998   "CDImage",
   1999   "CDImageBin",
   2000   "CDImageCHD",
   2001   "CDImageCueSheet",
   2002   "CDImageDevice",
   2003   "CDImageEcm",
   2004   "CDImageMds",
   2005   "CDImageMemory",
   2006   "CDImagePBP",
   2007   "CDImagePPF",
   2008   "CDROM",
   2009   "CDROMAsyncReader",
   2010   "CDSubChannelReplacement",
   2011   "CPU::CodeCache",
   2012   "CPU::Core",
   2013   "CPU::Recompiler",
   2014   "Common::PageFaultHandler",
   2015   "ControllerBindingWidget",
   2016   "CueParser",
   2017   "Cheats",
   2018   "DMA",
   2019   "DisplayWidget",
   2020   "FileSystem",
   2021   "FullscreenUI",
   2022   "GDBConnection",
   2023   "GDBProtocol",
   2024   "GDBServer",
   2025   "GPU",
   2026   "GPUBackend",
   2027   "GPUDevice",
   2028   "GPUShaderCache",
   2029   "GPUTexture",
   2030   "GPU_HW",
   2031   "GPU_SW",
   2032   "GameDatabase",
   2033   "GameList",
   2034   "GunCon",
   2035   "HTTPDownloader",
   2036   "Host",
   2037   "HostInterfaceProgressCallback",
   2038   "INISettingsInterface",
   2039   "ISOReader",
   2040   "ImGuiFullscreen",
   2041   "ImGuiManager",
   2042   "Image",
   2043   "InputManager",
   2044   "InterruptController",
   2045   "JitCodeBuffer",
   2046   "MDEC",
   2047   "MainWindow",
   2048   "MemoryArena",
   2049   "MemoryCard",
   2050   "Multitap",
   2051   "NoGUIHost",
   2052   "PCDrv",
   2053   "PGXP",
   2054   "PSFLoader",
   2055   "Pad",
   2056   "PlatformMisc",
   2057   "PlayStationMouse",
   2058   "PostProcessing",
   2059   "ProgressCallback",
   2060   "QTTranslations",
   2061   "QtHost",
   2062   "ReShadeFXShader",
   2063   "Recompiler::CodeGenerator",
   2064   "RegTestHost",
   2065   "SDLInputSource",
   2066   "SIO",
   2067   "SPIRVCompiler",
   2068   "SPU",
   2069   "Settings",
   2070   "ShaderGen",
   2071   "StateWrapper",
   2072   "System",
   2073   "TextureReplacements",
   2074   "Timers",
   2075   "TimingEvents",
   2076   "WAVWriter",
   2077   "WindowInfo",
   2078 
   2079 #ifndef __ANDROID__
   2080   "CubebAudioStream",
   2081   "SDLAudioStream",
   2082 #endif
   2083 
   2084 #ifdef ENABLE_OPENGL
   2085   "OpenGLContext",
   2086   "OpenGLDevice",
   2087 #endif
   2088 
   2089 #ifdef ENABLE_VULKAN
   2090   "VulkanDevice",
   2091 #endif
   2092 
   2093 #if defined(_WIN32)
   2094   "D3D11Device",
   2095   "D3D12Device",
   2096   "D3D12StreamBuffer",
   2097   "D3DCommon",
   2098   "DInputSource",
   2099   "Win32ProgressCallback",
   2100   "Win32RawInputSource",
   2101   "XAudio2AudioStream",
   2102   "XInputSource",
   2103 #elif defined(__APPLE__)
   2104   "CocoaNoGUIPlatform",
   2105   "CocoaProgressCallback",
   2106   "MetalDevice",
   2107 #else
   2108   "X11NoGUIPlatform",
   2109   "WaylandNoGUIPlatform",
   2110 #endif
   2111 };
   2112 
   2113 std::span<const char*> Settings::GetLogFilters()
   2114 {
   2115   return s_log_filters;
   2116 }