#include "game_settings.h" #include "common/assert.h" #include "common/byte_stream.h" #include "common/file_system.h" #include "common/log.h" #include "common/string.h" #include "common/string_util.h" #include "core/host_interface.h" #include "core/settings.h" #include #include Log_SetChannel(GameSettings); #ifdef WIN32 #include "common/windows_headers.h" #endif #include "SimpleIni.h" namespace GameSettings { std::array, static_cast(Trait::Count)> s_trait_names = {{ {"ForceInterpreter", TRANSLATABLE("GameSettingsTrait", "Force Interpreter")}, {"ForceSoftwareRenderer", TRANSLATABLE("GameSettingsTrait", "Force Software Renderer")}, {"ForceInterlacing", TRANSLATABLE("GameSettingsTrait", "Force Interlacing")}, {"DisableTrueColor", TRANSLATABLE("GameSettingsTrait", "Disable True Color")}, {"DisableUpscaling", TRANSLATABLE("GameSettingsTrait", "Disable Upscaling")}, {"DisableScaledDithering", TRANSLATABLE("GameSettingsTrait", "Disable Scaled Dithering")}, {"DisableForceNTSCTimings", TRANSLATABLE("GameSettingsTrait", "Disallow Forcing NTSC Timings")}, {"DisableWidescreen", TRANSLATABLE("GameSettingsTrait", "Disable Widescreen")}, {"DisablePGXP", TRANSLATABLE("GameSettingsTrait", "Disable PGXP")}, {"DisablePGXPCulling", TRANSLATABLE("GameSettingsTrait", "Disable PGXP Culling")}, {"DisablePGXPTextureCorrection", TRANSLATABLE("GameSettingsTrait", "Disable PGXP Texture Correction")}, {"ForcePGXPVertexCache", TRANSLATABLE("GameSettingsTrait", "Force PGXP Vertex Cache")}, {"ForcePGXPCPUMode", TRANSLATABLE("GameSettingsTrait", "Force PGXP CPU Mode")}, {"ForceDigitalController", TRANSLATABLE("GameSettingsTrait", "Force Digital Controller")}, {"ForceRecompilerMemoryExceptions", TRANSLATABLE("GameSettingsTrait", "Force Recompiler Memory Exceptions")}, {"ForceRecompilerICache", TRANSLATABLE("GameSettingsTrait", "Force Recompiler ICache")}, }}; const char* GetTraitName(Trait trait) { DebugAssert(trait < Trait::Count); return s_trait_names[static_cast(trait)].first; } const char* GetTraitDisplayName(Trait trait) { DebugAssert(trait < Trait::Count); return s_trait_names[static_cast(trait)].second; } template bool ReadOptionalFromStream(ByteStream* stream, std::optional* dest) { bool has_value; if (!stream->Read2(&has_value, sizeof(has_value))) return false; if (!has_value) return true; T value; if (!stream->Read2(&value, sizeof(T))) return false; *dest = value; return true; } static bool ReadStringFromStream(ByteStream* stream, std::string* dest) { u32 size; if (!stream->Read2(&size, sizeof(size))) return false; dest->resize(size); if (!stream->Read2(dest->data(), size)) return false; return true; } template bool WriteOptionalToStream(ByteStream* stream, const std::optional& src) { const bool has_value = src.has_value(); if (!stream->Write2(&has_value, sizeof(has_value))) return false; if (!has_value) return true; return stream->Write2(&src.value(), sizeof(T)); } static bool WriteStringToStream(ByteStream* stream, const std::string& str) { const u32 size = static_cast(str.size()); return (stream->Write2(&size, sizeof(size)) && (size == 0 || stream->Write2(str.data(), size))); } bool Entry::LoadFromStream(ByteStream* stream) { constexpr u32 num_bytes = (static_cast(Trait::Count) + 7) / 8; std::array bits; if (!stream->Read2(bits.data(), num_bytes) || !ReadOptionalFromStream(stream, &cpu_overclock_numerator) || !ReadOptionalFromStream(stream, &cpu_overclock_denominator) || !ReadOptionalFromStream(stream, &cpu_overclock_enable) || !ReadOptionalFromStream(stream, &cdrom_read_speedup) || !ReadOptionalFromStream(stream, &display_active_start_offset) || !ReadOptionalFromStream(stream, &display_active_end_offset) || !ReadOptionalFromStream(stream, &dma_max_slice_ticks) || !ReadOptionalFromStream(stream, &dma_halt_ticks) || !ReadOptionalFromStream(stream, &gpu_fifo_size) || !ReadOptionalFromStream(stream, &gpu_max_run_ahead) || !ReadOptionalFromStream(stream, &gpu_pgxp_tolerance) || !ReadOptionalFromStream(stream, &display_crop_mode) || !ReadOptionalFromStream(stream, &display_aspect_ratio) || !ReadOptionalFromStream(stream, &display_linear_upscaling) || !ReadOptionalFromStream(stream, &display_integer_upscaling) || !ReadOptionalFromStream(stream, &display_force_4_3_for_24bit) || !ReadOptionalFromStream(stream, &gpu_resolution_scale) || !ReadOptionalFromStream(stream, &gpu_multisamples) || !ReadOptionalFromStream(stream, &gpu_per_sample_shading) || !ReadOptionalFromStream(stream, &gpu_true_color) || !ReadOptionalFromStream(stream, &gpu_scaled_dithering) || !ReadOptionalFromStream(stream, &gpu_force_ntsc_timings) || !ReadOptionalFromStream(stream, &gpu_texture_filter) || !ReadOptionalFromStream(stream, &gpu_widescreen_hack) || !ReadOptionalFromStream(stream, &gpu_pgxp) || !ReadOptionalFromStream(stream, &controller_1_type) || !ReadOptionalFromStream(stream, &controller_2_type) || !ReadOptionalFromStream(stream, &memory_card_1_type) || !ReadOptionalFromStream(stream, &memory_card_2_type) || !ReadStringFromStream(stream, &memory_card_1_shared_path) || !ReadStringFromStream(stream, &memory_card_2_shared_path) || !ReadStringFromStream(stream, &input_profile_name)) { return false; } traits.reset(); for (u32 i = 0; i < static_cast(Trait::Count); i++) { if ((bits[i / 8] & (1u << (i % 8))) != 0) AddTrait(static_cast(i)); } return true; } bool Entry::SaveToStream(ByteStream* stream) const { constexpr u32 num_bytes = (static_cast(Trait::Count) + 7) / 8; std::array bits; bits.fill(0); for (u32 i = 0; i < static_cast(Trait::Count); i++) { if (HasTrait(static_cast(i))) bits[i / 8] |= (1u << (i % 8)); } return stream->Write2(bits.data(), num_bytes) && WriteOptionalToStream(stream, cpu_overclock_numerator) && WriteOptionalToStream(stream, cpu_overclock_denominator) && WriteOptionalToStream(stream, cpu_overclock_enable) && WriteOptionalToStream(stream, cdrom_read_speedup) && WriteOptionalToStream(stream, display_active_start_offset) && WriteOptionalToStream(stream, display_active_end_offset) && WriteOptionalToStream(stream, dma_max_slice_ticks) && WriteOptionalToStream(stream, dma_halt_ticks) && WriteOptionalToStream(stream, gpu_fifo_size) && WriteOptionalToStream(stream, gpu_max_run_ahead) && WriteOptionalToStream(stream, gpu_pgxp_tolerance) && WriteOptionalToStream(stream, display_crop_mode) && WriteOptionalToStream(stream, display_aspect_ratio) && WriteOptionalToStream(stream, display_linear_upscaling) && WriteOptionalToStream(stream, display_integer_upscaling) && WriteOptionalToStream(stream, display_force_4_3_for_24bit) && WriteOptionalToStream(stream, gpu_resolution_scale) && WriteOptionalToStream(stream, gpu_multisamples) && WriteOptionalToStream(stream, gpu_per_sample_shading) && WriteOptionalToStream(stream, gpu_true_color) && WriteOptionalToStream(stream, gpu_scaled_dithering) && WriteOptionalToStream(stream, gpu_force_ntsc_timings) && WriteOptionalToStream(stream, gpu_texture_filter) && WriteOptionalToStream(stream, gpu_widescreen_hack) && WriteOptionalToStream(stream, gpu_pgxp) && WriteOptionalToStream(stream, controller_1_type) && WriteOptionalToStream(stream, controller_2_type) && WriteOptionalToStream(stream, memory_card_1_type) && WriteOptionalToStream(stream, memory_card_2_type) && WriteStringToStream(stream, memory_card_1_shared_path) && WriteStringToStream(stream, memory_card_2_shared_path) && WriteStringToStream(stream, input_profile_name); } static void ParseIniSection(Entry* entry, const char* section, const CSimpleIniA& ini) { for (u32 trait = 0; trait < static_cast(Trait::Count); trait++) { if (ini.GetBoolValue(section, s_trait_names[trait].first, false)) entry->AddTrait(static_cast(trait)); } const char* cvalue = ini.GetValue(section, "CPUOverclockNumerator", nullptr); if (cvalue) entry->cpu_overclock_numerator = StringUtil::FromChars(cvalue); cvalue = ini.GetValue(section, "CPUOverclockDenominator", nullptr); if (cvalue) entry->cpu_overclock_denominator = StringUtil::FromChars(cvalue); cvalue = ini.GetValue(section, "CPUOverclockEnable", nullptr); if (cvalue) entry->cpu_overclock_enable = StringUtil::FromChars(cvalue); cvalue = ini.GetValue(section, "CDROMReadSpeedup", nullptr); if (cvalue) entry->cdrom_read_speedup = StringUtil::FromChars(cvalue); long lvalue = ini.GetLongValue(section, "DisplayActiveStartOffset", 0); if (lvalue != 0) entry->display_active_start_offset = static_cast(lvalue); lvalue = ini.GetLongValue(section, "DisplayActiveEndOffset", 0); if (lvalue != 0) entry->display_active_end_offset = static_cast(lvalue); lvalue = ini.GetLongValue(section, "DMAMaxSliceTicks", 0); if (lvalue > 0) entry->dma_max_slice_ticks = static_cast(lvalue); lvalue = ini.GetLongValue(section, "DMAHaltTicks", 0); if (lvalue > 0) entry->dma_halt_ticks = static_cast(lvalue); lvalue = ini.GetLongValue(section, "GPUFIFOSize", 0); if (lvalue > 0) entry->gpu_fifo_size = static_cast(lvalue); lvalue = ini.GetLongValue(section, "GPUMaxRunAhead", 0); if (lvalue > 0) entry->gpu_max_run_ahead = static_cast(lvalue); float fvalue = static_cast(ini.GetDoubleValue(section, "GPUPGXPTolerance", -1.0f)); if (fvalue >= 0.0f) entry->gpu_pgxp_tolerance = fvalue; cvalue = ini.GetValue(section, "DisplayCropMode", nullptr); if (cvalue) entry->display_crop_mode = Settings::ParseDisplayCropMode(cvalue); cvalue = ini.GetValue(section, "DisplayAspectRatio", nullptr); if (cvalue) entry->display_aspect_ratio = Settings::ParseDisplayAspectRatio(cvalue); cvalue = ini.GetValue(section, "DisplayLinearUpscaling", nullptr); if (cvalue) entry->display_linear_upscaling = StringUtil::FromChars(cvalue); cvalue = ini.GetValue(section, "DisplayIntegerUpscaling", nullptr); if (cvalue) entry->display_integer_upscaling = StringUtil::FromChars(cvalue); cvalue = ini.GetValue(section, "DisplayForce4_3For24Bit", nullptr); if (cvalue) entry->display_force_4_3_for_24bit = StringUtil::FromChars(cvalue); cvalue = ini.GetValue(section, "GPUResolutionScale", nullptr); if (cvalue) entry->gpu_resolution_scale = StringUtil::FromChars(cvalue); cvalue = ini.GetValue(section, "GPUMultisamples", nullptr); if (cvalue) entry->gpu_multisamples = StringUtil::FromChars(cvalue); cvalue = ini.GetValue(section, "GPUPerSampleShading", nullptr); if (cvalue) entry->gpu_per_sample_shading = StringUtil::FromChars(cvalue); cvalue = ini.GetValue(section, "GPUTrueColor", nullptr); if (cvalue) entry->gpu_true_color = StringUtil::FromChars(cvalue); cvalue = ini.GetValue(section, "GPUScaledDithering", nullptr); if (cvalue) entry->gpu_scaled_dithering = StringUtil::FromChars(cvalue); cvalue = ini.GetValue(section, "GPUBilinearTextureFiltering", nullptr); if (cvalue) entry->gpu_texture_filter = Settings::ParseTextureFilterName(cvalue); cvalue = ini.GetValue(section, "GPUForceNTSCTimings", nullptr); if (cvalue) entry->gpu_force_ntsc_timings = StringUtil::FromChars(cvalue); cvalue = ini.GetValue(section, "GPUWidescreenHack", nullptr); if (cvalue) entry->gpu_widescreen_hack = StringUtil::FromChars(cvalue); cvalue = ini.GetValue(section, "GPUPGXP", nullptr); if (cvalue) entry->gpu_pgxp = StringUtil::FromChars(cvalue); cvalue = ini.GetValue(section, "Controller1Type", nullptr); if (cvalue) entry->controller_1_type = Settings::ParseControllerTypeName(cvalue); cvalue = ini.GetValue(section, "Controller2Type", nullptr); if (cvalue) entry->controller_2_type = Settings::ParseControllerTypeName(cvalue); cvalue = ini.GetValue(section, "MemoryCard1Type", nullptr); if (cvalue) entry->memory_card_1_type = Settings::ParseMemoryCardTypeName(cvalue); cvalue = ini.GetValue(section, "MemoryCard2Type", nullptr); if (cvalue) entry->memory_card_2_type = Settings::ParseMemoryCardTypeName(cvalue); cvalue = ini.GetValue(section, "MemoryCard1SharedPath"); if (cvalue) entry->memory_card_1_shared_path = cvalue; cvalue = ini.GetValue(section, "MemoryCard2SharedPath"); if (cvalue) entry->memory_card_2_shared_path = cvalue; cvalue = ini.GetValue(section, "InputProfileName"); if (cvalue) entry->input_profile_name = cvalue; } static void StoreIniSection(const Entry& entry, const char* section, CSimpleIniA& ini) { for (u32 trait = 0; trait < static_cast(Trait::Count); trait++) { if (entry.HasTrait(static_cast(trait))) ini.SetBoolValue(section, s_trait_names[trait].first, true); } if (entry.cpu_overclock_numerator.has_value()) ini.SetLongValue(section, "CPUOverclockNumerator", static_cast(entry.cpu_overclock_numerator.value())); if (entry.cpu_overclock_denominator.has_value()) ini.SetLongValue(section, "CPUOverclockDenominator", static_cast(entry.cpu_overclock_denominator.value())); if (entry.cpu_overclock_enable.has_value()) ini.SetBoolValue(section, "CPUOverclockEnable", entry.cpu_overclock_enable.value()); if (entry.cdrom_read_speedup.has_value()) ini.SetLongValue(section, "CDROMReadSpeedup", static_cast(entry.cdrom_read_speedup.value())); if (entry.display_active_start_offset.has_value()) ini.SetLongValue(section, "DisplayActiveStartOffset", entry.display_active_start_offset.value()); if (entry.display_active_end_offset.has_value()) ini.SetLongValue(section, "DisplayActiveEndOffset", entry.display_active_end_offset.value()); if (entry.dma_max_slice_ticks.has_value()) ini.SetLongValue(section, "DMAMaxSliceTicks", static_cast(entry.dma_max_slice_ticks.value())); if (entry.dma_halt_ticks.has_value()) ini.SetLongValue(section, "DMAHaltTicks", static_cast(entry.dma_halt_ticks.value())); if (entry.gpu_fifo_size.has_value()) ini.SetLongValue(section, "GPUFIFOSize", static_cast(entry.gpu_fifo_size.value())); if (entry.gpu_max_run_ahead.has_value()) ini.SetLongValue(section, "GPUMaxRunAhead", static_cast(entry.gpu_max_run_ahead.value())); if (entry.gpu_pgxp_tolerance.has_value()) ini.SetDoubleValue(section, "GPUPGXPTolerance", static_cast(entry.gpu_pgxp_tolerance.value())); if (entry.display_crop_mode.has_value()) ini.SetValue(section, "DisplayCropMode", Settings::GetDisplayCropModeName(entry.display_crop_mode.value())); if (entry.display_aspect_ratio.has_value()) { ini.SetValue(section, "DisplayAspectRatio", Settings::GetDisplayAspectRatioName(entry.display_aspect_ratio.value())); } if (entry.display_linear_upscaling.has_value()) ini.SetValue(section, "DisplayLinearUpscaling", entry.display_linear_upscaling.value() ? "true" : "false"); if (entry.display_integer_upscaling.has_value()) ini.SetValue(section, "DisplayIntegerUpscaling", entry.display_integer_upscaling.value() ? "true" : "false"); if (entry.display_force_4_3_for_24bit.has_value()) ini.SetValue(section, "DisplayForce4_3For24Bit", entry.display_force_4_3_for_24bit.value() ? "true" : "false"); if (entry.gpu_resolution_scale.has_value()) ini.SetLongValue(section, "GPUResolutionScale", static_cast(entry.gpu_resolution_scale.value())); if (entry.gpu_multisamples.has_value()) ini.SetLongValue(section, "GPUMultisamples", static_cast(entry.gpu_multisamples.value())); if (entry.gpu_per_sample_shading.has_value()) ini.SetValue(section, "GPUPerSampleShading", entry.gpu_per_sample_shading.value() ? "true" : "false"); if (entry.gpu_true_color.has_value()) ini.SetValue(section, "GPUTrueColor", entry.gpu_true_color.value() ? "true" : "false"); if (entry.gpu_scaled_dithering.has_value()) ini.SetValue(section, "GPUScaledDithering", entry.gpu_scaled_dithering.value() ? "true" : "false"); if (entry.gpu_texture_filter.has_value()) ini.SetValue(section, "GPUTextureFilter", Settings::GetTextureFilterName(entry.gpu_texture_filter.value())); if (entry.gpu_force_ntsc_timings.has_value()) ini.SetValue(section, "GPUForceNTSCTimings", entry.gpu_force_ntsc_timings.value() ? "true" : "false"); if (entry.gpu_widescreen_hack.has_value()) ini.SetValue(section, "GPUWidescreenHack", entry.gpu_widescreen_hack.value() ? "true" : "false"); if (entry.gpu_pgxp.has_value()) ini.SetValue(section, "GPUPGXP", entry.gpu_pgxp.value() ? "true" : "false"); if (entry.controller_1_type.has_value()) ini.SetValue(section, "Controller1Type", Settings::GetControllerTypeName(entry.controller_1_type.value())); if (entry.controller_2_type.has_value()) ini.SetValue(section, "Controller2Type", Settings::GetControllerTypeName(entry.controller_2_type.value())); if (entry.controller_1_type.has_value()) ini.SetValue(section, "Controller1Type", Settings::GetControllerTypeName(entry.controller_1_type.value())); if (entry.controller_2_type.has_value()) ini.SetValue(section, "Controller2Type", Settings::GetControllerTypeName(entry.controller_2_type.value())); if (entry.memory_card_1_type.has_value()) ini.SetValue(section, "MemoryCard1Type", Settings::GetMemoryCardTypeName(entry.memory_card_1_type.value())); if (entry.memory_card_2_type.has_value()) ini.SetValue(section, "MemoryCard2Type", Settings::GetMemoryCardTypeName(entry.memory_card_2_type.value())); if (!entry.memory_card_1_shared_path.empty()) ini.SetValue(section, "MemoryCard1SharedPath", entry.memory_card_1_shared_path.c_str()); if (!entry.memory_card_2_shared_path.empty()) ini.SetValue(section, "MemoryCard2SharedPath", entry.memory_card_2_shared_path.c_str()); if (!entry.input_profile_name.empty()) ini.SetValue(section, "InputProfileName", entry.input_profile_name.c_str()); } Database::Database() = default; Database::~Database() = default; const GameSettings::Entry* Database::GetEntry(const std::string& code) const { auto it = m_entries.find(code); return (it != m_entries.end()) ? &it->second : nullptr; } bool Database::Load(const char* path) { auto fp = FileSystem::OpenManagedCFile(path, "rb"); if (!fp) return false; CSimpleIniA ini; SI_Error err = ini.LoadFile(fp.get()); if (err != SI_OK) { Log_ErrorPrintf("Failed to parse game settings ini: %d", static_cast(err)); return false; } std::list sections; ini.GetAllSections(sections); for (const CSimpleIniA::Entry& section_entry : sections) { std::string code(section_entry.pItem); auto it = m_entries.find(code); if (it != m_entries.end()) { ParseIniSection(&it->second, code.c_str(), ini); continue; } Entry entry; ParseIniSection(&entry, code.c_str(), ini); m_entries.emplace(std::move(code), std::move(entry)); } Log_InfoPrintf("Loaded settings for %zu games from '%s'", sections.size(), path); return true; } void Database::SetEntry(const std::string& code, const std::string& name, const Entry& entry, const char* save_path) { if (save_path) { CSimpleIniA ini; if (FileSystem::FileExists(save_path)) { auto fp = FileSystem::OpenManagedCFile(save_path, "rb"); if (fp) { SI_Error err = ini.LoadFile(fp.get()); if (err != SI_OK) Log_ErrorPrintf("Failed to parse game settings ini: %d. Contents will be lost.", static_cast(err)); } else { Log_ErrorPrintf("Failed to open existing settings ini: '%s'", save_path); } } ini.Delete(code.c_str(), nullptr, false); ini.SetValue(code.c_str(), nullptr, nullptr, SmallString::FromFormat("# %s (%s)", code.c_str(), name.c_str()), false); StoreIniSection(entry, code.c_str(), ini); const bool did_exist = FileSystem::FileExists(save_path); auto fp = FileSystem::OpenManagedCFile(save_path, "wb"); if (fp) { // write file comment so simpleini doesn't get confused if (!did_exist) std::fputs("# DuckStation Game Settings\n\n", fp.get()); SI_Error err = ini.SaveFile(fp.get()); if (err != SI_OK) Log_ErrorPrintf("Failed to save game settings ini: %d", static_cast(err)); } else { Log_ErrorPrintf("Failed to open settings ini for saving: '%s'", save_path); } } auto it = m_entries.find(code); if (it != m_entries.end()) it->second = entry; else m_entries.emplace(code, entry); } void Entry::ApplySettings(bool display_osd_messages) const { constexpr float osd_duration = 10.0f; if (cpu_overclock_numerator.has_value()) g_settings.cpu_overclock_numerator = cpu_overclock_numerator.value(); if (cpu_overclock_denominator.has_value()) g_settings.cpu_overclock_denominator = cpu_overclock_denominator.value(); if (cpu_overclock_enable.has_value()) g_settings.cpu_overclock_enable = cpu_overclock_enable.value(); g_settings.UpdateOverclockActive(); if (cdrom_read_speedup.has_value()) g_settings.cdrom_read_speedup = cdrom_read_speedup.value(); if (display_active_start_offset.has_value()) g_settings.display_active_start_offset = display_active_start_offset.value(); if (display_active_end_offset.has_value()) g_settings.display_active_end_offset = display_active_end_offset.value(); if (dma_max_slice_ticks.has_value()) g_settings.dma_max_slice_ticks = dma_max_slice_ticks.value(); if (dma_halt_ticks.has_value()) g_settings.dma_halt_ticks = dma_halt_ticks.value(); if (gpu_fifo_size.has_value()) g_settings.gpu_fifo_size = gpu_fifo_size.value(); if (gpu_max_run_ahead.has_value()) g_settings.gpu_max_run_ahead = gpu_max_run_ahead.value(); if (gpu_pgxp_tolerance.has_value()) g_settings.gpu_pgxp_tolerance = gpu_pgxp_tolerance.value(); if (display_crop_mode.has_value()) g_settings.display_crop_mode = display_crop_mode.value(); if (display_aspect_ratio.has_value()) g_settings.display_aspect_ratio = display_aspect_ratio.value(); if (display_linear_upscaling.has_value()) g_settings.display_linear_filtering = display_linear_upscaling.value(); if (display_integer_upscaling.has_value()) g_settings.display_integer_scaling = display_integer_upscaling.value(); if (display_force_4_3_for_24bit.has_value()) g_settings.display_force_4_3_for_24bit = display_force_4_3_for_24bit.value(); if (gpu_resolution_scale.has_value()) g_settings.gpu_resolution_scale = gpu_resolution_scale.value(); if (gpu_multisamples.has_value()) g_settings.gpu_multisamples = gpu_multisamples.value(); if (gpu_per_sample_shading.has_value()) g_settings.gpu_per_sample_shading = gpu_per_sample_shading.value(); if (gpu_true_color.has_value()) g_settings.gpu_true_color = gpu_true_color.value(); if (gpu_scaled_dithering.has_value()) g_settings.gpu_scaled_dithering = gpu_scaled_dithering.value(); if (gpu_force_ntsc_timings.has_value()) g_settings.gpu_force_ntsc_timings = gpu_force_ntsc_timings.value(); if (gpu_texture_filter.has_value()) g_settings.gpu_texture_filter = gpu_texture_filter.value(); if (gpu_widescreen_hack.has_value()) g_settings.gpu_widescreen_hack = gpu_widescreen_hack.value(); if (gpu_pgxp.has_value()) g_settings.gpu_pgxp_enable = gpu_pgxp.value(); if (controller_1_type.has_value()) g_settings.controller_types[0] = controller_1_type.value(); if (controller_2_type.has_value()) g_settings.controller_types[1] = controller_2_type.value(); if (memory_card_1_type.has_value()) g_settings.memory_card_types[0] = memory_card_1_type.value(); if (!memory_card_1_shared_path.empty()) g_settings.memory_card_paths[0] = memory_card_1_shared_path; if (memory_card_2_type.has_value()) g_settings.memory_card_types[1] = memory_card_2_type.value(); if (!memory_card_1_shared_path.empty()) g_settings.memory_card_paths[1] = memory_card_2_shared_path; if (HasTrait(Trait::ForceInterpreter)) { if (display_osd_messages && g_settings.cpu_execution_mode != CPUExecutionMode::Interpreter) { g_host_interface->AddOSDMessage( g_host_interface->TranslateStdString("OSDMessage", "CPU interpreter forced by game settings."), osd_duration); } g_settings.cpu_execution_mode = CPUExecutionMode::Interpreter; } if (HasTrait(Trait::ForceSoftwareRenderer)) { if (display_osd_messages && g_settings.gpu_renderer != GPURenderer::Software) { g_host_interface->AddOSDMessage( g_host_interface->TranslateStdString("OSDMessage", "Software renderer forced by game settings."), osd_duration); } g_settings.gpu_renderer = GPURenderer::Software; } if (HasTrait(Trait::ForceInterlacing)) { if (display_osd_messages && g_settings.gpu_disable_interlacing) { g_host_interface->AddOSDMessage( g_host_interface->TranslateStdString("OSDMessage", "Interlacing forced by game settings."), osd_duration); } g_settings.gpu_disable_interlacing = false; } if (HasTrait(Trait::DisableTrueColor)) { if (display_osd_messages && g_settings.gpu_true_color) { g_host_interface->AddOSDMessage( g_host_interface->TranslateStdString("OSDMessage", "True color disabled by game settings."), osd_duration); } g_settings.gpu_true_color = false; } if (HasTrait(Trait::DisableUpscaling)) { if (display_osd_messages && g_settings.gpu_resolution_scale > 1) { g_host_interface->AddOSDMessage( g_host_interface->TranslateStdString("OSDMessage", "Upscaling disabled by game settings."), osd_duration); } g_settings.gpu_resolution_scale = 1; } if (HasTrait(Trait::DisableScaledDithering)) { if (display_osd_messages && g_settings.gpu_scaled_dithering) { g_host_interface->AddOSDMessage( g_host_interface->TranslateStdString("OSDMessage", "Scaled dithering disabled by game settings."), osd_duration); } g_settings.gpu_scaled_dithering = false; } if (HasTrait(Trait::DisableWidescreen)) { if (display_osd_messages && (g_settings.display_aspect_ratio == DisplayAspectRatio::R16_9 || g_settings.gpu_widescreen_hack)) { g_host_interface->AddOSDMessage( g_host_interface->TranslateStdString("OSDMessage", "Widescreen disabled by game settings."), osd_duration); } g_settings.display_aspect_ratio = DisplayAspectRatio::R4_3; g_settings.gpu_widescreen_hack = false; } if (HasTrait(Trait::DisableForceNTSCTimings)) { if (display_osd_messages && g_settings.gpu_force_ntsc_timings) { g_host_interface->AddOSDMessage( g_host_interface->TranslateStdString("OSDMessage", "Forcing NTSC Timings disallowed by game settings."), osd_duration); } g_settings.gpu_force_ntsc_timings = false; } if (HasTrait(Trait::DisablePGXP)) { if (display_osd_messages && g_settings.gpu_pgxp_enable) { g_host_interface->AddOSDMessage( g_host_interface->TranslateStdString("OSDMessage", "PGXP geometry correction disabled by game settings."), osd_duration); } g_settings.gpu_pgxp_enable = false; } if (HasTrait(Trait::DisablePGXPCulling)) { if (display_osd_messages && g_settings.gpu_pgxp_culling) { g_host_interface->AddOSDMessage( g_host_interface->TranslateStdString("OSDMessage", "PGXP culling disabled by game settings."), osd_duration); } g_settings.gpu_pgxp_culling = false; } if (HasTrait(Trait::DisablePGXPTextureCorrection)) { if (display_osd_messages && g_settings.gpu_pgxp_culling && g_settings.gpu_pgxp_texture_correction) { g_host_interface->AddOSDMessage( g_host_interface->TranslateStdString("OSDMessage", "PGXP texture correction disabled by game settings."), osd_duration); } g_settings.gpu_pgxp_texture_correction = false; } if (HasTrait(Trait::ForcePGXPVertexCache)) { if (display_osd_messages && g_settings.gpu_pgxp_enable && !g_settings.gpu_pgxp_vertex_cache) { g_host_interface->AddOSDMessage( g_host_interface->TranslateStdString("OSDMessage", "PGXP vertex cache forced by game settings."), osd_duration); } g_settings.gpu_pgxp_vertex_cache = true; } if (HasTrait(Trait::ForcePGXPCPUMode)) { if (display_osd_messages && g_settings.gpu_pgxp_enable && !g_settings.gpu_pgxp_cpu) { g_host_interface->AddOSDMessage( g_host_interface->TranslateStdString("OSDMessage", "PGXP CPU mode forced by game settings."), osd_duration); } g_settings.gpu_pgxp_cpu = true; } if (HasTrait(Trait::ForceDigitalController)) { for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++) { if (g_settings.controller_types[i] == ControllerType::AnalogController) { if (display_osd_messages) { g_host_interface->AddFormattedOSDMessage( osd_duration, g_host_interface->TranslateString("OSDMessage", "Controller %u changed to digital by game settings."), i + 1u); } g_settings.controller_types[i] = ControllerType::DigitalController; } } } if (HasTrait(Trait::ForceRecompilerMemoryExceptions)) { if (display_osd_messages && g_settings.cpu_execution_mode == CPUExecutionMode::Recompiler && !g_settings.cpu_recompiler_memory_exceptions) { g_host_interface->AddOSDMessage( g_host_interface->TranslateStdString("OSDMessage", "Recompiler memory exceptions forced by game settings."), osd_duration); } g_settings.cpu_recompiler_memory_exceptions = true; } if (HasTrait(Trait::ForceRecompilerICache)) { if (display_osd_messages && g_settings.cpu_execution_mode != CPUExecutionMode::Interpreter && !g_settings.cpu_recompiler_icache) { g_host_interface->AddOSDMessage( g_host_interface->TranslateStdString("OSDMessage", "Recompiler ICache forced by game settings."), osd_duration); } g_settings.cpu_recompiler_icache = true; } } } // namespace GameSettings