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