1 // Copyright 2008 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "Core/ConfigManager.h"
6 
7 #include <cinttypes>
8 #include <climits>
9 #include <memory>
10 #include <optional>
11 #include <sstream>
12 #include <variant>
13 
14 #include <fmt/format.h>
15 
16 #include "AudioCommon/AudioCommon.h"
17 
18 #include "Common/Assert.h"
19 #include "Common/CDUtils.h"
20 #include "Common/CommonPaths.h"
21 #include "Common/CommonTypes.h"
22 #include "Common/Config/Config.h"
23 #include "Common/FileUtil.h"
24 #include "Common/IniFile.h"
25 #include "Common/Logging/Log.h"
26 #include "Common/MsgHandler.h"
27 #include "Common/NandPaths.h"
28 #include "Common/StringUtil.h"
29 #include "Common/scmrev.h"
30 
31 #include "Core/Analytics.h"
32 #include "Core/Boot/Boot.h"
33 #include "Core/CommonTitles.h"
34 #include "Core/Config/SYSCONFSettings.h"
35 #include "Core/ConfigLoaders/GameConfigLoader.h"
36 #include "Core/Core.h"
37 #include "Core/FifoPlayer/FifoDataFile.h"
38 #include "Core/HLE/HLE.h"
39 #include "Core/HW/DVD/DVDInterface.h"
40 #include "Core/HW/EXI/EXI_Device.h"
41 #include "Core/HW/SI/SI.h"
42 #include "Core/HW/SI/SI_Device.h"
43 #include "Core/Host.h"
44 #include "Core/IOS/ES/ES.h"
45 #include "Core/IOS/ES/Formats.h"
46 #include "Core/PatchEngine.h"
47 #include "Core/PowerPC/PPCSymbolDB.h"
48 #include "Core/PowerPC/PowerPC.h"
49 #include "Core/TitleDatabase.h"
50 #include "VideoCommon/HiresTextures.h"
51 
52 #include "DiscIO/Enums.h"
53 #include "DiscIO/Volume.h"
54 #include "DiscIO/VolumeWad.h"
55 
56 SConfig* SConfig::m_Instance;
57 
SConfig()58 SConfig::SConfig()
59 {
60   LoadDefaults();
61   // Make sure we have log manager
62   LoadSettings();
63 }
64 
Init()65 void SConfig::Init()
66 {
67   m_Instance = new SConfig;
68 }
69 
Shutdown()70 void SConfig::Shutdown()
71 {
72   delete m_Instance;
73   m_Instance = nullptr;
74 }
75 
~SConfig()76 SConfig::~SConfig()
77 {
78   SaveSettings();
79 }
80 
SaveSettings()81 void SConfig::SaveSettings()
82 {
83   NOTICE_LOG(BOOT, "Saving settings to %s", File::GetUserPath(F_DOLPHINCONFIG_IDX).c_str());
84   IniFile ini;
85   ini.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX));  // load first to not kill unknown stuff
86 
87   SaveGeneralSettings(ini);
88   SaveInterfaceSettings(ini);
89   SaveGameListSettings(ini);
90   SaveCoreSettings(ini);
91   SaveMovieSettings(ini);
92   SaveDSPSettings(ini);
93   SaveInputSettings(ini);
94   SaveFifoPlayerSettings(ini);
95   SaveBluetoothPassthroughSettings(ini);
96   SaveUSBPassthroughSettings(ini);
97   SaveAutoUpdateSettings(ini);
98   SaveJitDebugSettings(ini);
99 
100   ini.Save(File::GetUserPath(F_DOLPHINCONFIG_IDX));
101 
102   Config::Save();
103 }
104 
SaveGeneralSettings(IniFile & ini)105 void SConfig::SaveGeneralSettings(IniFile& ini)
106 {
107   IniFile::Section* general = ini.GetOrCreateSection("General");
108 
109   // General
110   general->Set("ShowLag", m_ShowLag);
111   general->Set("ShowFrameCount", m_ShowFrameCount);
112 
113   // ISO folders
114   // Clear removed folders
115   int oldPaths;
116   int numPaths = (int)m_ISOFolder.size();
117   general->Get("ISOPaths", &oldPaths, 0);
118   for (int i = numPaths; i < oldPaths; i++)
119   {
120     ini.DeleteKey("General", fmt::format("ISOPath{}", i));
121   }
122 
123   general->Set("ISOPaths", numPaths);
124   for (int i = 0; i < numPaths; i++)
125   {
126     general->Set(fmt::format("ISOPath{}", i), m_ISOFolder[i]);
127   }
128 
129   general->Set("WirelessMac", m_WirelessMac);
130 
131 #ifdef USE_GDBSTUB
132 #ifndef _WIN32
133   general->Set("GDBSocket", gdb_socket);
134 #endif
135   general->Set("GDBPort", iGDBPort);
136 #endif
137 }
138 
SaveInterfaceSettings(IniFile & ini)139 void SConfig::SaveInterfaceSettings(IniFile& ini)
140 {
141   IniFile::Section* interface = ini.GetOrCreateSection("Interface");
142 
143   interface->Set("ConfirmStop", bConfirmStop);
144   interface->Set("HideCursor", bHideCursor);
145   interface->Set("LanguageCode", m_InterfaceLanguage);
146   interface->Set("ExtendedFPSInfo", m_InterfaceExtendedFPSInfo);
147   interface->Set("ShowActiveTitle", m_show_active_title);
148   interface->Set("UseBuiltinTitleDatabase", m_use_builtin_title_database);
149   interface->Set("ThemeName", theme_name);
150   interface->Set("PauseOnFocusLost", m_PauseOnFocusLost);
151   interface->Set("DebugModeEnabled", bEnableDebugging);
152 }
153 
SaveGameListSettings(IniFile & ini)154 void SConfig::SaveGameListSettings(IniFile& ini)
155 {
156   IniFile::Section* gamelist = ini.GetOrCreateSection("GameList");
157 
158   gamelist->Set("ListDrives", m_ListDrives);
159   gamelist->Set("ListWad", m_ListWad);
160   gamelist->Set("ListElfDol", m_ListElfDol);
161   gamelist->Set("ListWii", m_ListWii);
162   gamelist->Set("ListGC", m_ListGC);
163   gamelist->Set("ListJap", m_ListJap);
164   gamelist->Set("ListPal", m_ListPal);
165   gamelist->Set("ListUsa", m_ListUsa);
166   gamelist->Set("ListAustralia", m_ListAustralia);
167   gamelist->Set("ListFrance", m_ListFrance);
168   gamelist->Set("ListGermany", m_ListGermany);
169   gamelist->Set("ListItaly", m_ListItaly);
170   gamelist->Set("ListKorea", m_ListKorea);
171   gamelist->Set("ListNetherlands", m_ListNetherlands);
172   gamelist->Set("ListRussia", m_ListRussia);
173   gamelist->Set("ListSpain", m_ListSpain);
174   gamelist->Set("ListTaiwan", m_ListTaiwan);
175   gamelist->Set("ListWorld", m_ListWorld);
176   gamelist->Set("ListUnknown", m_ListUnknown);
177   gamelist->Set("ListSort", m_ListSort);
178   gamelist->Set("ListSortSecondary", m_ListSort2);
179 
180   gamelist->Set("ColumnPlatform", m_showSystemColumn);
181   gamelist->Set("ColumnBanner", m_showBannerColumn);
182   gamelist->Set("ColumnDescription", m_showDescriptionColumn);
183   gamelist->Set("ColumnTitle", m_showTitleColumn);
184   gamelist->Set("ColumnNotes", m_showMakerColumn);
185   gamelist->Set("ColumnFileName", m_showFileNameColumn);
186   gamelist->Set("ColumnFilePath", m_showFilePathColumn);
187   gamelist->Set("ColumnID", m_showIDColumn);
188   gamelist->Set("ColumnRegion", m_showRegionColumn);
189   gamelist->Set("ColumnSize", m_showSizeColumn);
190   gamelist->Set("ColumnFileFormat", m_showFileFormatColumn);
191   gamelist->Set("ColumnBlockSize", m_showBlockSizeColumn);
192   gamelist->Set("ColumnCompression", m_showCompressionColumn);
193   gamelist->Set("ColumnTags", m_showTagsColumn);
194 }
195 
SaveCoreSettings(IniFile & ini)196 void SConfig::SaveCoreSettings(IniFile& ini)
197 {
198   IniFile::Section* core = ini.GetOrCreateSection("Core");
199 
200   core->Set("SkipIPL", bHLE_BS2);
201   core->Set("TimingVariance", iTimingVariance);
202   core->Set("CPUCore", cpu_core);
203   core->Set("Fastmem", bFastmem);
204   core->Set("CPUThread", bCPUThread);
205   core->Set("DSPHLE", bDSPHLE);
206   core->Set("SyncOnSkipIdle", bSyncGPUOnSkipIdleHack);
207   core->Set("SyncGPU", bSyncGPU);
208   core->Set("SyncGpuMaxDistance", iSyncGpuMaxDistance);
209   core->Set("SyncGpuMinDistance", iSyncGpuMinDistance);
210   core->Set("SyncGpuOverclock", fSyncGpuOverclock);
211   core->Set("FPRF", bFPRF);
212   core->Set("AccurateNaNs", bAccurateNaNs);
213   core->Set("EnableCheats", bEnableCheats);
214   core->Set("SelectedLanguage", SelectedLanguage);
215   core->Set("OverrideRegionSettings", bOverrideRegionSettings);
216   core->Set("DPL2Decoder", bDPL2Decoder);
217   core->Set("AudioLatency", iLatency);
218   core->Set("AudioStretch", m_audio_stretch);
219   core->Set("AudioStretchMaxLatency", m_audio_stretch_max_latency);
220   core->Set("AgpCartAPath", m_strGbaCartA);
221   core->Set("AgpCartBPath", m_strGbaCartB);
222   core->Set("SlotA", m_EXIDevice[0]);
223   core->Set("SlotB", m_EXIDevice[1]);
224   core->Set("SerialPort1", m_EXIDevice[2]);
225   core->Set("BBA_MAC", m_bba_mac);
226   core->Set("BBA_XLINK_IP", m_bba_xlink_ip);
227   core->Set("BBA_XLINK_CHAT_OSD", m_bba_xlink_chat_osd);
228   for (int i = 0; i < SerialInterface::MAX_SI_CHANNELS; ++i)
229   {
230     core->Set(fmt::format("SIDevice{}", i), m_SIDevice[i]);
231     core->Set(fmt::format("AdapterRumble{}", i), m_AdapterRumble[i]);
232     core->Set(fmt::format("SimulateKonga{}", i), m_AdapterKonga[i]);
233   }
234   core->Set("WiiSDCard", m_WiiSDCard);
235   core->Set("WiiKeyboard", m_WiiKeyboard);
236   core->Set("WiimoteContinuousScanning", m_WiimoteContinuousScanning);
237   core->Set("WiimoteEnableSpeaker", m_WiimoteEnableSpeaker);
238   core->Set("WiimoteControllerInterface", connect_wiimotes_for_ciface);
239   core->Set("RunCompareServer", bRunCompareServer);
240   core->Set("RunCompareClient", bRunCompareClient);
241   core->Set("MMU", bMMU);
242   core->Set("EmulationSpeed", m_EmulationSpeed);
243   core->Set("Overclock", m_OCFactor);
244   core->Set("OverclockEnable", m_OCEnable);
245   core->Set("GPUDeterminismMode", m_strGPUDeterminismMode);
246   core->Set("PerfMapDir", m_perfDir);
247   core->Set("EnableCustomRTC", bEnableCustomRTC);
248   core->Set("CustomRTCValue", m_customRTCValue);
249 }
250 
SaveMovieSettings(IniFile & ini)251 void SConfig::SaveMovieSettings(IniFile& ini)
252 {
253   IniFile::Section* movie = ini.GetOrCreateSection("Movie");
254 
255   movie->Set("PauseMovie", m_PauseMovie);
256   movie->Set("Author", m_strMovieAuthor);
257   movie->Set("DumpFrames", m_DumpFrames);
258   movie->Set("DumpFramesSilent", m_DumpFramesSilent);
259   movie->Set("ShowInputDisplay", m_ShowInputDisplay);
260   movie->Set("ShowRTC", m_ShowRTC);
261 }
262 
SaveDSPSettings(IniFile & ini)263 void SConfig::SaveDSPSettings(IniFile& ini)
264 {
265   IniFile::Section* dsp = ini.GetOrCreateSection("DSP");
266 
267   dsp->Set("EnableJIT", m_DSPEnableJIT);
268   dsp->Set("DumpAudio", m_DumpAudio);
269   dsp->Set("DumpAudioSilent", m_DumpAudioSilent);
270   dsp->Set("DumpUCode", m_DumpUCode);
271   dsp->Set("Backend", sBackend);
272   dsp->Set("Volume", m_Volume);
273   dsp->Set("CaptureLog", m_DSPCaptureLog);
274 
275 #ifdef _WIN32
276   dsp->Set("WASAPIDevice", sWASAPIDevice);
277 #endif
278 }
279 
SaveInputSettings(IniFile & ini)280 void SConfig::SaveInputSettings(IniFile& ini)
281 {
282   IniFile::Section* input = ini.GetOrCreateSection("Input");
283 
284   input->Set("BackgroundInput", m_BackgroundInput);
285 }
286 
SaveFifoPlayerSettings(IniFile & ini)287 void SConfig::SaveFifoPlayerSettings(IniFile& ini)
288 {
289   IniFile::Section* fifoplayer = ini.GetOrCreateSection("FifoPlayer");
290 
291   fifoplayer->Set("LoopReplay", bLoopFifoReplay);
292 }
293 
SaveBluetoothPassthroughSettings(IniFile & ini)294 void SConfig::SaveBluetoothPassthroughSettings(IniFile& ini)
295 {
296   IniFile::Section* section = ini.GetOrCreateSection("BluetoothPassthrough");
297 
298   section->Set("Enabled", m_bt_passthrough_enabled);
299   section->Set("VID", m_bt_passthrough_vid);
300   section->Set("PID", m_bt_passthrough_pid);
301   section->Set("LinkKeys", m_bt_passthrough_link_keys);
302 }
303 
SaveUSBPassthroughSettings(IniFile & ini)304 void SConfig::SaveUSBPassthroughSettings(IniFile& ini)
305 {
306   IniFile::Section* section = ini.GetOrCreateSection("USBPassthrough");
307 
308   std::ostringstream oss;
309   for (const auto& device : m_usb_passthrough_devices)
310     oss << fmt::format("{:04x}:{:04x}", device.first, device.second) << ',';
311   std::string devices_string = oss.str();
312   if (!devices_string.empty())
313     devices_string.pop_back();
314 
315   section->Set("Devices", devices_string);
316 }
317 
SaveAutoUpdateSettings(IniFile & ini)318 void SConfig::SaveAutoUpdateSettings(IniFile& ini)
319 {
320   IniFile::Section* section = ini.GetOrCreateSection("AutoUpdate");
321 
322   section->Set("UpdateTrack", m_auto_update_track);
323   section->Set("HashOverride", m_auto_update_hash_override);
324 }
325 
SaveJitDebugSettings(IniFile & ini)326 void SConfig::SaveJitDebugSettings(IniFile& ini)
327 {
328   IniFile::Section* section = ini.GetOrCreateSection("Debug");
329 
330   section->Set("JitOff", bJITOff);
331   section->Set("JitLoadStoreOff", bJITLoadStoreOff);
332   section->Set("JitLoadStoreFloatingOff", bJITLoadStoreFloatingOff);
333   section->Set("JitLoadStorePairedOff", bJITLoadStorePairedOff);
334   section->Set("JitFloatingPointOff", bJITFloatingPointOff);
335   section->Set("JitIntegerOff", bJITIntegerOff);
336   section->Set("JitPairedOff", bJITPairedOff);
337   section->Set("JitSystemRegistersOff", bJITSystemRegistersOff);
338   section->Set("JitBranchOff", bJITBranchOff);
339   section->Set("JitRegisterCacheOff", bJITRegisterCacheOff);
340 }
341 
LoadSettings()342 void SConfig::LoadSettings()
343 {
344   Config::Load();
345 
346   INFO_LOG(BOOT, "Loading Settings from %s", File::GetUserPath(F_DOLPHINCONFIG_IDX).c_str());
347   IniFile ini;
348   ini.Load(File::GetUserPath(F_DOLPHINCONFIG_IDX));
349 
350   LoadGeneralSettings(ini);
351   LoadInterfaceSettings(ini);
352   LoadGameListSettings(ini);
353   LoadCoreSettings(ini);
354   LoadMovieSettings(ini);
355   LoadDSPSettings(ini);
356   LoadInputSettings(ini);
357   LoadFifoPlayerSettings(ini);
358   LoadBluetoothPassthroughSettings(ini);
359   LoadUSBPassthroughSettings(ini);
360   LoadAutoUpdateSettings(ini);
361   LoadJitDebugSettings(ini);
362 }
363 
LoadGeneralSettings(IniFile & ini)364 void SConfig::LoadGeneralSettings(IniFile& ini)
365 {
366   IniFile::Section* general = ini.GetOrCreateSection("General");
367 
368   general->Get("ShowLag", &m_ShowLag, false);
369   general->Get("ShowFrameCount", &m_ShowFrameCount, false);
370 #ifdef USE_GDBSTUB
371 #ifndef _WIN32
372   general->Get("GDBSocket", &gdb_socket, "");
373 #endif
374   general->Get("GDBPort", &(iGDBPort), -1);
375 #endif
376 
377   m_ISOFolder.clear();
378   int numISOPaths;
379 
380   if (general->Get("ISOPaths", &numISOPaths, 0))
381   {
382     for (int i = 0; i < numISOPaths; i++)
383     {
384       std::string tmpPath;
385       general->Get(fmt::format("ISOPath{}", i), &tmpPath, "");
386       m_ISOFolder.push_back(std::move(tmpPath));
387     }
388   }
389 
390   general->Get("WirelessMac", &m_WirelessMac);
391 }
392 
LoadInterfaceSettings(IniFile & ini)393 void SConfig::LoadInterfaceSettings(IniFile& ini)
394 {
395   IniFile::Section* interface = ini.GetOrCreateSection("Interface");
396 
397   interface->Get("ConfirmStop", &bConfirmStop, true);
398   interface->Get("HideCursor", &bHideCursor, false);
399   interface->Get("LanguageCode", &m_InterfaceLanguage, "");
400   interface->Get("ExtendedFPSInfo", &m_InterfaceExtendedFPSInfo, false);
401   interface->Get("ShowActiveTitle", &m_show_active_title, true);
402   interface->Get("UseBuiltinTitleDatabase", &m_use_builtin_title_database, true);
403   interface->Get("ThemeName", &theme_name, DEFAULT_THEME_DIR);
404   interface->Get("PauseOnFocusLost", &m_PauseOnFocusLost, false);
405   interface->Get("DebugModeEnabled", &bEnableDebugging, false);
406 }
407 
LoadGameListSettings(IniFile & ini)408 void SConfig::LoadGameListSettings(IniFile& ini)
409 {
410   IniFile::Section* gamelist = ini.GetOrCreateSection("GameList");
411 
412   gamelist->Get("ListDrives", &m_ListDrives, false);
413   gamelist->Get("ListWad", &m_ListWad, true);
414   gamelist->Get("ListElfDol", &m_ListElfDol, true);
415   gamelist->Get("ListWii", &m_ListWii, true);
416   gamelist->Get("ListGC", &m_ListGC, true);
417   gamelist->Get("ListJap", &m_ListJap, true);
418   gamelist->Get("ListPal", &m_ListPal, true);
419   gamelist->Get("ListUsa", &m_ListUsa, true);
420 
421   gamelist->Get("ListAustralia", &m_ListAustralia, true);
422   gamelist->Get("ListFrance", &m_ListFrance, true);
423   gamelist->Get("ListGermany", &m_ListGermany, true);
424   gamelist->Get("ListItaly", &m_ListItaly, true);
425   gamelist->Get("ListKorea", &m_ListKorea, true);
426   gamelist->Get("ListNetherlands", &m_ListNetherlands, true);
427   gamelist->Get("ListRussia", &m_ListRussia, true);
428   gamelist->Get("ListSpain", &m_ListSpain, true);
429   gamelist->Get("ListTaiwan", &m_ListTaiwan, true);
430   gamelist->Get("ListWorld", &m_ListWorld, true);
431   gamelist->Get("ListUnknown", &m_ListUnknown, true);
432   gamelist->Get("ListSort", &m_ListSort, 3);
433   gamelist->Get("ListSortSecondary", &m_ListSort2, 0);
434 
435   // Gamelist columns toggles
436   gamelist->Get("ColumnPlatform", &m_showSystemColumn, true);
437   gamelist->Get("ColumnDescription", &m_showDescriptionColumn, false);
438   gamelist->Get("ColumnBanner", &m_showBannerColumn, true);
439   gamelist->Get("ColumnTitle", &m_showTitleColumn, true);
440   gamelist->Get("ColumnNotes", &m_showMakerColumn, true);
441   gamelist->Get("ColumnFileName", &m_showFileNameColumn, false);
442   gamelist->Get("ColumnFilePath", &m_showFilePathColumn, false);
443   gamelist->Get("ColumnID", &m_showIDColumn, false);
444   gamelist->Get("ColumnRegion", &m_showRegionColumn, true);
445   gamelist->Get("ColumnSize", &m_showSizeColumn, true);
446   gamelist->Get("ColumnFileFormat", &m_showFileFormatColumn, false);
447   gamelist->Get("ColumnBlockSize", &m_showBlockSizeColumn, false);
448   gamelist->Get("ColumnCompression", &m_showCompressionColumn, false);
449   gamelist->Get("ColumnTags", &m_showTagsColumn, false);
450 }
451 
LoadCoreSettings(IniFile & ini)452 void SConfig::LoadCoreSettings(IniFile& ini)
453 {
454   IniFile::Section* core = ini.GetOrCreateSection("Core");
455 
456   core->Get("SkipIPL", &bHLE_BS2, true);
457 #ifdef _M_X86
458   core->Get("CPUCore", &cpu_core, PowerPC::CPUCore::JIT64);
459 #elif _M_ARM_64
460   core->Get("CPUCore", &cpu_core, PowerPC::CPUCore::JITARM64);
461 #else
462   core->Get("CPUCore", &cpu_core, PowerPC::CPUCore::Interpreter);
463 #endif
464   core->Get("JITFollowBranch", &bJITFollowBranch, true);
465   core->Get("Fastmem", &bFastmem, true);
466   core->Get("DSPHLE", &bDSPHLE, true);
467   core->Get("TimingVariance", &iTimingVariance, 40);
468   core->Get("CPUThread", &bCPUThread, true);
469   core->Get("SyncOnSkipIdle", &bSyncGPUOnSkipIdleHack, true);
470   core->Get("EnableCheats", &bEnableCheats, false);
471   core->Get("SelectedLanguage", &SelectedLanguage, 0);
472   core->Get("OverrideRegionSettings", &bOverrideRegionSettings, false);
473   core->Get("DPL2Decoder", &bDPL2Decoder, false);
474   core->Get("AudioLatency", &iLatency, 20);
475   core->Get("AudioStretch", &m_audio_stretch, false);
476   core->Get("AudioStretchMaxLatency", &m_audio_stretch_max_latency, 80);
477   core->Get("AgpCartAPath", &m_strGbaCartA);
478   core->Get("AgpCartBPath", &m_strGbaCartB);
479   core->Get("SlotA", (int*)&m_EXIDevice[0], ExpansionInterface::EXIDEVICE_MEMORYCARDFOLDER);
480   core->Get("SlotB", (int*)&m_EXIDevice[1], ExpansionInterface::EXIDEVICE_NONE);
481   core->Get("SerialPort1", (int*)&m_EXIDevice[2], ExpansionInterface::EXIDEVICE_NONE);
482   core->Get("BBA_MAC", &m_bba_mac);
483   core->Get("BBA_XLINK_IP", &m_bba_xlink_ip, "127.0.0.1");
484   core->Get("BBA_XLINK_CHAT_OSD", &m_bba_xlink_chat_osd, true);
485   for (size_t i = 0; i < std::size(m_SIDevice); ++i)
486   {
487     core->Get(fmt::format("SIDevice{}", i), &m_SIDevice[i],
488               (i == 0) ? SerialInterface::SIDEVICE_GC_CONTROLLER : SerialInterface::SIDEVICE_NONE);
489     core->Get(fmt::format("AdapterRumble{}", i), &m_AdapterRumble[i], true);
490     core->Get(fmt::format("SimulateKonga{}", i), &m_AdapterKonga[i], false);
491   }
492   core->Get("WiiSDCard", &m_WiiSDCard, true);
493   core->Get("WiiKeyboard", &m_WiiKeyboard, false);
494   core->Get("WiimoteContinuousScanning", &m_WiimoteContinuousScanning, false);
495   core->Get("WiimoteEnableSpeaker", &m_WiimoteEnableSpeaker, false);
496   core->Get("WiimoteControllerInterface", &connect_wiimotes_for_ciface, false);
497   core->Get("RunCompareServer", &bRunCompareServer, false);
498   core->Get("RunCompareClient", &bRunCompareClient, false);
499   core->Get("MMU", &bMMU, bMMU);
500   core->Get("BBDumpPort", &iBBDumpPort, -1);
501   core->Get("SyncGPU", &bSyncGPU, false);
502   core->Get("SyncGpuMaxDistance", &iSyncGpuMaxDistance, 200000);
503   core->Get("SyncGpuMinDistance", &iSyncGpuMinDistance, -200000);
504   core->Get("SyncGpuOverclock", &fSyncGpuOverclock, 1.0f);
505   core->Get("FastDiscSpeed", &bFastDiscSpeed, false);
506   core->Get("LowDCBZHack", &bLowDCBZHack, false);
507   core->Get("FPRF", &bFPRF, false);
508   core->Get("AccurateNaNs", &bAccurateNaNs, false);
509   core->Get("EmulationSpeed", &m_EmulationSpeed, 1.0f);
510   core->Get("Overclock", &m_OCFactor, 1.0f);
511   core->Get("OverclockEnable", &m_OCEnable, false);
512   core->Get("GPUDeterminismMode", &m_strGPUDeterminismMode, "auto");
513   core->Get("PerfMapDir", &m_perfDir, "");
514   core->Get("EnableCustomRTC", &bEnableCustomRTC, false);
515   // Default to seconds between 1.1.1970 and 1.1.2000
516   core->Get("CustomRTCValue", &m_customRTCValue, 946684800);
517 }
518 
LoadMovieSettings(IniFile & ini)519 void SConfig::LoadMovieSettings(IniFile& ini)
520 {
521   IniFile::Section* movie = ini.GetOrCreateSection("Movie");
522 
523   movie->Get("PauseMovie", &m_PauseMovie, false);
524   movie->Get("Author", &m_strMovieAuthor, "");
525   movie->Get("DumpFrames", &m_DumpFrames, false);
526   movie->Get("DumpFramesSilent", &m_DumpFramesSilent, false);
527   movie->Get("ShowInputDisplay", &m_ShowInputDisplay, false);
528   movie->Get("ShowRTC", &m_ShowRTC, false);
529 }
530 
LoadDSPSettings(IniFile & ini)531 void SConfig::LoadDSPSettings(IniFile& ini)
532 {
533   IniFile::Section* dsp = ini.GetOrCreateSection("DSP");
534 
535   dsp->Get("EnableJIT", &m_DSPEnableJIT, true);
536   dsp->Get("DumpAudio", &m_DumpAudio, false);
537   dsp->Get("DumpAudioSilent", &m_DumpAudioSilent, false);
538   dsp->Get("DumpUCode", &m_DumpUCode, false);
539   dsp->Get("Backend", &sBackend, AudioCommon::GetDefaultSoundBackend());
540   dsp->Get("Volume", &m_Volume, 100);
541   dsp->Get("CaptureLog", &m_DSPCaptureLog, false);
542 
543 #ifdef _WIN32
544   dsp->Get("WASAPIDevice", &sWASAPIDevice, "default");
545 #endif
546 
547   m_IsMuted = false;
548 }
549 
LoadInputSettings(IniFile & ini)550 void SConfig::LoadInputSettings(IniFile& ini)
551 {
552   IniFile::Section* input = ini.GetOrCreateSection("Input");
553 
554   input->Get("BackgroundInput", &m_BackgroundInput, false);
555 }
556 
LoadFifoPlayerSettings(IniFile & ini)557 void SConfig::LoadFifoPlayerSettings(IniFile& ini)
558 {
559   IniFile::Section* fifoplayer = ini.GetOrCreateSection("FifoPlayer");
560 
561   fifoplayer->Get("LoopReplay", &bLoopFifoReplay, true);
562 }
563 
LoadBluetoothPassthroughSettings(IniFile & ini)564 void SConfig::LoadBluetoothPassthroughSettings(IniFile& ini)
565 {
566   IniFile::Section* section = ini.GetOrCreateSection("BluetoothPassthrough");
567 
568   section->Get("Enabled", &m_bt_passthrough_enabled, false);
569   section->Get("VID", &m_bt_passthrough_vid, -1);
570   section->Get("PID", &m_bt_passthrough_pid, -1);
571   section->Get("LinkKeys", &m_bt_passthrough_link_keys, "");
572 }
573 
LoadUSBPassthroughSettings(IniFile & ini)574 void SConfig::LoadUSBPassthroughSettings(IniFile& ini)
575 {
576   IniFile::Section* section = ini.GetOrCreateSection("USBPassthrough");
577   m_usb_passthrough_devices.clear();
578   std::string devices_string;
579   section->Get("Devices", &devices_string, "");
580   for (const auto& pair : SplitString(devices_string, ','))
581   {
582     const auto index = pair.find(':');
583     if (index == std::string::npos)
584       continue;
585 
586     const u16 vid = static_cast<u16>(strtol(pair.substr(0, index).c_str(), nullptr, 16));
587     const u16 pid = static_cast<u16>(strtol(pair.substr(index + 1).c_str(), nullptr, 16));
588     if (vid && pid)
589       m_usb_passthrough_devices.emplace(vid, pid);
590   }
591 }
592 
LoadAutoUpdateSettings(IniFile & ini)593 void SConfig::LoadAutoUpdateSettings(IniFile& ini)
594 {
595   IniFile::Section* section = ini.GetOrCreateSection("AutoUpdate");
596 
597   section->Get("UpdateTrack", &m_auto_update_track, SCM_UPDATE_TRACK_STR);
598   section->Get("HashOverride", &m_auto_update_hash_override, "");
599 }
600 
LoadJitDebugSettings(IniFile & ini)601 void SConfig::LoadJitDebugSettings(IniFile& ini)
602 {
603   IniFile::Section* section = ini.GetOrCreateSection("Debug");
604   section->Get("JitOff", &bJITOff, false);
605   section->Get("JitLoadStoreOff", &bJITLoadStoreOff, false);
606   section->Get("JitLoadStoreFloatingOff", &bJITLoadStoreFloatingOff, false);
607   section->Get("JitLoadStorePairedOff", &bJITLoadStorePairedOff, false);
608   section->Get("JitFloatingPointOff", &bJITFloatingPointOff, false);
609   section->Get("JitIntegerOff", &bJITIntegerOff, false);
610   section->Get("JitPairedOff", &bJITPairedOff, false);
611   section->Get("JitSystemRegistersOff", &bJITSystemRegistersOff, false);
612   section->Get("JitBranchOff", &bJITBranchOff, false);
613   section->Get("JitRegisterCacheOff", &bJITRegisterCacheOff, false);
614 }
615 
ResetRunningGameMetadata()616 void SConfig::ResetRunningGameMetadata()
617 {
618   SetRunningGameMetadata("00000000", "", 0, 0, DiscIO::Region::Unknown);
619 }
620 
SetRunningGameMetadata(const DiscIO::Volume & volume,const DiscIO::Partition & partition)621 void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume,
622                                      const DiscIO::Partition& partition)
623 {
624   if (partition == volume.GetGamePartition())
625   {
626     SetRunningGameMetadata(volume.GetGameID(), volume.GetGameTDBID(),
627                            volume.GetTitleID().value_or(0), volume.GetRevision().value_or(0),
628                            volume.GetRegion());
629   }
630   else
631   {
632     SetRunningGameMetadata(volume.GetGameID(partition), volume.GetGameTDBID(),
633                            volume.GetTitleID(partition).value_or(0),
634                            volume.GetRevision(partition).value_or(0), volume.GetRegion());
635   }
636 }
637 
SetRunningGameMetadata(const IOS::ES::TMDReader & tmd,DiscIO::Platform platform)638 void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Platform platform)
639 {
640   const u64 tmd_title_id = tmd.GetTitleId();
641 
642   // If we're launching a disc game, we want to read the revision from
643   // the disc header instead of the TMD. They can differ.
644   // (IOS HLE ES calls us with a TMDReader rather than a volume when launching
645   // a disc game, because ES has no reason to be accessing the disc directly.)
646   if (platform == DiscIO::Platform::WiiWAD ||
647       !DVDInterface::UpdateRunningGameMetadata(tmd_title_id))
648   {
649     // If not launching a disc game, just read everything from the TMD.
650     SetRunningGameMetadata(tmd.GetGameID(), tmd.GetGameTDBID(), tmd_title_id, tmd.GetTitleVersion(),
651                            tmd.GetRegion());
652   }
653 }
654 
SetRunningGameMetadata(const std::string & game_id,const std::string & gametdb_id,u64 title_id,u16 revision,DiscIO::Region region)655 void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id,
656                                      u64 title_id, u16 revision, DiscIO::Region region)
657 {
658   const bool was_changed = m_game_id != game_id || m_gametdb_id != gametdb_id ||
659                            m_title_id != title_id || m_revision != revision;
660   m_game_id = game_id;
661   m_gametdb_id = gametdb_id;
662   m_title_id = title_id;
663   m_revision = revision;
664 
665   if (game_id.length() == 6)
666   {
667     m_debugger_game_id = game_id;
668   }
669   else if (title_id != 0)
670   {
671     m_debugger_game_id =
672         fmt::format("{:08X}_{:08X}", static_cast<u32>(title_id >> 32), static_cast<u32>(title_id));
673   }
674   else
675   {
676     m_debugger_game_id.clear();
677   }
678 
679   if (!was_changed)
680     return;
681 
682   if (game_id == "00000000")
683   {
684     m_title_description.clear();
685     return;
686   }
687 
688   const Core::TitleDatabase title_database;
689   const DiscIO::Language language = GetLanguageAdjustedForRegion(bWii, region);
690   m_title_description = title_database.Describe(m_gametdb_id, language);
691   NOTICE_LOG(CORE, "Active title: %s", m_title_description.c_str());
692   Host_TitleChanged();
693 
694   Config::AddLayer(ConfigLoaders::GenerateGlobalGameConfigLoader(game_id, revision));
695   Config::AddLayer(ConfigLoaders::GenerateLocalGameConfigLoader(game_id, revision));
696 
697   if (Core::IsRunning())
698   {
699     // TODO: have a callback mechanism for title changes?
700     if (!g_symbolDB.IsEmpty())
701     {
702       g_symbolDB.Clear();
703       Host_NotifyMapLoaded();
704     }
705     CBoot::LoadMapFromFilename();
706     HLE::Reload();
707     PatchEngine::Reload();
708     HiresTexture::Update();
709     DolphinAnalytics::Instance().ReportGameStart();
710   }
711 }
712 
LoadDefaults()713 void SConfig::LoadDefaults()
714 {
715   bEnableDebugging = false;
716   bAutomaticStart = false;
717   bBootToPause = false;
718 
719 #ifdef USE_GDBSTUB
720   iGDBPort = -1;
721 #ifndef _WIN32
722   gdb_socket = "";
723 #endif
724 #endif
725 
726   cpu_core = PowerPC::DefaultCPUCore();
727   iTimingVariance = 40;
728   bCPUThread = false;
729   bSyncGPUOnSkipIdleHack = true;
730   bRunCompareServer = false;
731   bDSPHLE = true;
732   bFastmem = true;
733   bFPRF = false;
734   bAccurateNaNs = false;
735   bMMU = false;
736   bLowDCBZHack = false;
737   iBBDumpPort = -1;
738   bSyncGPU = false;
739   bFastDiscSpeed = false;
740   bEnableMemcardSdWriting = true;
741   SelectedLanguage = 0;
742   bOverrideRegionSettings = false;
743   bWii = false;
744   bDPL2Decoder = false;
745   iLatency = 20;
746   m_audio_stretch = false;
747   m_audio_stretch_max_latency = 80;
748 
749   bLoopFifoReplay = true;
750 
751   bJITOff = false;  // debugger only settings
752   bJITLoadStoreOff = false;
753   bJITLoadStoreFloatingOff = false;
754   bJITLoadStorePairedOff = false;
755   bJITFloatingPointOff = false;
756   bJITIntegerOff = false;
757   bJITPairedOff = false;
758   bJITSystemRegistersOff = false;
759   bJITBranchOff = false;
760   bJITRegisterCacheOff = false;
761 
762   ResetRunningGameMetadata();
763 }
764 
IsUSBDeviceWhitelisted(const std::pair<u16,u16> vid_pid) const765 bool SConfig::IsUSBDeviceWhitelisted(const std::pair<u16, u16> vid_pid) const
766 {
767   return m_usb_passthrough_devices.find(vid_pid) != m_usb_passthrough_devices.end();
768 }
769 
770 // The reason we need this function is because some memory card code
771 // expects to get a non-NTSC-K region even if we're emulating an NTSC-K Wii.
ToGameCubeRegion(DiscIO::Region region)772 DiscIO::Region SConfig::ToGameCubeRegion(DiscIO::Region region)
773 {
774   if (region != DiscIO::Region::NTSC_K)
775     return region;
776 
777   // GameCube has no NTSC-K region. No choice of replacement value is completely
778   // non-arbitrary, but let's go with NTSC-J since Korean GameCubes are NTSC-J.
779   return DiscIO::Region::NTSC_J;
780 }
781 
GetDirectoryForRegion(DiscIO::Region region)782 const char* SConfig::GetDirectoryForRegion(DiscIO::Region region)
783 {
784   if (region == DiscIO::Region::Unknown)
785     region = ToGameCubeRegion(GetFallbackRegion());
786 
787   switch (region)
788   {
789   case DiscIO::Region::NTSC_J:
790     return JAP_DIR;
791 
792   case DiscIO::Region::NTSC_U:
793     return USA_DIR;
794 
795   case DiscIO::Region::PAL:
796     return EUR_DIR;
797 
798   case DiscIO::Region::NTSC_K:
799     ASSERT_MSG(BOOT, false, "NTSC-K is not a valid GameCube region");
800     return JAP_DIR;  // See ToGameCubeRegion
801 
802   default:
803     ASSERT_MSG(BOOT, false, "Default case should not be reached");
804     return EUR_DIR;
805   }
806 }
807 
GetBootROMPath(const std::string & region_directory) const808 std::string SConfig::GetBootROMPath(const std::string& region_directory) const
809 {
810   const std::string path =
811       File::GetUserPath(D_GCUSER_IDX) + DIR_SEP + region_directory + DIR_SEP GC_IPL;
812   if (!File::Exists(path))
813     return File::GetSysDirectory() + GC_SYS_DIR + DIR_SEP + region_directory + DIR_SEP GC_IPL;
814   return path;
815 }
816 
817 struct SetGameMetadata
818 {
SetGameMetadataSetGameMetadata819   SetGameMetadata(SConfig* config_, DiscIO::Region* region_) : config(config_), region(region_) {}
operator ()SetGameMetadata820   bool operator()(const BootParameters::Disc& disc) const
821   {
822     config->SetRunningGameMetadata(*disc.volume, disc.volume->GetGamePartition());
823     config->bWii = disc.volume->GetVolumeType() == DiscIO::Platform::WiiDisc;
824     config->m_disc_booted_from_game_list = true;
825     *region = disc.volume->GetRegion();
826     return true;
827   }
828 
operator ()SetGameMetadata829   bool operator()(const BootParameters::Executable& executable) const
830   {
831     if (!executable.reader->IsValid())
832       return false;
833 
834     config->bWii = executable.reader->IsWii();
835 
836     *region = DiscIO::Region::Unknown;
837 
838     // Strip the .elf/.dol file extension and directories before the name
839     SplitPath(executable.path, nullptr, &config->m_debugger_game_id, nullptr);
840     return true;
841   }
842 
operator ()SetGameMetadata843   bool operator()(const DiscIO::VolumeWAD& wad) const
844   {
845     if (!wad.GetTMD().IsValid())
846     {
847       PanicAlertT("This WAD is not valid.");
848       return false;
849     }
850     if (!IOS::ES::IsChannel(wad.GetTMD().GetTitleId()))
851     {
852       PanicAlertT("This WAD is not bootable.");
853       return false;
854     }
855 
856     const IOS::ES::TMDReader& tmd = wad.GetTMD();
857     config->SetRunningGameMetadata(tmd, DiscIO::Platform::WiiWAD);
858     config->bWii = true;
859     *region = tmd.GetRegion();
860     return true;
861   }
862 
operator ()SetGameMetadata863   bool operator()(const BootParameters::NANDTitle& nand_title) const
864   {
865     IOS::HLE::Kernel ios;
866     const IOS::ES::TMDReader tmd = ios.GetES()->FindInstalledTMD(nand_title.id);
867     if (!tmd.IsValid() || !IOS::ES::IsChannel(nand_title.id))
868     {
869       PanicAlertT("This title cannot be booted.");
870       return false;
871     }
872     config->SetRunningGameMetadata(tmd, DiscIO::Platform::WiiWAD);
873     config->bWii = true;
874     *region = tmd.GetRegion();
875     return true;
876   }
877 
operator ()SetGameMetadata878   bool operator()(const BootParameters::IPL& ipl) const
879   {
880     config->bWii = false;
881     *region = ipl.region;
882     return true;
883   }
884 
operator ()SetGameMetadata885   bool operator()(const BootParameters::DFF& dff) const
886   {
887     std::unique_ptr<FifoDataFile> dff_file(FifoDataFile::Load(dff.dff_path, true));
888     if (!dff_file)
889       return false;
890 
891     config->bWii = dff_file->GetIsWii();
892     *region = DiscIO::Region::NTSC_U;
893     return true;
894   }
895 
896 private:
897   SConfig* config;
898   DiscIO::Region* region;
899 };
900 
SetPathsAndGameMetadata(const BootParameters & boot)901 bool SConfig::SetPathsAndGameMetadata(const BootParameters& boot)
902 {
903   m_is_mios = false;
904   m_disc_booted_from_game_list = false;
905   if (!std::visit(SetGameMetadata(this, &m_region), boot.parameters))
906     return false;
907 
908   if (m_region == DiscIO::Region::Unknown)
909     m_region = GetFallbackRegion();
910 
911   // Set up paths
912   const std::string region_dir = GetDirectoryForRegion(ToGameCubeRegion(m_region));
913   m_strSRAM = File::GetUserPath(F_GCSRAM_IDX);
914   m_strBootROM = GetBootROMPath(region_dir);
915 
916   return true;
917 }
918 
GetFallbackRegion()919 DiscIO::Region SConfig::GetFallbackRegion()
920 {
921   // Fall back to the system menu region, if possible.
922   IOS::HLE::Kernel ios;
923   const IOS::ES::TMDReader system_menu_tmd = ios.GetES()->FindInstalledTMD(Titles::SYSTEM_MENU);
924   if (system_menu_tmd.IsValid())
925   {
926     const DiscIO::Region region = system_menu_tmd.GetRegion();
927     if (region != DiscIO::Region::Unknown)
928       return region;
929   }
930 
931   // Fall back to PAL.
932   return DiscIO::Region::PAL;
933 }
934 
GetCurrentLanguage(bool wii) const935 DiscIO::Language SConfig::GetCurrentLanguage(bool wii) const
936 {
937   int language_value;
938   if (wii)
939     language_value = Config::Get(Config::SYSCONF_LANGUAGE);
940   else
941     language_value = SConfig::GetInstance().SelectedLanguage + 1;
942   DiscIO::Language language = static_cast<DiscIO::Language>(language_value);
943 
944   // Get rid of invalid values (probably doesn't matter, but might as well do it)
945   if (language > DiscIO::Language::Unknown || language < DiscIO::Language::Japanese)
946     language = DiscIO::Language::Unknown;
947   return language;
948 }
949 
GetLanguageAdjustedForRegion(bool wii,DiscIO::Region region) const950 DiscIO::Language SConfig::GetLanguageAdjustedForRegion(bool wii, DiscIO::Region region) const
951 {
952   const DiscIO::Language language = GetCurrentLanguage(wii);
953 
954   if (!wii && region == DiscIO::Region::NTSC_K)
955     region = DiscIO::Region::NTSC_J;  // NTSC-K only exists on Wii, so use a fallback
956 
957   if (!wii && region == DiscIO::Region::NTSC_J && language == DiscIO::Language::English)
958     return DiscIO::Language::Japanese;  // English and Japanese both use the value 0 in GC SRAM
959 
960   if (!bOverrideRegionSettings)
961   {
962     if (region == DiscIO::Region::NTSC_J)
963       return DiscIO::Language::Japanese;
964 
965     if (region == DiscIO::Region::NTSC_U && language != DiscIO::Language::English &&
966         (!wii || (language != DiscIO::Language::French && language != DiscIO::Language::Spanish)))
967     {
968       return DiscIO::Language::English;
969     }
970 
971     if (region == DiscIO::Region::PAL &&
972         (language < DiscIO::Language::English || language > DiscIO::Language::Dutch))
973     {
974       return DiscIO::Language::English;
975     }
976 
977     if (region == DiscIO::Region::NTSC_K)
978       return DiscIO::Language::Korean;
979   }
980 
981   return language;
982 }
983 
LoadDefaultGameIni() const984 IniFile SConfig::LoadDefaultGameIni() const
985 {
986   return LoadDefaultGameIni(GetGameID(), m_revision);
987 }
988 
LoadLocalGameIni() const989 IniFile SConfig::LoadLocalGameIni() const
990 {
991   return LoadLocalGameIni(GetGameID(), m_revision);
992 }
993 
LoadGameIni() const994 IniFile SConfig::LoadGameIni() const
995 {
996   return LoadGameIni(GetGameID(), m_revision);
997 }
998 
LoadDefaultGameIni(const std::string & id,std::optional<u16> revision)999 IniFile SConfig::LoadDefaultGameIni(const std::string& id, std::optional<u16> revision)
1000 {
1001   IniFile game_ini;
1002   for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(id, revision))
1003     game_ini.Load(File::GetSysDirectory() + GAMESETTINGS_DIR DIR_SEP + filename, true);
1004   return game_ini;
1005 }
1006 
LoadLocalGameIni(const std::string & id,std::optional<u16> revision)1007 IniFile SConfig::LoadLocalGameIni(const std::string& id, std::optional<u16> revision)
1008 {
1009   IniFile game_ini;
1010   for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(id, revision))
1011     game_ini.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + filename, true);
1012   return game_ini;
1013 }
1014 
LoadGameIni(const std::string & id,std::optional<u16> revision)1015 IniFile SConfig::LoadGameIni(const std::string& id, std::optional<u16> revision)
1016 {
1017   IniFile game_ini;
1018   for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(id, revision))
1019     game_ini.Load(File::GetSysDirectory() + GAMESETTINGS_DIR DIR_SEP + filename, true);
1020   for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(id, revision))
1021     game_ini.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + filename, true);
1022   return game_ini;
1023 }
1024 
ShouldUseDPL2Decoder() const1025 bool SConfig::ShouldUseDPL2Decoder() const
1026 {
1027   return bDPL2Decoder && !bDSPHLE;
1028 }
1029