1 // Copyright 2014 Citra Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4
5 #include <algorithm>
6 #include <array>
7 #include <unordered_map>
8 #include <QKeySequence>
9 #include <QSettings>
10 #include "citra_qt/configuration/config.h"
11 #include "citra_qt/uisettings.h"
12 #include "common/file_util.h"
13 #include "core/frontend/mic.h"
14 #include "core/hle/service/service.h"
15 #include "input_common/main.h"
16 #include "input_common/udp/client.h"
17 #include "network/network.h"
18
Config()19 Config::Config() {
20 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
21 qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
22 FileUtil::CreateFullPath(qt_config_loc);
23 qt_config =
24 std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
25 Reload();
26 }
27
~Config()28 Config::~Config() {
29 Save();
30 }
31
32 const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
33 Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_T, Qt::Key_G,
34 Qt::Key_F, Qt::Key_H, Qt::Key_Q, Qt::Key_W, Qt::Key_M, Qt::Key_N,
35 Qt::Key_O, Qt::Key_P, Qt::Key_1, Qt::Key_2, Qt::Key_B,
36 };
37
38 const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
39 {
40 Qt::Key_Up,
41 Qt::Key_Down,
42 Qt::Key_Left,
43 Qt::Key_Right,
44 Qt::Key_D,
45 },
46 {
47 Qt::Key_I,
48 Qt::Key_K,
49 Qt::Key_J,
50 Qt::Key_L,
51 Qt::Key_D,
52 },
53 }};
54
55 // This shouldn't have anything except static initializers (no functions). So
56 // QKeySequence(...).toString() is NOT ALLOWED HERE.
57 // This must be in alphabetical order according to action name as it must have the same order as
58 // UISetting::values.shortcuts, which is alphabetically ordered.
59 // clang-format off
60 const std::array<UISettings::Shortcut, 23> default_hotkeys{
61 {{QStringLiteral("Advance Frame"), QStringLiteral("Main Window"), {QStringLiteral("\\"), Qt::ApplicationShortcut}},
62 {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), Qt::ApplicationShortcut}},
63 {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), Qt::WindowShortcut}},
64 {QStringLiteral("Decrease Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("-"), Qt::ApplicationShortcut}},
65 {QStringLiteral("Exit Citra"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), Qt::WindowShortcut}},
66 {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), Qt::WindowShortcut}},
67 {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), Qt::WindowShortcut}},
68 {QStringLiteral("Increase Speed Limit"), QStringLiteral("Main Window"), {QStringLiteral("+"), Qt::ApplicationShortcut}},
69 {QStringLiteral("Load Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), Qt::ApplicationShortcut}},
70 {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), Qt::WindowShortcut}},
71 {QStringLiteral("Load from Newest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+V"), Qt::WindowShortcut}},
72 {QStringLiteral("Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F3"), Qt::ApplicationShortcut}},
73 {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), Qt::WindowShortcut}},
74 {QStringLiteral("Rotate Screens Upright"), QStringLiteral("Main Window"), {QStringLiteral("F8"), Qt::WindowShortcut}},
75 {QStringLiteral("Save to Oldest Slot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+C"), Qt::WindowShortcut}},
76 {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), Qt::WindowShortcut}},
77 {QStringLiteral("Swap Screens"), QStringLiteral("Main Window"), {QStringLiteral("F9"), Qt::WindowShortcut}},
78 {QStringLiteral("Toggle Alternate Speed"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Z"), Qt::ApplicationShortcut}},
79 {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), Qt::WindowShortcut}},
80 {QStringLiteral("Toggle Frame Advancing"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+A"), Qt::ApplicationShortcut}},
81 {QStringLiteral("Toggle Screen Layout"), QStringLiteral("Main Window"), {QStringLiteral("F10"), Qt::WindowShortcut}},
82 {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), Qt::WindowShortcut}},
83 {QStringLiteral("Toggle Texture Dumping"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+D"), Qt::ApplicationShortcut}}}};
84 // clang-format on
85
ReadValues()86 void Config::ReadValues() {
87 ReadControlValues();
88 ReadCoreValues();
89 ReadRendererValues();
90 ReadLayoutValues();
91 ReadAudioValues();
92 ReadCameraValues();
93 ReadDataStorageValues();
94 ReadSystemValues();
95 ReadMiscellaneousValues();
96 ReadDebuggingValues();
97 ReadWebServiceValues();
98 ReadVideoDumpingValues();
99 ReadUIValues();
100 ReadUtilityValues();
101 }
102
ReadAudioValues()103 void Config::ReadAudioValues() {
104 qt_config->beginGroup(QStringLiteral("Audio"));
105
106 Settings::values.enable_dsp_lle = ReadSetting(QStringLiteral("enable_dsp_lle"), false).toBool();
107 Settings::values.enable_dsp_lle_multithread =
108 ReadSetting(QStringLiteral("enable_dsp_lle_multithread"), false).toBool();
109 Settings::values.sink_id = ReadSetting(QStringLiteral("output_engine"), QStringLiteral("auto"))
110 .toString()
111 .toStdString();
112 Settings::values.enable_audio_stretching =
113 ReadSetting(QStringLiteral("enable_audio_stretching"), true).toBool();
114 Settings::values.audio_device_id =
115 ReadSetting(QStringLiteral("output_device"), QStringLiteral("auto"))
116 .toString()
117 .toStdString();
118 Settings::values.volume = ReadSetting(QStringLiteral("volume"), 1).toFloat();
119 Settings::values.mic_input_type = static_cast<Settings::MicInputType>(
120 ReadSetting(QStringLiteral("mic_input_type"), 0).toInt());
121 Settings::values.mic_input_device =
122 ReadSetting(QStringLiteral("mic_input_device"),
123 QString::fromUtf8(Frontend::Mic::default_device_name))
124 .toString()
125 .toStdString();
126
127 qt_config->endGroup();
128 }
129
ReadCameraValues()130 void Config::ReadCameraValues() {
131 using namespace Service::CAM;
132 qt_config->beginGroup(QStringLiteral("Camera"));
133
134 Settings::values.camera_name[OuterRightCamera] =
135 ReadSetting(QStringLiteral("camera_outer_right_name"), QStringLiteral("blank"))
136 .toString()
137 .toStdString();
138 Settings::values.camera_config[OuterRightCamera] =
139 ReadSetting(QStringLiteral("camera_outer_right_config"), QString{})
140 .toString()
141 .toStdString();
142 Settings::values.camera_flip[OuterRightCamera] =
143 ReadSetting(QStringLiteral("camera_outer_right_flip"), 0).toInt();
144 Settings::values.camera_name[InnerCamera] =
145 ReadSetting(QStringLiteral("camera_inner_name"), QStringLiteral("blank"))
146 .toString()
147 .toStdString();
148 Settings::values.camera_config[InnerCamera] =
149 ReadSetting(QStringLiteral("camera_inner_config"), QString{}).toString().toStdString();
150 Settings::values.camera_flip[InnerCamera] =
151 ReadSetting(QStringLiteral("camera_inner_flip"), 0).toInt();
152 Settings::values.camera_name[OuterLeftCamera] =
153 ReadSetting(QStringLiteral("camera_outer_left_name"), QStringLiteral("blank"))
154 .toString()
155 .toStdString();
156 Settings::values.camera_config[OuterLeftCamera] =
157 ReadSetting(QStringLiteral("camera_outer_left_config"), QString{}).toString().toStdString();
158 Settings::values.camera_flip[OuterLeftCamera] =
159 ReadSetting(QStringLiteral("camera_outer_left_flip"), 0).toInt();
160
161 qt_config->endGroup();
162 }
163
ReadControlValues()164 void Config::ReadControlValues() {
165 qt_config->beginGroup(QStringLiteral("Controls"));
166
167 int num_touch_from_button_maps =
168 qt_config->beginReadArray(QStringLiteral("touch_from_button_maps"));
169
170 if (num_touch_from_button_maps > 0) {
171 const auto append_touch_from_button_map = [this] {
172 Settings::TouchFromButtonMap map;
173 map.name = ReadSetting(QStringLiteral("name"), QStringLiteral("default"))
174 .toString()
175 .toStdString();
176 const int num_touch_maps = qt_config->beginReadArray(QStringLiteral("entries"));
177 map.buttons.reserve(num_touch_maps);
178 for (int i = 0; i < num_touch_maps; i++) {
179 qt_config->setArrayIndex(i);
180 std::string touch_mapping =
181 ReadSetting(QStringLiteral("bind")).toString().toStdString();
182 map.buttons.emplace_back(std::move(touch_mapping));
183 }
184 qt_config->endArray(); // entries
185 Settings::values.touch_from_button_maps.emplace_back(std::move(map));
186 };
187
188 for (int i = 0; i < num_touch_from_button_maps; ++i) {
189 qt_config->setArrayIndex(i);
190 append_touch_from_button_map();
191 }
192 } else {
193 Settings::values.touch_from_button_maps.emplace_back(
194 Settings::TouchFromButtonMap{"default", {}});
195 num_touch_from_button_maps = 1;
196 }
197 qt_config->endArray();
198
199 Settings::values.current_input_profile_index =
200 ReadSetting(QStringLiteral("profile"), 0).toInt();
201
202 const auto append_profile = [this, num_touch_from_button_maps] {
203 Settings::InputProfile profile;
204 profile.name =
205 ReadSetting(QStringLiteral("name"), QStringLiteral("default")).toString().toStdString();
206 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
207 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
208 profile.buttons[i] = ReadSetting(QString::fromUtf8(Settings::NativeButton::mapping[i]),
209 QString::fromStdString(default_param))
210 .toString()
211 .toStdString();
212 if (profile.buttons[i].empty())
213 profile.buttons[i] = default_param;
214 }
215 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
216 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
217 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
218 default_analogs[i][3], default_analogs[i][4], 0.5f);
219 profile.analogs[i] = ReadSetting(QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
220 QString::fromStdString(default_param))
221 .toString()
222 .toStdString();
223 if (profile.analogs[i].empty())
224 profile.analogs[i] = default_param;
225 }
226 profile.motion_device =
227 ReadSetting(QStringLiteral("motion_device"),
228 QStringLiteral(
229 "engine:motion_emu,update_period:100,sensitivity:0.01,tilt_clamp:90.0"))
230 .toString()
231 .toStdString();
232 profile.touch_device =
233 ReadSetting(QStringLiteral("touch_device"), QStringLiteral("engine:emu_window"))
234 .toString()
235 .toStdString();
236 profile.use_touch_from_button =
237 ReadSetting(QStringLiteral("use_touch_from_button"), false).toBool();
238 profile.touch_from_button_map_index =
239 ReadSetting(QStringLiteral("touch_from_button_map"), 0).toInt();
240 profile.touch_from_button_map_index =
241 std::clamp(profile.touch_from_button_map_index, 0, num_touch_from_button_maps - 1);
242 profile.udp_input_address =
243 ReadSetting(QStringLiteral("udp_input_address"),
244 QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR))
245 .toString()
246 .toStdString();
247 profile.udp_input_port = static_cast<u16>(
248 ReadSetting(QStringLiteral("udp_input_port"), InputCommon::CemuhookUDP::DEFAULT_PORT)
249 .toInt());
250 profile.udp_pad_index =
251 static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt());
252 Settings::values.input_profiles.emplace_back(std::move(profile));
253 };
254
255 int num_input_profiles = qt_config->beginReadArray(QStringLiteral("profiles"));
256
257 for (int i = 0; i < num_input_profiles; ++i) {
258 qt_config->setArrayIndex(i);
259 append_profile();
260 }
261
262 qt_config->endArray();
263
264 // create a input profile if no input profiles exist, with the default or old settings
265 if (num_input_profiles == 0) {
266 append_profile();
267 num_input_profiles = 1;
268 }
269
270 // ensure that the current input profile index is valid.
271 Settings::values.current_input_profile_index =
272 std::clamp(Settings::values.current_input_profile_index, 0, num_input_profiles - 1);
273
274 Settings::LoadProfile(Settings::values.current_input_profile_index);
275
276 qt_config->endGroup();
277 }
278
ReadUtilityValues()279 void Config::ReadUtilityValues() {
280 qt_config->beginGroup(QStringLiteral("Utility"));
281
282 Settings::values.dump_textures = ReadSetting(QStringLiteral("dump_textures"), false).toBool();
283 Settings::values.custom_textures =
284 ReadSetting(QStringLiteral("custom_textures"), false).toBool();
285 Settings::values.preload_textures =
286 ReadSetting(QStringLiteral("preload_textures"), false).toBool();
287
288 qt_config->endGroup();
289 }
290
ReadCoreValues()291 void Config::ReadCoreValues() {
292 qt_config->beginGroup(QStringLiteral("Core"));
293
294 Settings::values.use_cpu_jit = ReadSetting(QStringLiteral("use_cpu_jit"), true).toBool();
295 Settings::values.cpu_clock_percentage =
296 ReadSetting(QStringLiteral("cpu_clock_percentage"), 100).toInt();
297
298 qt_config->endGroup();
299 }
300
ReadDataStorageValues()301 void Config::ReadDataStorageValues() {
302 qt_config->beginGroup(QStringLiteral("Data Storage"));
303
304 Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool();
305 std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
306 Settings::values.nand_dir =
307 ReadSetting(QStringLiteral("nand_directory"), QString::fromStdString(nand_dir))
308 .toString()
309 .toStdString();
310 std::string sdmc_dir = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
311 Settings::values.sdmc_dir =
312 ReadSetting(QStringLiteral("sdmc_directory"), QString::fromStdString(sdmc_dir))
313 .toString()
314 .toStdString();
315
316 qt_config->endGroup();
317 }
318
ReadDebuggingValues()319 void Config::ReadDebuggingValues() {
320 qt_config->beginGroup(QStringLiteral("Debugging"));
321
322 // Intentionally not using the QT default setting as this is intended to be changed in the ini
323 Settings::values.record_frame_times =
324 qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
325 Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool();
326 Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt();
327
328 qt_config->beginGroup(QStringLiteral("LLE"));
329 for (const auto& service_module : Service::service_module_map) {
330 bool use_lle = ReadSetting(QString::fromStdString(service_module.name), false).toBool();
331 Settings::values.lle_modules.emplace(service_module.name, use_lle);
332 }
333 qt_config->endGroup();
334
335 qt_config->endGroup();
336 }
337
ReadLayoutValues()338 void Config::ReadLayoutValues() {
339 qt_config->beginGroup(QStringLiteral("Layout"));
340
341 Settings::values.render_3d = static_cast<Settings::StereoRenderOption>(
342 ReadSetting(QStringLiteral("render_3d"), 0).toInt());
343 Settings::values.factor_3d = ReadSetting(QStringLiteral("factor_3d"), 0).toInt();
344 Settings::values.pp_shader_name =
345 ReadSetting(QStringLiteral("pp_shader_name"),
346 (Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph)
347 ? QStringLiteral("dubois (builtin)")
348 : QStringLiteral("none (builtin)"))
349 .toString()
350 .toStdString();
351 Settings::values.filter_mode = ReadSetting(QStringLiteral("filter_mode"), true).toBool();
352 Settings::values.layout_option =
353 static_cast<Settings::LayoutOption>(ReadSetting(QStringLiteral("layout_option")).toInt());
354 Settings::values.swap_screen = ReadSetting(QStringLiteral("swap_screen"), false).toBool();
355 Settings::values.upright_screen = ReadSetting(QStringLiteral("upright_screen"), false).toBool();
356 Settings::values.custom_layout = ReadSetting(QStringLiteral("custom_layout"), false).toBool();
357 Settings::values.custom_top_left = ReadSetting(QStringLiteral("custom_top_left"), 0).toInt();
358 Settings::values.custom_top_top = ReadSetting(QStringLiteral("custom_top_top"), 0).toInt();
359 Settings::values.custom_top_right =
360 ReadSetting(QStringLiteral("custom_top_right"), 400).toInt();
361 Settings::values.custom_top_bottom =
362 ReadSetting(QStringLiteral("custom_top_bottom"), 240).toInt();
363 Settings::values.custom_bottom_left =
364 ReadSetting(QStringLiteral("custom_bottom_left"), 40).toInt();
365 Settings::values.custom_bottom_top =
366 ReadSetting(QStringLiteral("custom_bottom_top"), 240).toInt();
367 Settings::values.custom_bottom_right =
368 ReadSetting(QStringLiteral("custom_bottom_right"), 360).toInt();
369 Settings::values.custom_bottom_bottom =
370 ReadSetting(QStringLiteral("custom_bottom_bottom"), 480).toInt();
371
372 qt_config->endGroup();
373 }
374
ReadMiscellaneousValues()375 void Config::ReadMiscellaneousValues() {
376 qt_config->beginGroup(QStringLiteral("Miscellaneous"));
377
378 Settings::values.log_filter =
379 ReadSetting(QStringLiteral("log_filter"), QStringLiteral("*:Info"))
380 .toString()
381 .toStdString();
382
383 qt_config->endGroup();
384 }
385
ReadMultiplayerValues()386 void Config::ReadMultiplayerValues() {
387 qt_config->beginGroup(QStringLiteral("Multiplayer"));
388
389 UISettings::values.nickname = ReadSetting(QStringLiteral("nickname"), QString{}).toString();
390 UISettings::values.ip = ReadSetting(QStringLiteral("ip"), QString{}).toString();
391 UISettings::values.port =
392 ReadSetting(QStringLiteral("port"), Network::DefaultRoomPort).toString();
393 UISettings::values.room_nickname =
394 ReadSetting(QStringLiteral("room_nickname"), QString{}).toString();
395 UISettings::values.room_name = ReadSetting(QStringLiteral("room_name"), QString{}).toString();
396 UISettings::values.room_port =
397 ReadSetting(QStringLiteral("room_port"), QStringLiteral("24872")).toString();
398 bool ok;
399 UISettings::values.host_type = ReadSetting(QStringLiteral("host_type"), 0).toUInt(&ok);
400 if (!ok) {
401 UISettings::values.host_type = 0;
402 }
403 UISettings::values.max_player = ReadSetting(QStringLiteral("max_player"), 8).toUInt();
404 UISettings::values.game_id = ReadSetting(QStringLiteral("game_id"), 0).toULongLong();
405 UISettings::values.room_description =
406 ReadSetting(QStringLiteral("room_description"), QString{}).toString();
407 // Read ban list back
408 int size = qt_config->beginReadArray(QStringLiteral("username_ban_list"));
409 UISettings::values.ban_list.first.resize(size);
410 for (int i = 0; i < size; ++i) {
411 qt_config->setArrayIndex(i);
412 UISettings::values.ban_list.first[i] =
413 ReadSetting(QStringLiteral("username")).toString().toStdString();
414 }
415 qt_config->endArray();
416 size = qt_config->beginReadArray(QStringLiteral("ip_ban_list"));
417 UISettings::values.ban_list.second.resize(size);
418 for (int i = 0; i < size; ++i) {
419 qt_config->setArrayIndex(i);
420 UISettings::values.ban_list.second[i] =
421 ReadSetting(QStringLiteral("ip")).toString().toStdString();
422 }
423 qt_config->endArray();
424
425 qt_config->endGroup();
426 }
427
ReadPathValues()428 void Config::ReadPathValues() {
429 qt_config->beginGroup(QStringLiteral("Paths"));
430
431 UISettings::values.roms_path = ReadSetting(QStringLiteral("romsPath")).toString();
432 UISettings::values.symbols_path = ReadSetting(QStringLiteral("symbolsPath")).toString();
433 UISettings::values.movie_record_path =
434 ReadSetting(QStringLiteral("movieRecordPath")).toString();
435 UISettings::values.movie_playback_path =
436 ReadSetting(QStringLiteral("moviePlaybackPath")).toString();
437 UISettings::values.screenshot_path = ReadSetting(QStringLiteral("screenshotPath")).toString();
438 UISettings::values.video_dumping_path =
439 ReadSetting(QStringLiteral("videoDumpingPath")).toString();
440 UISettings::values.game_dir_deprecated =
441 ReadSetting(QStringLiteral("gameListRootDir"), QStringLiteral(".")).toString();
442 UISettings::values.game_dir_deprecated_deepscan =
443 ReadSetting(QStringLiteral("gameListDeepScan"), false).toBool();
444 int size = qt_config->beginReadArray(QStringLiteral("gamedirs"));
445 for (int i = 0; i < size; ++i) {
446 qt_config->setArrayIndex(i);
447 UISettings::GameDir game_dir;
448 game_dir.path = ReadSetting(QStringLiteral("path")).toString();
449 game_dir.deep_scan = ReadSetting(QStringLiteral("deep_scan"), false).toBool();
450 game_dir.expanded = ReadSetting(QStringLiteral("expanded"), true).toBool();
451 UISettings::values.game_dirs.append(game_dir);
452 }
453 qt_config->endArray();
454 // create NAND and SD card directories if empty, these are not removable through the UI,
455 // also carries over old game list settings if present
456 if (UISettings::values.game_dirs.isEmpty()) {
457 UISettings::GameDir game_dir;
458 game_dir.path = QStringLiteral("INSTALLED");
459 game_dir.expanded = true;
460 UISettings::values.game_dirs.append(game_dir);
461 game_dir.path = QStringLiteral("SYSTEM");
462 UISettings::values.game_dirs.append(game_dir);
463 if (UISettings::values.game_dir_deprecated != QStringLiteral(".")) {
464 game_dir.path = UISettings::values.game_dir_deprecated;
465 game_dir.deep_scan = UISettings::values.game_dir_deprecated_deepscan;
466 UISettings::values.game_dirs.append(game_dir);
467 }
468 }
469 UISettings::values.recent_files = ReadSetting(QStringLiteral("recentFiles")).toStringList();
470 UISettings::values.language = ReadSetting(QStringLiteral("language"), QString{}).toString();
471
472 qt_config->endGroup();
473 }
474
ReadRendererValues()475 void Config::ReadRendererValues() {
476 qt_config->beginGroup(QStringLiteral("Renderer"));
477
478 Settings::values.use_hw_renderer =
479 ReadSetting(QStringLiteral("use_hw_renderer"), true).toBool();
480 Settings::values.use_hw_shader = ReadSetting(QStringLiteral("use_hw_shader"), true).toBool();
481 #ifdef __APPLE__
482 // Hardware shader is broken on macos with Intel GPUs thanks to poor drivers.
483 // We still want to provide this option for test/development purposes, but disable it by
484 // default.
485 Settings::values.separable_shader =
486 ReadSetting(QStringLiteral("separable_shader"), false).toBool();
487 #endif
488 Settings::values.shaders_accurate_mul =
489 ReadSetting(QStringLiteral("shaders_accurate_mul"), true).toBool();
490 Settings::values.use_shader_jit = ReadSetting(QStringLiteral("use_shader_jit"), true).toBool();
491 Settings::values.use_disk_shader_cache =
492 ReadSetting(QStringLiteral("use_disk_shader_cache"), true).toBool();
493 Settings::values.use_vsync_new = ReadSetting(QStringLiteral("use_vsync_new"), true).toBool();
494 Settings::values.resolution_factor =
495 static_cast<u16>(ReadSetting(QStringLiteral("resolution_factor"), 1).toInt());
496 Settings::values.frame_limit = ReadSetting(QStringLiteral("frame_limit"), 100).toInt();
497 Settings::values.use_frame_limit_alternate =
498 ReadSetting(QStringLiteral("use_frame_limit_alternate"), false).toBool();
499 Settings::values.frame_limit_alternate =
500 ReadSetting(QStringLiteral("frame_limit_alternate"), 200).toInt();
501
502 Settings::values.bg_red = ReadSetting(QStringLiteral("bg_red"), 0.0).toFloat();
503 Settings::values.bg_green = ReadSetting(QStringLiteral("bg_green"), 0.0).toFloat();
504 Settings::values.bg_blue = ReadSetting(QStringLiteral("bg_blue"), 0.0).toFloat();
505
506 Settings::values.texture_filter_name =
507 ReadSetting(QStringLiteral("texture_filter_name"), QStringLiteral("none"))
508 .toString()
509 .toStdString();
510
511 qt_config->endGroup();
512 }
513
ReadShortcutValues()514 void Config::ReadShortcutValues() {
515 qt_config->beginGroup(QStringLiteral("Shortcuts"));
516
517 for (auto [name, group, shortcut] : default_hotkeys) {
518 auto [keyseq, context] = shortcut;
519 qt_config->beginGroup(group);
520 qt_config->beginGroup(name);
521 UISettings::values.shortcuts.push_back(
522 {name,
523 group,
524 {ReadSetting(QStringLiteral("KeySeq"), keyseq).toString(),
525 ReadSetting(QStringLiteral("Context"), context).toInt()}});
526 qt_config->endGroup();
527 qt_config->endGroup();
528 }
529
530 qt_config->endGroup();
531 }
532
ReadSystemValues()533 void Config::ReadSystemValues() {
534 qt_config->beginGroup(QStringLiteral("System"));
535
536 Settings::values.is_new_3ds = ReadSetting(QStringLiteral("is_new_3ds"), true).toBool();
537 Settings::values.region_value =
538 ReadSetting(QStringLiteral("region_value"), Settings::REGION_VALUE_AUTO_SELECT).toInt();
539 Settings::values.init_clock = static_cast<Settings::InitClock>(
540 ReadSetting(QStringLiteral("init_clock"), static_cast<u32>(Settings::InitClock::SystemTime))
541 .toInt());
542 Settings::values.init_time =
543 ReadSetting(QStringLiteral("init_time"), 946681277ULL).toULongLong();
544
545 qt_config->endGroup();
546 }
547
548 // Options for variable bit rate live streaming taken from here:
549 // https://developers.google.com/media/vp9/live-encoding
550 const QString DEFAULT_VIDEO_ENCODER_OPTIONS =
551 QStringLiteral("quality:realtime,speed:6,tile-columns:4,frame-parallel:1,threads:8,row-mt:1");
552 const QString DEFAULT_AUDIO_ENCODER_OPTIONS = QString{};
553
ReadVideoDumpingValues()554 void Config::ReadVideoDumpingValues() {
555 qt_config->beginGroup(QStringLiteral("VideoDumping"));
556
557 Settings::values.output_format =
558 ReadSetting(QStringLiteral("output_format"), QStringLiteral("webm"))
559 .toString()
560 .toStdString();
561 Settings::values.format_options =
562 ReadSetting(QStringLiteral("format_options")).toString().toStdString();
563
564 Settings::values.video_encoder =
565 ReadSetting(QStringLiteral("video_encoder"), QStringLiteral("libvpx-vp9"))
566 .toString()
567 .toStdString();
568
569 Settings::values.video_encoder_options =
570 ReadSetting(QStringLiteral("video_encoder_options"), DEFAULT_VIDEO_ENCODER_OPTIONS)
571 .toString()
572 .toStdString();
573
574 Settings::values.video_bitrate =
575 ReadSetting(QStringLiteral("video_bitrate"), 2500000).toULongLong();
576
577 Settings::values.audio_encoder =
578 ReadSetting(QStringLiteral("audio_encoder"), QStringLiteral("libvorbis"))
579 .toString()
580 .toStdString();
581 Settings::values.audio_encoder_options =
582 ReadSetting(QStringLiteral("audio_encoder_options"), DEFAULT_AUDIO_ENCODER_OPTIONS)
583 .toString()
584 .toStdString();
585 Settings::values.audio_bitrate =
586 ReadSetting(QStringLiteral("audio_bitrate"), 64000).toULongLong();
587
588 qt_config->endGroup();
589 }
590
ReadUIValues()591 void Config::ReadUIValues() {
592 qt_config->beginGroup(QStringLiteral("UI"));
593
594 UISettings::values.theme =
595 ReadSetting(QStringLiteral("theme"), QString::fromUtf8(UISettings::themes[0].second))
596 .toString();
597 UISettings::values.enable_discord_presence =
598 ReadSetting(QStringLiteral("enable_discord_presence"), true).toBool();
599 UISettings::values.screenshot_resolution_factor =
600 static_cast<u16>(ReadSetting(QStringLiteral("screenshot_resolution_factor"), 0).toUInt());
601
602 ReadUpdaterValues();
603 ReadUILayoutValues();
604 ReadUIGameListValues();
605 ReadPathValues();
606 ReadShortcutValues();
607 ReadMultiplayerValues();
608
609 UISettings::values.single_window_mode =
610 ReadSetting(QStringLiteral("singleWindowMode"), true).toBool();
611 UISettings::values.fullscreen = ReadSetting(QStringLiteral("fullscreen"), false).toBool();
612 UISettings::values.display_titlebar =
613 ReadSetting(QStringLiteral("displayTitleBars"), true).toBool();
614 UISettings::values.show_filter_bar =
615 ReadSetting(QStringLiteral("showFilterBar"), true).toBool();
616 UISettings::values.show_status_bar =
617 ReadSetting(QStringLiteral("showStatusBar"), true).toBool();
618 UISettings::values.confirm_before_closing =
619 ReadSetting(QStringLiteral("confirmClose"), true).toBool();
620 UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool();
621 UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt();
622 UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool();
623 UISettings::values.pause_when_in_background =
624 ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool();
625 UISettings::values.hide_mouse =
626 ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool();
627
628 qt_config->endGroup();
629 }
630
ReadUIGameListValues()631 void Config::ReadUIGameListValues() {
632 qt_config->beginGroup(QStringLiteral("GameList"));
633
634 auto icon_size = UISettings::GameListIconSize{
635 ReadSetting(QStringLiteral("iconSize"),
636 static_cast<int>(UISettings::GameListIconSize::LargeIcon))
637 .toInt()};
638 if (icon_size < UISettings::GameListIconSize::NoIcon ||
639 icon_size > UISettings::GameListIconSize::LargeIcon) {
640 icon_size = UISettings::GameListIconSize::LargeIcon;
641 }
642 UISettings::values.game_list_icon_size = icon_size;
643
644 UISettings::GameListText row_1 = UISettings::GameListText{
645 ReadSetting(QStringLiteral("row1"), static_cast<int>(UISettings::GameListText::TitleName))
646 .toInt()};
647 if (row_1 <= UISettings::GameListText::NoText || row_1 >= UISettings::GameListText::ListEnd) {
648 row_1 = UISettings::GameListText::TitleName;
649 }
650 UISettings::values.game_list_row_1 = row_1;
651
652 UISettings::GameListText row_2 = UISettings::GameListText{
653 ReadSetting(QStringLiteral("row2"), static_cast<int>(UISettings::GameListText::FileName))
654 .toInt()};
655 if (row_2 < UISettings::GameListText::NoText || row_2 >= UISettings::GameListText::ListEnd) {
656 row_2 = UISettings::GameListText::FileName;
657 }
658 UISettings::values.game_list_row_2 = row_2;
659
660 UISettings::values.game_list_hide_no_icon =
661 ReadSetting(QStringLiteral("hideNoIcon"), false).toBool();
662 UISettings::values.game_list_single_line_mode =
663 ReadSetting(QStringLiteral("singleLineMode"), false).toBool();
664
665 qt_config->endGroup();
666 }
667
ReadUILayoutValues()668 void Config::ReadUILayoutValues() {
669 qt_config->beginGroup(QStringLiteral("UILayout"));
670
671 UISettings::values.geometry = ReadSetting(QStringLiteral("geometry")).toByteArray();
672 UISettings::values.state = ReadSetting(QStringLiteral("state")).toByteArray();
673 UISettings::values.renderwindow_geometry =
674 ReadSetting(QStringLiteral("geometryRenderWindow")).toByteArray();
675 UISettings::values.gamelist_header_state =
676 ReadSetting(QStringLiteral("gameListHeaderState")).toByteArray();
677 UISettings::values.microprofile_geometry =
678 ReadSetting(QStringLiteral("microProfileDialogGeometry")).toByteArray();
679 UISettings::values.microprofile_visible =
680 ReadSetting(QStringLiteral("microProfileDialogVisible"), false).toBool();
681
682 qt_config->endGroup();
683 }
684
ReadUpdaterValues()685 void Config::ReadUpdaterValues() {
686 qt_config->beginGroup(QStringLiteral("Updater"));
687
688 UISettings::values.check_for_update_on_start =
689 ReadSetting(QStringLiteral("check_for_update_on_start"), true).toBool();
690 UISettings::values.update_on_close =
691 ReadSetting(QStringLiteral("update_on_close"), false).toBool();
692
693 qt_config->endGroup();
694 }
695
ReadWebServiceValues()696 void Config::ReadWebServiceValues() {
697 qt_config->beginGroup(QStringLiteral("WebService"));
698
699 Settings::values.enable_telemetry =
700 ReadSetting(QStringLiteral("enable_telemetry"), true).toBool();
701 Settings::values.web_api_url =
702 ReadSetting(QStringLiteral("web_api_url"), QStringLiteral("https://api.citra-emu.org"))
703 .toString()
704 .toStdString();
705 Settings::values.citra_username =
706 ReadSetting(QStringLiteral("citra_username")).toString().toStdString();
707 Settings::values.citra_token =
708 ReadSetting(QStringLiteral("citra_token")).toString().toStdString();
709
710 qt_config->endGroup();
711 }
712
SaveValues()713 void Config::SaveValues() {
714 SaveControlValues();
715 SaveCoreValues();
716 SaveRendererValues();
717 SaveLayoutValues();
718 SaveAudioValues();
719 SaveCameraValues();
720 SaveDataStorageValues();
721 SaveSystemValues();
722 SaveMiscellaneousValues();
723 SaveDebuggingValues();
724 SaveWebServiceValues();
725 SaveVideoDumpingValues();
726 SaveUIValues();
727 SaveUtilityValues();
728 }
729
SaveAudioValues()730 void Config::SaveAudioValues() {
731 qt_config->beginGroup(QStringLiteral("Audio"));
732
733 WriteSetting(QStringLiteral("enable_dsp_lle"), Settings::values.enable_dsp_lle, false);
734 WriteSetting(QStringLiteral("enable_dsp_lle_multithread"),
735 Settings::values.enable_dsp_lle_multithread, false);
736 WriteSetting(QStringLiteral("output_engine"), QString::fromStdString(Settings::values.sink_id),
737 QStringLiteral("auto"));
738 WriteSetting(QStringLiteral("enable_audio_stretching"),
739 Settings::values.enable_audio_stretching, true);
740 WriteSetting(QStringLiteral("output_device"),
741 QString::fromStdString(Settings::values.audio_device_id), QStringLiteral("auto"));
742 WriteSetting(QStringLiteral("volume"), Settings::values.volume, 1.0f);
743 WriteSetting(QStringLiteral("mic_input_device"),
744 QString::fromStdString(Settings::values.mic_input_device),
745 QString::fromUtf8(Frontend::Mic::default_device_name));
746 WriteSetting(QStringLiteral("mic_input_type"),
747 static_cast<int>(Settings::values.mic_input_type), 0);
748
749 qt_config->endGroup();
750 }
751
SaveCameraValues()752 void Config::SaveCameraValues() {
753 using namespace Service::CAM;
754 qt_config->beginGroup(QStringLiteral("Camera"));
755
756 WriteSetting(QStringLiteral("camera_outer_right_name"),
757 QString::fromStdString(Settings::values.camera_name[OuterRightCamera]),
758 QStringLiteral("blank"));
759 WriteSetting(QStringLiteral("camera_outer_right_config"),
760 QString::fromStdString(Settings::values.camera_config[OuterRightCamera]),
761 QString{});
762 WriteSetting(QStringLiteral("camera_outer_right_flip"),
763 Settings::values.camera_flip[OuterRightCamera], 0);
764 WriteSetting(QStringLiteral("camera_inner_name"),
765 QString::fromStdString(Settings::values.camera_name[InnerCamera]),
766 QStringLiteral("blank"));
767 WriteSetting(QStringLiteral("camera_inner_config"),
768 QString::fromStdString(Settings::values.camera_config[InnerCamera]), QString{});
769 WriteSetting(QStringLiteral("camera_inner_flip"), Settings::values.camera_flip[InnerCamera], 0);
770 WriteSetting(QStringLiteral("camera_outer_left_name"),
771 QString::fromStdString(Settings::values.camera_name[OuterLeftCamera]),
772 QStringLiteral("blank"));
773 WriteSetting(QStringLiteral("camera_outer_left_config"),
774 QString::fromStdString(Settings::values.camera_config[OuterLeftCamera]),
775 QString{});
776 WriteSetting(QStringLiteral("camera_outer_left_flip"),
777 Settings::values.camera_flip[OuterLeftCamera], 0);
778
779 qt_config->endGroup();
780 }
781
SaveControlValues()782 void Config::SaveControlValues() {
783 qt_config->beginGroup(QStringLiteral("Controls"));
784
785 WriteSetting(QStringLiteral("profile"), Settings::values.current_input_profile_index, 0);
786 qt_config->beginWriteArray(QStringLiteral("profiles"));
787 for (std::size_t p = 0; p < Settings::values.input_profiles.size(); ++p) {
788 qt_config->setArrayIndex(static_cast<int>(p));
789 const auto& profile = Settings::values.input_profiles[p];
790 WriteSetting(QStringLiteral("name"), QString::fromStdString(profile.name),
791 QStringLiteral("default"));
792 for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
793 std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
794 WriteSetting(QString::fromStdString(Settings::NativeButton::mapping[i]),
795 QString::fromStdString(profile.buttons[i]),
796 QString::fromStdString(default_param));
797 }
798 for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
799 std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
800 default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
801 default_analogs[i][3], default_analogs[i][4], 0.5f);
802 WriteSetting(QString::fromStdString(Settings::NativeAnalog::mapping[i]),
803 QString::fromStdString(profile.analogs[i]),
804 QString::fromStdString(default_param));
805 }
806 WriteSetting(
807 QStringLiteral("motion_device"), QString::fromStdString(profile.motion_device),
808 QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01,tilt_clamp:90.0"));
809 WriteSetting(QStringLiteral("touch_device"), QString::fromStdString(profile.touch_device),
810 QStringLiteral("engine:emu_window"));
811 WriteSetting(QStringLiteral("use_touch_from_button"), profile.use_touch_from_button, false);
812 WriteSetting(QStringLiteral("touch_from_button_map"), profile.touch_from_button_map_index,
813 0);
814 WriteSetting(QStringLiteral("udp_input_address"),
815 QString::fromStdString(profile.udp_input_address),
816 QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR));
817 WriteSetting(QStringLiteral("udp_input_port"), profile.udp_input_port,
818 InputCommon::CemuhookUDP::DEFAULT_PORT);
819 WriteSetting(QStringLiteral("udp_pad_index"), profile.udp_pad_index, 0);
820 }
821 qt_config->endArray();
822
823 qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps"));
824 for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
825 qt_config->setArrayIndex(static_cast<int>(p));
826 const auto& map = Settings::values.touch_from_button_maps[p];
827 WriteSetting(QStringLiteral("name"), QString::fromStdString(map.name),
828 QStringLiteral("default"));
829 qt_config->beginWriteArray(QStringLiteral("entries"));
830 for (std::size_t q = 0; q < map.buttons.size(); ++q) {
831 qt_config->setArrayIndex(static_cast<int>(q));
832 WriteSetting(QStringLiteral("bind"), QString::fromStdString(map.buttons[q]));
833 }
834 qt_config->endArray();
835 }
836 qt_config->endArray();
837
838 qt_config->endGroup();
839 }
840
SaveUtilityValues()841 void Config::SaveUtilityValues() {
842 qt_config->beginGroup(QStringLiteral("Utility"));
843
844 WriteSetting(QStringLiteral("dump_textures"), Settings::values.dump_textures, false);
845 WriteSetting(QStringLiteral("custom_textures"), Settings::values.custom_textures, false);
846 WriteSetting(QStringLiteral("preload_textures"), Settings::values.preload_textures, false);
847
848 qt_config->endGroup();
849 }
850
SaveCoreValues()851 void Config::SaveCoreValues() {
852 qt_config->beginGroup(QStringLiteral("Core"));
853
854 WriteSetting(QStringLiteral("use_cpu_jit"), Settings::values.use_cpu_jit, true);
855 WriteSetting(QStringLiteral("cpu_clock_percentage"), Settings::values.cpu_clock_percentage,
856 100);
857
858 qt_config->endGroup();
859 }
860
SaveDataStorageValues()861 void Config::SaveDataStorageValues() {
862 qt_config->beginGroup(QStringLiteral("Data Storage"));
863
864 WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true);
865 WriteSetting(QStringLiteral("nand_directory"),
866 QString::fromStdString(Settings::values.nand_dir),
867 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
868 WriteSetting(QStringLiteral("sdmc_directory"),
869 QString::fromStdString(Settings::values.sdmc_dir),
870 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
871
872 qt_config->endGroup();
873 }
874
SaveDebuggingValues()875 void Config::SaveDebuggingValues() {
876 qt_config->beginGroup(QStringLiteral("Debugging"));
877
878 // Intentionally not using the QT default setting as this is intended to be changed in the ini
879 qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
880 WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false);
881 WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689);
882
883 qt_config->beginGroup(QStringLiteral("LLE"));
884 for (const auto& service_module : Settings::values.lle_modules) {
885 WriteSetting(QString::fromStdString(service_module.first), service_module.second, false);
886 }
887 qt_config->endGroup();
888
889 qt_config->endGroup();
890 }
891
SaveLayoutValues()892 void Config::SaveLayoutValues() {
893 qt_config->beginGroup(QStringLiteral("Layout"));
894
895 WriteSetting(QStringLiteral("render_3d"), static_cast<int>(Settings::values.render_3d), 0);
896 WriteSetting(QStringLiteral("factor_3d"), Settings::values.factor_3d.load(), 0);
897 WriteSetting(QStringLiteral("pp_shader_name"),
898 QString::fromStdString(Settings::values.pp_shader_name),
899 (Settings::values.render_3d == Settings::StereoRenderOption::Anaglyph)
900 ? QStringLiteral("dubois (builtin)")
901 : QStringLiteral("none (builtin)"));
902 WriteSetting(QStringLiteral("filter_mode"), Settings::values.filter_mode, true);
903 WriteSetting(QStringLiteral("layout_option"), static_cast<int>(Settings::values.layout_option));
904 WriteSetting(QStringLiteral("swap_screen"), Settings::values.swap_screen, false);
905 WriteSetting(QStringLiteral("upright_screen"), Settings::values.upright_screen, false);
906 WriteSetting(QStringLiteral("custom_layout"), Settings::values.custom_layout, false);
907 WriteSetting(QStringLiteral("custom_top_left"), Settings::values.custom_top_left, 0);
908 WriteSetting(QStringLiteral("custom_top_top"), Settings::values.custom_top_top, 0);
909 WriteSetting(QStringLiteral("custom_top_right"), Settings::values.custom_top_right, 400);
910 WriteSetting(QStringLiteral("custom_top_bottom"), Settings::values.custom_top_bottom, 240);
911 WriteSetting(QStringLiteral("custom_bottom_left"), Settings::values.custom_bottom_left, 40);
912 WriteSetting(QStringLiteral("custom_bottom_top"), Settings::values.custom_bottom_top, 240);
913 WriteSetting(QStringLiteral("custom_bottom_right"), Settings::values.custom_bottom_right, 360);
914 WriteSetting(QStringLiteral("custom_bottom_bottom"), Settings::values.custom_bottom_bottom,
915 480);
916
917 qt_config->endGroup();
918 }
919
SaveMiscellaneousValues()920 void Config::SaveMiscellaneousValues() {
921 qt_config->beginGroup(QStringLiteral("Miscellaneous"));
922
923 WriteSetting(QStringLiteral("log_filter"), QString::fromStdString(Settings::values.log_filter),
924 QStringLiteral("*:Info"));
925
926 qt_config->endGroup();
927 }
928
SaveMultiplayerValues()929 void Config::SaveMultiplayerValues() {
930 qt_config->beginGroup(QStringLiteral("Multiplayer"));
931
932 WriteSetting(QStringLiteral("nickname"), UISettings::values.nickname, QString{});
933 WriteSetting(QStringLiteral("ip"), UISettings::values.ip, QString{});
934 WriteSetting(QStringLiteral("port"), UISettings::values.port, Network::DefaultRoomPort);
935 WriteSetting(QStringLiteral("room_nickname"), UISettings::values.room_nickname, QString{});
936 WriteSetting(QStringLiteral("room_name"), UISettings::values.room_name, QString{});
937 WriteSetting(QStringLiteral("room_port"), UISettings::values.room_port,
938 QStringLiteral("24872"));
939 WriteSetting(QStringLiteral("host_type"), UISettings::values.host_type, 0);
940 WriteSetting(QStringLiteral("max_player"), UISettings::values.max_player, 8);
941 WriteSetting(QStringLiteral("game_id"), UISettings::values.game_id, 0);
942 WriteSetting(QStringLiteral("room_description"), UISettings::values.room_description,
943 QString{});
944 // Write ban list
945 qt_config->beginWriteArray(QStringLiteral("username_ban_list"));
946 for (std::size_t i = 0; i < UISettings::values.ban_list.first.size(); ++i) {
947 qt_config->setArrayIndex(i);
948 WriteSetting(QStringLiteral("username"),
949 QString::fromStdString(UISettings::values.ban_list.first[i]));
950 }
951 qt_config->endArray();
952 qt_config->beginWriteArray(QStringLiteral("ip_ban_list"));
953 for (std::size_t i = 0; i < UISettings::values.ban_list.second.size(); ++i) {
954 qt_config->setArrayIndex(i);
955 WriteSetting(QStringLiteral("ip"),
956 QString::fromStdString(UISettings::values.ban_list.second[i]));
957 }
958 qt_config->endArray();
959
960 qt_config->endGroup();
961 }
962
SavePathValues()963 void Config::SavePathValues() {
964 qt_config->beginGroup(QStringLiteral("Paths"));
965
966 WriteSetting(QStringLiteral("romsPath"), UISettings::values.roms_path);
967 WriteSetting(QStringLiteral("symbolsPath"), UISettings::values.symbols_path);
968 WriteSetting(QStringLiteral("movieRecordPath"), UISettings::values.movie_record_path);
969 WriteSetting(QStringLiteral("moviePlaybackPath"), UISettings::values.movie_playback_path);
970 WriteSetting(QStringLiteral("screenshotPath"), UISettings::values.screenshot_path);
971 WriteSetting(QStringLiteral("videoDumpingPath"), UISettings::values.video_dumping_path);
972 qt_config->beginWriteArray(QStringLiteral("gamedirs"));
973 for (int i = 0; i < UISettings::values.game_dirs.size(); ++i) {
974 qt_config->setArrayIndex(i);
975 const auto& game_dir = UISettings::values.game_dirs[i];
976 WriteSetting(QStringLiteral("path"), game_dir.path);
977 WriteSetting(QStringLiteral("deep_scan"), game_dir.deep_scan, false);
978 WriteSetting(QStringLiteral("expanded"), game_dir.expanded, true);
979 }
980 qt_config->endArray();
981 WriteSetting(QStringLiteral("recentFiles"), UISettings::values.recent_files);
982 WriteSetting(QStringLiteral("language"), UISettings::values.language, QString{});
983
984 qt_config->endGroup();
985 }
986
SaveRendererValues()987 void Config::SaveRendererValues() {
988 qt_config->beginGroup(QStringLiteral("Renderer"));
989
990 WriteSetting(QStringLiteral("use_hw_renderer"), Settings::values.use_hw_renderer, true);
991 WriteSetting(QStringLiteral("use_hw_shader"), Settings::values.use_hw_shader, true);
992 #ifdef __APPLE__
993 // Hardware shader is broken on macos thanks to poor drivers.
994 // TODO: enable this for none Intel GPUs
995 WriteSetting(QStringLiteral("use_separable_shader"), Settings::values.separable_shader, false);
996 #endif
997 WriteSetting(QStringLiteral("shaders_accurate_mul"), Settings::values.shaders_accurate_mul,
998 true);
999 WriteSetting(QStringLiteral("use_shader_jit"), Settings::values.use_shader_jit, true);
1000 WriteSetting(QStringLiteral("use_disk_shader_cache"), Settings::values.use_disk_shader_cache,
1001 true);
1002 WriteSetting(QStringLiteral("use_vsync_new"), Settings::values.use_vsync_new, true);
1003 WriteSetting(QStringLiteral("resolution_factor"), Settings::values.resolution_factor, 1);
1004 WriteSetting(QStringLiteral("frame_limit"), Settings::values.frame_limit, 100);
1005 WriteSetting(QStringLiteral("use_frame_limit_alternate"),
1006 Settings::values.use_frame_limit_alternate, false);
1007 WriteSetting(QStringLiteral("frame_limit_alternate"), Settings::values.frame_limit_alternate,
1008 200);
1009
1010 // Cast to double because Qt's written float values are not human-readable
1011 WriteSetting(QStringLiteral("bg_red"), (double)Settings::values.bg_red, 0.0);
1012 WriteSetting(QStringLiteral("bg_green"), (double)Settings::values.bg_green, 0.0);
1013 WriteSetting(QStringLiteral("bg_blue"), (double)Settings::values.bg_blue, 0.0);
1014
1015 WriteSetting(QStringLiteral("texture_filter_name"),
1016 QString::fromStdString(Settings::values.texture_filter_name),
1017 QStringLiteral("none"));
1018
1019 qt_config->endGroup();
1020 }
1021
SaveShortcutValues()1022 void Config::SaveShortcutValues() {
1023 qt_config->beginGroup(QStringLiteral("Shortcuts"));
1024
1025 // Lengths of UISettings::values.shortcuts & default_hotkeys are same.
1026 // However, their ordering must also be the same.
1027 for (std::size_t i = 0; i < default_hotkeys.size(); i++) {
1028 auto [name, group, shortcut] = UISettings::values.shortcuts[i];
1029 qt_config->beginGroup(group);
1030 qt_config->beginGroup(name);
1031 WriteSetting(QStringLiteral("KeySeq"), shortcut.first, default_hotkeys[i].shortcut.first);
1032 WriteSetting(QStringLiteral("Context"), shortcut.second,
1033 default_hotkeys[i].shortcut.second);
1034 qt_config->endGroup();
1035 qt_config->endGroup();
1036 }
1037
1038 qt_config->endGroup();
1039 }
1040
SaveSystemValues()1041 void Config::SaveSystemValues() {
1042 qt_config->beginGroup(QStringLiteral("System"));
1043
1044 WriteSetting(QStringLiteral("is_new_3ds"), Settings::values.is_new_3ds, true);
1045 WriteSetting(QStringLiteral("region_value"), Settings::values.region_value,
1046 Settings::REGION_VALUE_AUTO_SELECT);
1047 WriteSetting(QStringLiteral("init_clock"), static_cast<u32>(Settings::values.init_clock),
1048 static_cast<u32>(Settings::InitClock::SystemTime));
1049 WriteSetting(QStringLiteral("init_time"),
1050 static_cast<unsigned long long>(Settings::values.init_time), 946681277ULL);
1051
1052 qt_config->endGroup();
1053 }
1054
SaveVideoDumpingValues()1055 void Config::SaveVideoDumpingValues() {
1056 qt_config->beginGroup(QStringLiteral("VideoDumping"));
1057
1058 WriteSetting(QStringLiteral("output_format"),
1059 QString::fromStdString(Settings::values.output_format), QStringLiteral("webm"));
1060 WriteSetting(QStringLiteral("format_options"),
1061 QString::fromStdString(Settings::values.format_options));
1062 WriteSetting(QStringLiteral("video_encoder"),
1063 QString::fromStdString(Settings::values.video_encoder),
1064 QStringLiteral("libvpx-vp9"));
1065 WriteSetting(QStringLiteral("video_encoder_options"),
1066 QString::fromStdString(Settings::values.video_encoder_options),
1067 DEFAULT_VIDEO_ENCODER_OPTIONS);
1068 WriteSetting(QStringLiteral("video_bitrate"),
1069 static_cast<unsigned long long>(Settings::values.video_bitrate), 2500000);
1070 WriteSetting(QStringLiteral("audio_encoder"),
1071 QString::fromStdString(Settings::values.audio_encoder),
1072 QStringLiteral("libvorbis"));
1073 WriteSetting(QStringLiteral("audio_encoder_options"),
1074 QString::fromStdString(Settings::values.audio_encoder_options),
1075 DEFAULT_AUDIO_ENCODER_OPTIONS);
1076 WriteSetting(QStringLiteral("audio_bitrate"),
1077 static_cast<unsigned long long>(Settings::values.audio_bitrate), 64000);
1078
1079 qt_config->endGroup();
1080 }
1081
SaveUIValues()1082 void Config::SaveUIValues() {
1083 qt_config->beginGroup(QStringLiteral("UI"));
1084
1085 WriteSetting(QStringLiteral("theme"), UISettings::values.theme,
1086 QString::fromUtf8(UISettings::themes[0].second));
1087 WriteSetting(QStringLiteral("enable_discord_presence"),
1088 UISettings::values.enable_discord_presence, true);
1089 WriteSetting(QStringLiteral("screenshot_resolution_factor"),
1090 UISettings::values.screenshot_resolution_factor, 0);
1091
1092 SaveUpdaterValues();
1093 SaveUILayoutValues();
1094 SaveUIGameListValues();
1095 SavePathValues();
1096 SaveShortcutValues();
1097 SaveMultiplayerValues();
1098
1099 WriteSetting(QStringLiteral("singleWindowMode"), UISettings::values.single_window_mode, true);
1100 WriteSetting(QStringLiteral("fullscreen"), UISettings::values.fullscreen, false);
1101 WriteSetting(QStringLiteral("displayTitleBars"), UISettings::values.display_titlebar, true);
1102 WriteSetting(QStringLiteral("showFilterBar"), UISettings::values.show_filter_bar, true);
1103 WriteSetting(QStringLiteral("showStatusBar"), UISettings::values.show_status_bar, true);
1104 WriteSetting(QStringLiteral("confirmClose"), UISettings::values.confirm_before_closing, true);
1105 WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true);
1106 WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0);
1107 WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false);
1108 WriteSetting(QStringLiteral("pauseWhenInBackground"),
1109 UISettings::values.pause_when_in_background, false);
1110 WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false);
1111
1112 qt_config->endGroup();
1113 }
1114
SaveUIGameListValues()1115 void Config::SaveUIGameListValues() {
1116 qt_config->beginGroup(QStringLiteral("GameList"));
1117
1118 WriteSetting(QStringLiteral("iconSize"),
1119 static_cast<int>(UISettings::values.game_list_icon_size), 2);
1120 WriteSetting(QStringLiteral("row1"), static_cast<int>(UISettings::values.game_list_row_1), 2);
1121 WriteSetting(QStringLiteral("row2"), static_cast<int>(UISettings::values.game_list_row_2), 0);
1122 WriteSetting(QStringLiteral("hideNoIcon"), UISettings::values.game_list_hide_no_icon, false);
1123 WriteSetting(QStringLiteral("singleLineMode"), UISettings::values.game_list_single_line_mode,
1124 false);
1125
1126 qt_config->endGroup();
1127 }
1128
SaveUILayoutValues()1129 void Config::SaveUILayoutValues() {
1130 qt_config->beginGroup(QStringLiteral("UILayout"));
1131
1132 WriteSetting(QStringLiteral("geometry"), UISettings::values.geometry);
1133 WriteSetting(QStringLiteral("state"), UISettings::values.state);
1134 WriteSetting(QStringLiteral("geometryRenderWindow"), UISettings::values.renderwindow_geometry);
1135 WriteSetting(QStringLiteral("gameListHeaderState"), UISettings::values.gamelist_header_state);
1136 WriteSetting(QStringLiteral("microProfileDialogGeometry"),
1137 UISettings::values.microprofile_geometry);
1138 WriteSetting(QStringLiteral("microProfileDialogVisible"),
1139 UISettings::values.microprofile_visible, false);
1140
1141 qt_config->endGroup();
1142 }
1143
SaveUpdaterValues()1144 void Config::SaveUpdaterValues() {
1145 qt_config->beginGroup(QStringLiteral("Updater"));
1146
1147 WriteSetting(QStringLiteral("check_for_update_on_start"),
1148 UISettings::values.check_for_update_on_start, true);
1149 WriteSetting(QStringLiteral("update_on_close"), UISettings::values.update_on_close, false);
1150
1151 qt_config->endGroup();
1152 }
1153
SaveWebServiceValues()1154 void Config::SaveWebServiceValues() {
1155 qt_config->beginGroup(QStringLiteral("WebService"));
1156
1157 WriteSetting(QStringLiteral("enable_telemetry"), Settings::values.enable_telemetry, true);
1158 WriteSetting(QStringLiteral("web_api_url"),
1159 QString::fromStdString(Settings::values.web_api_url),
1160 QStringLiteral("https://api.citra-emu.org"));
1161 WriteSetting(QStringLiteral("citra_username"),
1162 QString::fromStdString(Settings::values.citra_username));
1163 WriteSetting(QStringLiteral("citra_token"),
1164 QString::fromStdString(Settings::values.citra_token));
1165
1166 qt_config->endGroup();
1167 }
1168
ReadSetting(const QString & name) const1169 QVariant Config::ReadSetting(const QString& name) const {
1170 return qt_config->value(name);
1171 }
1172
ReadSetting(const QString & name,const QVariant & default_value) const1173 QVariant Config::ReadSetting(const QString& name, const QVariant& default_value) const {
1174 QVariant result;
1175 if (qt_config->value(name + QStringLiteral("/default"), false).toBool()) {
1176 result = default_value;
1177 } else {
1178 result = qt_config->value(name, default_value);
1179 }
1180 return result;
1181 }
1182
WriteSetting(const QString & name,const QVariant & value)1183 void Config::WriteSetting(const QString& name, const QVariant& value) {
1184 qt_config->setValue(name, value);
1185 }
1186
WriteSetting(const QString & name,const QVariant & value,const QVariant & default_value)1187 void Config::WriteSetting(const QString& name, const QVariant& value,
1188 const QVariant& default_value) {
1189 qt_config->setValue(name + QStringLiteral("/default"), value == default_value);
1190 qt_config->setValue(name, value);
1191 }
1192
Reload()1193 void Config::Reload() {
1194 ReadValues();
1195 // To apply default value changes
1196 SaveValues();
1197 Settings::Apply();
1198 }
1199
Save()1200 void Config::Save() {
1201 SaveValues();
1202 }
1203