14 enum class ReloadType {
27 static const std::map<std::string, std::pair<ReloadType, std::string>> settingsMetadata;
30 std::string VPXTablesPath =
"$HOME/Games/VPX_Tables/";
31 std::string VPinballXPath =
"$HOME/Games/vpinball/build/VPinballX_BGFX";
32 std::string vpxIniPath =
"";
33 std::string vpxStartArgs =
"";
34 std::string vpxEndArgs =
"";
35 bool autoPatchTables =
false;
38 float dpiScale = 1.0f;
39 bool enableDpiScaling =
true;
42 std::string defaultPlayfieldImage =
"img/default_table.png";
43 std::string defaultBackglassImage =
"img/default_backglass.png";
44 std::string defaultDmdImage =
"img/default_dmd.png";
45 std::string defaultWheelImage =
"img/default_wheel.png";
46 std::string defaultTopperImage =
"img/default_topper.png";
47 std::string defaultPlayfieldVideo =
"img/default_table.mp4";
48 std::string defaultBackglassVideo =
"img/default_backglass.mp4";
49 std::string defaultDmdVideo =
"img/default_dmd.mp4";
50 std::string defaultTopperVideo =
"img/default_topper.mp4";
53 std::string customPlayfieldImage =
"images/table.png";
54 std::string customBackglassImage =
"images/backglass.png";
55 std::string customDmdImage =
"images/dmd.png";
56 std::string customWheelImage =
"images/wheel.png";
57 std::string customTopperImage =
"images/topper.png";
58 std::string customPlayfieldVideo =
"video/table.mp4";
59 std::string customBackglassVideo =
"video/backglass.mp4";
60 std::string customDmdVideo =
"video/dmd.mp4";
61 std::string customTopperVideo =
"images/topper.mp4";
62 std::string tableMusic =
"audio/music.mp3";
63 std::string customLaunchSound =
"audio/launch.mp3";
66 std::string videoBackend =
"ffmpeg";
68 bool forceSoftwareRenderer =
false;
69 bool useVPinballXIni =
false;
70 int playfieldWindowWidth = 1080;
71 int playfieldWindowHeight = 1920;
75 bool showBackglass =
true;
76 int backglassWindowWidth = 1024;
77 int backglassWindowHeight = 768;
82 int dmdWindowWidth = 1024;
83 int dmdWindowHeight = 256;
87 bool showTopper =
false;
88 int topperWindowWidth = 512;
89 int topperWindowHeight = 128;
90 int topperWindowX = -1;
91 int topperWindowY = -1;
94 std::string titleSource =
"filename";
95 bool fetchVPSdb =
false;
96 bool useVpxtool =
false;
97 bool ignoreScanners =
false;
98 bool forceRebuildMetadata =
false;
99 std::string titleSortBy =
"title";
101 float titleWeight = 0.6f;
102 float yearWeight = 0.2f;
103 float manufacturerWeight = 0.1f;
104 float romWeight = 0.25f;
105 float titleThreshold = 0.55f;
106 float confidenceThreshold = 0.6f;
109 bool showMetadata =
false;
110 float metadataPanelWidth = 0.7f;
111 float metadataPanelHeight = 0.5f;
112 float metadataPanelAlpha = 0.6f;
114 bool showArrowHint =
true;
115 float arrowHintWidth = 20.0f;
116 float arrowHintHeight = 100.0f;
117 float arrowThickness = 4.0f;
118 float arrowAlpha = 0.6f;
119 float arrowGlow = 1.5f;
120 SDL_Color arrowGlowColor = {200, 200, 200, 255};
121 SDL_Color arrowColorTop = {100, 100, 100, 255};
122 SDL_Color arrowColorBottom = {150, 150, 150, 255};
124 bool showScrollbar =
true;
125 float scrollbarWidth = 12.0f;
126 float thumbWidth = 15.0f;
127 float scrollbarLength = 0.5f;
128 SDL_Color scrollbarColor = {50, 50, 50, 200};
129 SDL_Color scrollbarThumbColor = {50, 150, 150, 255};
132 bool showWheel =
true;
133 std::string wheelWindow =
"playfield";
134 bool showTitle =
true;
135 std::string titleWindow =
"playfield";
136 std::string fontPath =
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf";
137 SDL_Color fontColor = {255, 255, 255, 255};
138 SDL_Color fontBgColor = {0, 0, 0, 128};
144 bool fetchVpinMediaDb =
false;
145 bool resizeToWindows =
false;
146 bool forceImagesOnly =
false;
148 int wheelMediaHeight = 350;
149 int wheelMediaWidth = 350;
150 int wheelMediaX = 720;
151 int wheelMediaY = 1550;
153 int playfieldMediaWidth = 1080;
154 int playfieldMediaHeight = 1920;
155 int playfieldMediaX = 0;
156 int playfieldMediaY = 0;
157 int playfieldRotation = 0;
159 int backglassMediaWidth = 1024;
160 int backglassMediaHeight = 768;
161 int backglassMediaX = 0;
162 int backglassMediaY = 0;
163 int backglassRotation = 0;
165 int dmdMediaWidth = 1024;
166 int dmdMediaHeight = 256;
171 int topperMediaWidth = 512;
172 int topperMediaHeight = 128;
173 int topperMediaX = 0;
174 int topperMediaY = 0;
175 int topperRotation = 0;
178 bool masterMute =
false;
179 float masterVol = 100.0f;
180 bool mediaAudioMute =
false;
181 float mediaAudioVol = 60.0f;
182 bool tableMusicMute =
false;
183 float tableMusicVol = 60.0f;
184 bool interfaceAudioMute =
false;
185 float interfaceAudioVol = 60.0f;
186 bool interfaceAmbienceMute =
false;
187 float interfaceAmbienceVol = 60.0f;
190 std::string scrollNormalSound =
"snd/scroll_normal.mp3";
191 std::string scrollFastSound =
"snd/scroll_fast.mp3";
192 std::string scrollJumpSound =
"snd/scroll_jump.mp3";
193 std::string scrollRandomSound =
"snd/scroll_random.mp3";
194 std::string launchTableSound =
"snd/launch_table.mp3";
195 std::string launchScreenshotSound =
"snd/launch_screenshot.mp3";
196 std::string panelToggleSound =
"snd/panel_toggle.mp3";
197 std::string screenshotTakeSound =
"snd/screenshot_take.mp3";
198 std::string ambienceSound =
"snd/interface_ambience.mp3";
202 std::string vpxSubCmd =
"-Play";
203 std::string vpsDbPath =
"data/vpsdb.json";
204 std::string vpsDbUpdateFrequency =
"startup";
205 std::string vpsDbLastUpdated =
"data/lastUpdated.json";
206 std::string vpxtoolBin =
"";
207 std::string vpxtoolIndex =
"vpxtool_index.json";
208 std::string indexPath =
"data/asapcab_index.json";
209 int screenshotWait = 4;
211 float configUIWidth = 0.7f;
212 float configUIHeight = 0.5f;
215 std::map<std::string, std::string> keybinds_ = {
216 {
"Previous Table",
"Left Shift"},
217 {
"Next Table",
"Right Shift"},
218 {
"Fast Previous Table",
"Left Ctrl"},
219 {
"Fast Next Table",
"Right Ctrl"},
220 {
"Jump Next Letter",
"/"},
221 {
"Jump Previous Letter",
"Z"},
222 {
"Random Table",
"R"},
223 {
"Launch Table",
"Return"},
224 {
"Toggle Config",
"C"},
226 {
"Screenshot Mode",
"S"},
227 {
"Screenshot Key",
"S"},
228 {
"Screenshot Quit",
"Q"},
229 {
"Toggle Editor",
"M"},
230 {
"Toggle Catalog",
"N"}
234 void applyPostProcessing(
const std::string& exeDirectory) {
236 this->exeDir = exeDirectory;
239 VPXTablesPath = resolvePath(VPXTablesPath, exeDirectory);
240 VPinballXPath = resolvePath(VPinballXPath, exeDirectory);
243 std::vector<std::string> pathFields = {
245 "defaultPlayfieldImage",
"defaultBackglassImage",
"defaultDmdImage",
246 "defaultWheelImage",
"defaultTopperImage",
"defaultPlayfieldVideo",
247 "defaultBackglassVideo",
"defaultDmdVideo",
"defaultTopperVideo",
250 "scrollNormalSound",
"scrollFastSound",
"scrollJumpSound",
251 "scrollRandomSound",
"launchTableSound",
"launchScreenshotSound",
252 "panelToggleSound",
"screenshotTakeSound",
"ambienceSound",
255 "vpsDbPath",
"vpsDbLastUpdated",
"indexPath",
259 for (
const auto& field : pathFields) {
260 if (field ==
"defaultPlayfieldImage") defaultPlayfieldImage = resolvePath(defaultPlayfieldImage, exeDir);
261 else if (field ==
"defaultBackglassImage") defaultBackglassImage = resolvePath(defaultBackglassImage, exeDir);
262 else if (field ==
"defaultDmdImage") defaultDmdImage = resolvePath(defaultDmdImage, exeDir);
263 else if (field ==
"defaultWheelImage") defaultWheelImage = resolvePath(defaultWheelImage, exeDir);
264 else if (field ==
"defaultTopperImage") defaultTopperImage = resolvePath(defaultTopperImage, exeDir);
265 else if (field ==
"defaultPlayfieldVideo") defaultPlayfieldVideo = resolvePath(defaultPlayfieldVideo, exeDir);
266 else if (field ==
"defaultBackglassVideo") defaultBackglassVideo = resolvePath(defaultBackglassVideo, exeDir);
267 else if (field ==
"defaultDmdVideo") defaultDmdVideo = resolvePath(defaultDmdVideo, exeDir);
268 else if (field ==
"defaultTopperVideo") defaultTopperVideo = resolvePath(defaultTopperVideo, exeDir);
270 else if (field ==
"scrollNormalSound") scrollNormalSound = resolvePath(scrollNormalSound, exeDir);
271 else if (field ==
"scrollFastSound") scrollFastSound = resolvePath(scrollFastSound, exeDir);
272 else if (field ==
"scrollJumpSound") scrollJumpSound = resolvePath(scrollJumpSound, exeDir);
273 else if (field ==
"scrollRandomSound") scrollRandomSound = resolvePath(scrollRandomSound, exeDir);
274 else if (field ==
"launchTableSound") launchTableSound = resolvePath(launchTableSound, exeDir);
275 else if (field ==
"launchScreenshotSound") launchScreenshotSound = resolvePath(launchScreenshotSound, exeDir);
276 else if (field ==
"panelToggleSound") panelToggleSound = resolvePath(panelToggleSound, exeDir);
277 else if (field ==
"screenshotTakeSound") screenshotTakeSound = resolvePath(screenshotTakeSound, exeDir);
278 else if (field ==
"ambienceSound") ambienceSound = resolvePath(ambienceSound, exeDir);
280 else if (field ==
"vpsDbPath") vpsDbPath = resolvePath(vpsDbPath, exeDir);
281 else if (field ==
"vpsDbLastUpdated") vpsDbLastUpdated = resolvePath(vpsDbLastUpdated, exeDir);
282 else if (field ==
"indexPath") indexPath = resolvePath(indexPath, exeDir);
286 if (enableDpiScaling) {
287 fontSize =
static_cast<int>(
static_cast<float>(fontSize) * dpiScale);
291 if (fontPath.empty() || !std::filesystem::exists(fontPath)) {
292 std::vector<std::string> candidates = {
293 "/usr/share/fonts/truetype/dejavu/Arial.ttf",
294 "/usr/share/fonts/truetype/dejavu/FreeSans.ttf",
295 "/usr/share/fonts/truetype/dejavu/FreeMono.ttf",
296 "/usr/share/fonts/TTF/DejaVuSans.ttf",
297 "/usr/share/fonts/TTF/Arial.ttf",
298 "/usr/share/fonts/TTF/FreeSans.ttf",
299 "/usr/share/fonts/TTF/FreeMono.ttf",
300 "/usr/share/fonts/dejavu/DejaVuSans.ttf",
301 "/usr/share/fonts/arial/Arial.ttf",
302 "/usr/share/fonts/freesans/FreeSans.ttf",
303 "/usr/share/fonts/freemono/FreeMono.ttf",
304 "/usr/local/share/fonts/DejaVuSans.ttf",
305 "/usr/local/share/fonts/Arial.ttf",
306 "/usr/local/share/fonts/FreeSans.ttf",
307 "/usr/local/share/fonts/FreeMono.ttf",
308 "/usr/share/fonts/TTF/HackNerdFont-Regular.ttf"
310 for (
const auto& path : candidates) {
311 if (std::filesystem::exists(path)) {
321 std::string resolvePath(
const std::string& value,
const std::string& exeDirectory)
const {
322 std::string result = value;
324 const char* home = std::getenv(
"HOME");
327 while ((pos = result.find(
"$HOME")) != std::string::npos) {
328 result.replace(pos, 5, home);
330 if (!result.empty() && result[0] ==
'~') {
331 result.replace(0, 1, home);
335 if (result.empty())
return exeDirectory + value;
336 if (result[0] ==
'/' || result[0] ==
'\\')
return result;
337 return exeDirectory + result;
342 friend void to_json(nlohmann::json& j,
const Settings& s) {
345 {
"VPXTablesPath", s.VPXTablesPath},
346 {
"VPinballXPath", s.VPinballXPath},
347 {
"vpxIniPath", s.vpxIniPath},
348 {
"vpxStartArgs", s.vpxStartArgs},
349 {
"vpxEndArgs", s.vpxEndArgs},
350 {
"autoPatchTables", s.autoPatchTables}
353 {
"dpiScale", s.dpiScale},
354 {
"enableDpiScaling", s.enableDpiScaling}
357 {
"defaultPlayfieldImage", s.defaultPlayfieldImage},
358 {
"defaultBackglassImage", s.defaultBackglassImage},
359 {
"defaultDmdImage", s.defaultDmdImage},
360 {
"defaultWheelImage", s.defaultWheelImage},
361 {
"defaultTopperImage", s.defaultTopperImage},
362 {
"defaultPlayfieldVideo", s.defaultPlayfieldVideo},
363 {
"defaultBackglassVideo", s.defaultBackglassVideo},
364 {
"defaultDmdVideo", s.defaultDmdVideo},
365 {
"defaultTopperVideo", s.defaultTopperVideo}
368 {
"customPlayfieldImage", s.customPlayfieldImage},
369 {
"customBackglassImage", s.customBackglassImage},
370 {
"customDmdImage", s.customDmdImage},
371 {
"customWheelImage", s.customWheelImage},
372 {
"customTopperImage", s.customTopperImage},
373 {
"customPlayfieldVideo", s.customPlayfieldVideo},
374 {
"customBackglassVideo", s.customBackglassVideo},
375 {
"customDmdVideo", s.customDmdVideo},
376 {
"customTopperVideo", s.customTopperVideo},
377 {
"tableMusic", s.tableMusic},
378 {
"customLaunchSound", s.customLaunchSound}
381 {
"videoBackend", s.videoBackend},
382 {
"useVPinballXIni", s.useVPinballXIni},
383 {
"playfieldWindowWidth", s.playfieldWindowWidth},
384 {
"playfieldWindowHeight", s.playfieldWindowHeight},
385 {
"playfieldX", s.playfieldX},
386 {
"playfieldY", s.playfieldY},
387 {
"showBackglass", s.showBackglass},
388 {
"backglassWindowWidth", s.backglassWindowWidth},
389 {
"backglassWindowHeight", s.backglassWindowHeight},
390 {
"backglassX", s.backglassX},
391 {
"backglassY", s.backglassY},
392 {
"showDMD", s.showDMD},
393 {
"dmdWindowWidth", s.dmdWindowWidth},
394 {
"dmdWindowHeight", s.dmdWindowHeight},
397 {
"showTopper", s.showTopper},
398 {
"topperWindowWidth", s.topperWindowWidth},
399 {
"topperWindowHeight", s.topperWindowHeight},
400 {
"topperWindowX", s.topperWindowX},
401 {
"topperWindowY", s.topperWindowY}
404 {
"titleSource", s.titleSource},
405 {
"fetchVPSdb", s.fetchVPSdb},
406 {
"useVpxtool", s.useVpxtool},
407 {
"ignoreScanners", s.ignoreScanners},
408 {
"forceRebuildMetadata", s.forceRebuildMetadata},
409 {
"titleSortBy", s.titleSortBy},
410 {
"titleWeight", s.titleWeight},
411 {
"yearWeight", s.yearWeight},
412 {
"manufacturerWeight", s.manufacturerWeight},
413 {
"romWeight", s.romWeight},
414 {
"titleThreshold", s.titleThreshold},
415 {
"confidenceThreshold", s.confidenceThreshold}
418 {
"showMetadata", s.showMetadata},
419 {
"metadataPanelWidth", s.metadataPanelWidth},
420 {
"metadataPanelHeight", s.metadataPanelHeight},
421 {
"metadataPanelAlpha", s.metadataPanelAlpha},
422 {
"showArrowHint", s.showArrowHint},
423 {
"arrowHintWidth", s.arrowHintWidth},
424 {
"arrowHintHeight", s.arrowHintHeight},
425 {
"arrowThickness", s.arrowThickness},
426 {
"arrowAlpha", s.arrowAlpha},
427 {
"arrowGlow", s.arrowGlow},
428 {
"arrowGlowColor", {s.arrowGlowColor.r, s.arrowGlowColor.g, s.arrowGlowColor.b, s.arrowGlowColor.a}},
429 {
"arrowColorTop", {s.arrowColorTop.r, s.arrowColorTop.g, s.arrowColorTop.b, s.arrowColorTop.a}},
430 {
"arrowColorBottom", {s.arrowColorBottom.r, s.arrowColorBottom.g, s.arrowColorBottom.b, s.arrowColorBottom.a}},
431 {
"showScrollbar", s.showScrollbar},
432 {
"scrollbarWidth", s.scrollbarWidth},
433 {
"thumbWidth", s.thumbWidth},
434 {
"scrollbarLength", s.scrollbarLength},
435 {
"scrollbarColor", {s.scrollbarColor.r, s.scrollbarColor.g, s.scrollbarColor.b, s.scrollbarColor.a}},
436 {
"scrollbarThumbColor", {s.scrollbarThumbColor.r, s.scrollbarThumbColor.g, s.scrollbarThumbColor.b, s.scrollbarThumbColor.a}}
439 {
"showWheel", s.showWheel},
440 {
"wheelWindow", s.wheelWindow},
441 {
"showTitle", s.showTitle},
442 {
"titleWindow", s.titleWindow},
443 {
"fontPath", s.fontPath},
444 {
"fontColor", {s.fontColor.r, s.fontColor.g, s.fontColor.b, s.fontColor.a}},
445 {
"fontBgColor", {s.fontBgColor.r, s.fontBgColor.g, s.fontBgColor.b, s.fontBgColor.a}},
446 {
"fontSize", s.fontSize},
447 {
"titleX", s.titleX},
450 {
"MediaDimensions", {
451 {
"fetchVpinMediaDb", s.fetchVpinMediaDb},
452 {
"resizeToWindows", s.resizeToWindows},
453 {
"forceImagesOnly", s.forceImagesOnly},
454 {
"wheelMediaHeight", s.wheelMediaHeight},
455 {
"wheelMediaWidth", s.wheelMediaWidth},
456 {
"wheelMediaX", s.wheelMediaX},
457 {
"wheelMediaY", s.wheelMediaY},
458 {
"playfieldMediaWidth", s.playfieldMediaWidth},
459 {
"playfieldMediaHeight", s.playfieldMediaHeight},
460 {
"playfieldMediaX", s.playfieldMediaX},
461 {
"playfieldMediaY", s.playfieldMediaY},
462 {
"playfieldRotation", s.playfieldRotation},
463 {
"backglassMediaWidth", s.backglassMediaWidth},
464 {
"backglassMediaHeight", s.backglassMediaHeight},
465 {
"backglassMediaX", s.backglassMediaX},
466 {
"backglassMediaY", s.backglassMediaY},
467 {
"backglassRotation", s.backglassRotation},
468 {
"dmdMediaWidth", s.dmdMediaWidth},
469 {
"dmdMediaHeight", s.dmdMediaHeight},
470 {
"dmdMediaX", s.dmdMediaX},
471 {
"dmdMediaY", s.dmdMediaY},
472 {
"dmdRotation", s.dmdRotation},
473 {
"topperMediaWidth", s.topperMediaWidth},
474 {
"topperMediaHeight", s.topperMediaHeight},
475 {
"topperMediaX", s.topperMediaX},
476 {
"topperMediaY", s.topperMediaY},
477 {
"topperRotation", s.topperRotation}
480 {
"masterMute", s.masterMute},
481 {
"masterVol", s.masterVol},
482 {
"mediaAudioMute", s.mediaAudioMute},
483 {
"mediaAudioVol", s.mediaAudioVol},
484 {
"tableMusicMute", s.tableMusicMute},
485 {
"tableMusicVol", s.tableMusicVol},
486 {
"interfaceAudioMute", s.interfaceAudioMute},
487 {
"interfaceAudioVol", s.interfaceAudioVol},
488 {
"interfaceAmbienceMute", s.interfaceAmbienceMute},
489 {
"interfaceAmbienceVol", s.interfaceAmbienceVol}
492 {
"scrollNormalSound", s.scrollNormalSound},
493 {
"scrollFastSound", s.scrollFastSound},
494 {
"scrollJumpSound", s.scrollJumpSound},
495 {
"scrollRandomSound", s.scrollRandomSound},
496 {
"launchTableSound", s.launchTableSound},
497 {
"launchScreenshotSound", s.launchScreenshotSound},
498 {
"panelToggleSound", s.panelToggleSound},
499 {
"screenshotTakeSound", s.screenshotTakeSound},
500 {
"ambienceSound", s.ambienceSound}
503 {
"exeDir", s.exeDir},
504 {
"vpxSubCmd", s.vpxSubCmd},
505 {
"vpsDbPath", s.vpsDbPath},
506 {
"vpsDbUpdateFrequency", s.vpsDbUpdateFrequency},
507 {
"vpsDbLastUpdated", s.vpsDbLastUpdated},
508 {
"vpxtoolBin", s.vpxtoolBin},
509 {
"vpxtoolIndex", s.vpxtoolIndex},
510 {
"indexPath", s.indexPath},
511 {
"screenshotWait", s.screenshotWait},
512 {
"configUIWidth", s.configUIWidth},
513 {
"configUIHeight", s.configUIHeight}
515 {
"Keybinds", s.keybinds_}
519 friend void from_json(
const nlohmann::json& j,
Settings& s) {
521 s.VPXTablesPath = j.value(
"VPX", nlohmann::json{}).value(
"VPXTablesPath", s.VPXTablesPath);
522 s.VPinballXPath = j.value(
"VPX", nlohmann::json{}).value(
"VPinballXPath", s.VPinballXPath);
523 s.vpxIniPath = j.value(
"VPX", nlohmann::json{}).value(
"vpxIniPath", s.vpxIniPath);
524 s.vpxStartArgs = j.value(
"VPX", nlohmann::json{}).value(
"vpxStartArgs", s.vpxStartArgs);
525 s.vpxEndArgs = j.value(
"VPX", nlohmann::json{}).value(
"vpxEndArgs", s.vpxEndArgs);
526 s.autoPatchTables = j.value(
"VPX", nlohmann::json{}).value(
"autoPatchTables", s.autoPatchTables);
529 s.dpiScale = j.value(
"DPISettings", nlohmann::json{}).value(
"dpiScale", s.dpiScale);
530 s.enableDpiScaling = j.value(
"DPISettings", nlohmann::json{}).value(
"enableDpiScaling", s.enableDpiScaling);
533 s.defaultPlayfieldImage = j.value(
"DefaultMedia", nlohmann::json{}).value(
"defaultPlayfieldImage", s.defaultPlayfieldImage);
534 s.defaultBackglassImage = j.value(
"DefaultMedia", nlohmann::json{}).value(
"defaultBackglassImage", s.defaultBackglassImage);
535 s.defaultDmdImage = j.value(
"DefaultMedia", nlohmann::json{}).value(
"defaultDmdImage", s.defaultDmdImage);
536 s.defaultWheelImage = j.value(
"DefaultMedia", nlohmann::json{}).value(
"defaultWheelImage", s.defaultWheelImage);
537 s.defaultTopperImage = j.value(
"DefaultMedia", nlohmann::json{}).value(
"defaultTopperImage", s.defaultTopperImage);
538 s.defaultPlayfieldVideo = j.value(
"DefaultMedia", nlohmann::json{}).value(
"defaultPlayfieldVideo", s.defaultPlayfieldVideo);
539 s.defaultBackglassVideo = j.value(
"DefaultMedia", nlohmann::json{}).value(
"defaultBackglassVideo", s.defaultBackglassVideo);
540 s.defaultDmdVideo = j.value(
"DefaultMedia", nlohmann::json{}).value(
"defaultDmdVideo", s.defaultDmdVideo);
541 s.defaultTopperVideo = j.value(
"DefaultMedia", nlohmann::json{}).value(
"defaultTopperVideo", s.defaultTopperVideo);
544 s.customPlayfieldImage = j.value(
"CustomMedia", nlohmann::json{}).value(
"customPlayfieldImage", s.customPlayfieldImage);
545 s.customBackglassImage = j.value(
"CustomMedia", nlohmann::json{}).value(
"customBackglassImage", s.customBackglassImage);
546 s.customDmdImage = j.value(
"CustomMedia", nlohmann::json{}).value(
"customDmdImage", s.customDmdImage);
547 s.customWheelImage = j.value(
"CustomMedia", nlohmann::json{}).value(
"customWheelImage", s.customWheelImage);
548 s.customTopperImage = j.value(
"CustomMedia", nlohmann::json{}).value(
"customTopperImage", s.customTopperImage);
549 s.customPlayfieldVideo = j.value(
"CustomMedia", nlohmann::json{}).value(
"customPlayfieldVideo", s.customPlayfieldVideo);
550 s.customBackglassVideo = j.value(
"CustomMedia", nlohmann::json{}).value(
"customBackglassVideo", s.customBackglassVideo);
551 s.customDmdVideo = j.value(
"CustomMedia", nlohmann::json{}).value(
"customDmdVideo", s.customDmdVideo);
552 s.customTopperVideo = j.value(
"CustomMedia", nlohmann::json{}).value(
"customTopperVideo", s.customTopperVideo);
553 s.tableMusic = j.value(
"CustomMedia", nlohmann::json{}).value(
"tableMusic", s.tableMusic);
554 s.customLaunchSound = j.value(
"CustomMedia", nlohmann::json{}).value(
"customLaunchSound", s.customLaunchSound);
557 s.videoBackend = j.value(
"WindowSettings", nlohmann::json{}).value(
"videoBackend", s.videoBackend);
558 s.useVPinballXIni = j.value(
"WindowSettings", nlohmann::json{}).value(
"useVPinballXIni", s.useVPinballXIni);
559 s.playfieldWindowWidth = j.value(
"WindowSettings", nlohmann::json{}).value(
"playfieldWindowWidth", s.playfieldWindowWidth);
560 s.playfieldWindowHeight = j.value(
"WindowSettings", nlohmann::json{}).value(
"playfieldWindowHeight", s.playfieldWindowHeight);
561 s.playfieldX = j.value(
"WindowSettings", nlohmann::json{}).value(
"playfieldX", s.playfieldX);
562 s.playfieldY = j.value(
"WindowSettings", nlohmann::json{}).value(
"playfieldY", s.playfieldY);
563 s.showBackglass = j.value(
"WindowSettings", nlohmann::json{}).value(
"showBackglass", s.showBackglass);
564 s.backglassWindowWidth = j.value(
"WindowSettings", nlohmann::json{}).value(
"backglassWindowWidth", s.backglassWindowWidth);
565 s.backglassWindowHeight = j.value(
"WindowSettings", nlohmann::json{}).value(
"backglassWindowHeight", s.backglassWindowHeight);
566 s.backglassX = j.value(
"WindowSettings", nlohmann::json{}).value(
"backglassX", s.backglassX);
567 s.backglassY = j.value(
"WindowSettings", nlohmann::json{}).value(
"backglassY", s.backglassY);
568 s.showDMD = j.value(
"WindowSettings", nlohmann::json{}).value(
"showDMD", s.showDMD);
569 s.dmdWindowWidth = j.value(
"WindowSettings", nlohmann::json{}).value(
"dmdWindowWidth", s.dmdWindowWidth);
570 s.dmdWindowHeight = j.value(
"WindowSettings", nlohmann::json{}).value(
"dmdWindowHeight", s.dmdWindowHeight);
571 s.dmdX = j.value(
"WindowSettings", nlohmann::json{}).value(
"dmdX", s.dmdX);
572 s.dmdY = j.value(
"WindowSettings", nlohmann::json{}).value(
"dmdY", s.dmdY);
573 s.showTopper = j.value(
"WindowSettings", nlohmann::json{}).value(
"showTopper", s.showTopper);
574 s.topperWindowWidth = j.value(
"WindowSettings", nlohmann::json{}).value(
"topperWindowWidth", s.topperWindowWidth);
575 s.topperWindowHeight = j.value(
"WindowSettings", nlohmann::json{}).value(
"topperWindowHeight", s.topperWindowHeight);
576 s.topperWindowX = j.value(
"WindowSettings", nlohmann::json{}).value(
"topperWindowX", s.topperWindowX);
577 s.topperWindowY = j.value(
"WindowSettings", nlohmann::json{}).value(
"topperWindowY", s.topperWindowY);
580 s.titleSource = j.value(
"TableMetadata", nlohmann::json{}).value(
"titleSource", s.titleSource);
581 s.fetchVPSdb = j.value(
"TableMetadata", nlohmann::json{}).value(
"fetchVPSdb", s.fetchVPSdb);
582 s.useVpxtool = j.value(
"TableMetadata", nlohmann::json{}).value(
"useVpxtool", s.useVpxtool);
583 s.forceRebuildMetadata = j.value(
"TableMetadata", nlohmann::json{}).value(
"forceRebuildMetadata", s.forceRebuildMetadata);
584 s.ignoreScanners = j.value(
"TableMetadata", nlohmann::json{}).value(
"ignoreScanners", s.ignoreScanners);
585 s.titleSortBy = j.value(
"TableMetadata", nlohmann::json{}).value(
"titleSortBy", s.titleSortBy);
587 s.titleWeight = j.value(
"TableMetadata", nlohmann::json{}).value(
"titleWeight", s.titleWeight);
588 s.yearWeight = j.value(
"TableMetadata", nlohmann::json{}).value(
"yearWeight", s.yearWeight);
589 s.manufacturerWeight = j.value(
"TableMetadata", nlohmann::json{}).value(
"manufacturerWeight", s.manufacturerWeight);
590 s.romWeight = j.value(
"TableMetadata", nlohmann::json{}).value(
"romWeight", s.romWeight);
591 s.titleThreshold = j.value(
"TableMetadata", nlohmann::json{}).value(
"titleThreshold", s.titleThreshold);
592 s.confidenceThreshold = j.value(
"TableMetadata", nlohmann::json{}).value(
"confidenceThreshold", s.confidenceThreshold);
595 s.showMetadata = j.value(
"UIWidgets", nlohmann::json{}).value(
"showMetadata", s.showMetadata);
596 s.metadataPanelWidth = j.value(
"UIWidgets", nlohmann::json{}).value(
"metadataPanelWidth", s.metadataPanelWidth);
597 s.metadataPanelHeight = j.value(
"UIWidgets", nlohmann::json{}).value(
"metadataPanelHeight", s.metadataPanelHeight);
598 s.metadataPanelAlpha = j.value(
"UIWidgets", nlohmann::json{}).value(
"metadataPanelAlpha", s.metadataPanelAlpha);
599 s.showArrowHint = j.value(
"UIWidgets", nlohmann::json{}).value(
"showArrowHint", s.showArrowHint);
600 s.arrowHintWidth = j.value(
"UIWidgets", nlohmann::json{}).value(
"arrowHintWidth", s.arrowHintWidth);
601 s.arrowHintHeight = j.value(
"UIWidgets", nlohmann::json{}).value(
"arrowHintHeight", s.arrowHintHeight);
602 s.arrowThickness = j.value(
"UIWidgets", nlohmann::json{}).value(
"arrowThickness", s.arrowThickness);
603 s.arrowAlpha = j.value(
"UIWidgets", nlohmann::json{}).value(
"arrowAlpha", s.arrowAlpha);
604 s.arrowGlow = j.value(
"UIWidgets", nlohmann::json{}).value(
"arrowGlow", s.arrowGlow);
605 if (j.value(
"UIWidgets", nlohmann::json{}).contains(
"arrowGlowColor") && j[
"UIWidgets"][
"arrowGlowColor"].is_array() && j[
"UIWidgets"][
"arrowGlowColor"].size() == 4) {
606 s.arrowGlowColor = {
static_cast<Uint8
>(j[
"UIWidgets"][
"arrowGlowColor"][0]),
607 static_cast<Uint8
>(j[
"UIWidgets"][
"arrowGlowColor"][1]),
608 static_cast<Uint8
>(j[
"UIWidgets"][
"arrowGlowColor"][2]),
609 static_cast<Uint8
>(j[
"UIWidgets"][
"arrowGlowColor"][3])};
611 if (j.value(
"UIWidgets", nlohmann::json{}).contains(
"arrowColorTop") && j[
"UIWidgets"][
"arrowColorTop"].is_array() && j[
"UIWidgets"][
"arrowColorTop"].size() == 4) {
612 s.arrowColorTop = {
static_cast<Uint8
>(j[
"UIWidgets"][
"arrowColorTop"][0]),
613 static_cast<Uint8
>(j[
"UIWidgets"][
"arrowColorTop"][1]),
614 static_cast<Uint8
>(j[
"UIWidgets"][
"arrowColorTop"][2]),
615 static_cast<Uint8
>(j[
"UIWidgets"][
"arrowColorTop"][3])};
617 if (j.value(
"UIWidgets", nlohmann::json{}).contains(
"arrowColorBottom") && j[
"UIWidgets"][
"arrowColorBottom"].is_array() && j[
"UIWidgets"][
"arrowColorBottom"].size() == 4) {
618 s.arrowColorBottom = {
static_cast<Uint8
>(j[
"UIWidgets"][
"arrowColorBottom"][0]),
619 static_cast<Uint8
>(j[
"UIWidgets"][
"arrowColorBottom"][1]),
620 static_cast<Uint8
>(j[
"UIWidgets"][
"arrowColorBottom"][2]),
621 static_cast<Uint8
>(j[
"UIWidgets"][
"arrowColorBottom"][3])};
623 s.showScrollbar = j.value(
"UIWidgets", nlohmann::json{}).value(
"showScrollbar", s.showScrollbar);
624 s.scrollbarWidth = j.value(
"UIWidgets", nlohmann::json{}).value(
"scrollbarWidth", s.scrollbarWidth);
625 s.thumbWidth = j.value(
"UIWidgets", nlohmann::json{}).value(
"thumbWidth", s.thumbWidth);
626 s.scrollbarLength = j.value(
"UIWidgets", nlohmann::json{}).value(
"scrollbarLength", s.scrollbarLength);
627 if (j.value(
"UIWidgets", nlohmann::json{}).contains(
"scrollbarColor") && j[
"UIWidgets"][
"scrollbarColor"].is_array() && j[
"UIWidgets"][
"scrollbarColor"].size() == 4) {
628 s.scrollbarColor = {
static_cast<Uint8
>(j[
"UIWidgets"][
"scrollbarColor"][0]),
629 static_cast<Uint8
>(j[
"UIWidgets"][
"scrollbarColor"][1]),
630 static_cast<Uint8
>(j[
"UIWidgets"][
"scrollbarColor"][2]),
631 static_cast<Uint8
>(j[
"UIWidgets"][
"scrollbarColor"][3])};
633 if (j.value(
"UIWidgets", nlohmann::json{}).contains(
"scrollbarThumbColor") && j[
"UIWidgets"][
"scrollbarThumbColor"].is_array() && j[
"UIWidgets"][
"scrollbarThumbColor"].size() == 4) {
634 s.scrollbarThumbColor = {
static_cast<Uint8
>(j[
"UIWidgets"][
"scrollbarThumbColor"][0]),
635 static_cast<Uint8
>(j[
"UIWidgets"][
"scrollbarThumbColor"][1]),
636 static_cast<Uint8
>(j[
"UIWidgets"][
"scrollbarThumbColor"][2]),
637 static_cast<Uint8
>(j[
"UIWidgets"][
"scrollbarThumbColor"][3])};
641 s.showWheel = j.value(
"TitleDisplay", nlohmann::json{}).value(
"showWheel", s.showWheel);
642 s.wheelWindow = j.value(
"TitleDisplay", nlohmann::json{}).value(
"wheelWindow", s.wheelWindow);
643 s.showTitle = j.value(
"TitleDisplay", nlohmann::json{}).value(
"showTitle", s.showTitle);
644 s.titleWindow = j.value(
"TitleDisplay", nlohmann::json{}).value(
"titleWindow", s.titleWindow);
645 s.fontPath = j.value(
"TitleDisplay", nlohmann::json{}).value(
"fontPath", s.fontPath);
646 if (j.value(
"TitleDisplay", nlohmann::json{}).contains(
"fontColor") && j[
"TitleDisplay"][
"fontColor"].is_array() && j[
"TitleDisplay"][
"fontColor"].size() == 4) {
647 s.fontColor = {
static_cast<Uint8
>(j[
"TitleDisplay"][
"fontColor"][0]),
648 static_cast<Uint8
>(j[
"TitleDisplay"][
"fontColor"][1]),
649 static_cast<Uint8
>(j[
"TitleDisplay"][
"fontColor"][2]),
650 static_cast<Uint8
>(j[
"TitleDisplay"][
"fontColor"][3])};
652 if (j.value(
"TitleDisplay", nlohmann::json{}).contains(
"fontBgColor") && j[
"TitleDisplay"][
"fontBgColor"].is_array() && j[
"TitleDisplay"][
"fontBgColor"].size() == 4) {
653 s.fontBgColor = {
static_cast<Uint8
>(j[
"TitleDisplay"][
"fontBgColor"][0]),
654 static_cast<Uint8
>(j[
"TitleDisplay"][
"fontBgColor"][1]),
655 static_cast<Uint8
>(j[
"TitleDisplay"][
"fontBgColor"][2]),
656 static_cast<Uint8
>(j[
"TitleDisplay"][
"fontBgColor"][3])};
658 s.fontSize = j.value(
"TitleDisplay", nlohmann::json{}).value(
"fontSize", s.fontSize);
659 s.titleX = j.value(
"TitleDisplay", nlohmann::json{}).value(
"titleX", s.titleX);
660 s.titleY = j.value(
"TitleDisplay", nlohmann::json{}).value(
"titleY", s.titleY);
663 s.fetchVpinMediaDb = j.value(
"MediaDimensions", nlohmann::json{}).value(
"fetchVpinMediaDb", s.fetchVpinMediaDb);
664 s.resizeToWindows = j.value(
"MediaDimensions", nlohmann::json{}).value(
"resizeToWindows", s.resizeToWindows);
665 s.forceImagesOnly = j.value(
"MediaDimensions", nlohmann::json{}).value(
"forceImagesOnly", s.forceImagesOnly);
666 s.wheelMediaHeight = j.value(
"MediaDimensions", nlohmann::json{}).value(
"wheelMediaHeight", s.wheelMediaHeight);
667 s.wheelMediaWidth = j.value(
"MediaDimensions", nlohmann::json{}).value(
"wheelMediaWidth", s.wheelMediaWidth);
668 s.wheelMediaX = j.value(
"MediaDimensions", nlohmann::json{}).value(
"wheelMediaX", s.wheelMediaX);
669 s.wheelMediaY = j.value(
"MediaDimensions", nlohmann::json{}).value(
"wheelMediaY", s.wheelMediaY);
670 s.playfieldMediaWidth = j.value(
"MediaDimensions", nlohmann::json{}).value(
"playfieldMediaWidth", s.playfieldMediaWidth);
671 s.playfieldMediaHeight = j.value(
"MediaDimensions", nlohmann::json{}).value(
"playfieldMediaHeight", s.playfieldMediaHeight);
672 s.playfieldMediaX = j.value(
"MediaDimensions", nlohmann::json{}).value(
"playfieldMediaX", s.playfieldMediaX);
673 s.playfieldMediaY = j.value(
"MediaDimensions", nlohmann::json{}).value(
"playfieldMediaY", s.playfieldMediaY);
674 s.playfieldRotation = j.value(
"MediaDimensions", nlohmann::json{}).value(
"playfieldRotation", s.playfieldRotation);
675 s.backglassMediaWidth = j.value(
"MediaDimensions", nlohmann::json{}).value(
"backglassMediaWidth", s.backglassMediaWidth);
676 s.backglassMediaHeight = j.value(
"MediaDimensions", nlohmann::json{}).value(
"backglassMediaHeight", s.backglassMediaHeight);
677 s.backglassMediaX = j.value(
"MediaDimensions", nlohmann::json{}).value(
"backglassMediaX", s.backglassMediaX);
678 s.backglassMediaY = j.value(
"MediaDimensions", nlohmann::json{}).value(
"backglassMediaY", s.backglassMediaY);
679 s.backglassRotation = j.value(
"MediaDimensions", nlohmann::json{}).value(
"backglassRotation", s.backglassRotation);
680 s.dmdMediaWidth = j.value(
"MediaDimensions", nlohmann::json{}).value(
"dmdMediaWidth", s.dmdMediaWidth);
681 s.dmdMediaHeight = j.value(
"MediaDimensions", nlohmann::json{}).value(
"dmdMediaHeight", s.dmdMediaHeight);
682 s.dmdMediaX = j.value(
"MediaDimensions", nlohmann::json{}).value(
"dmdMediaX", s.dmdMediaX);
683 s.dmdMediaY = j.value(
"MediaDimensions", nlohmann::json{}).value(
"dmdMediaY", s.dmdMediaY);
684 s.dmdRotation = j.value(
"MediaDimensions", nlohmann::json{}).value(
"dmdRotation", s.dmdRotation);
685 s.topperMediaWidth = j.value(
"MediaDimensions", nlohmann::json{}).value(
"topperMediaWidth", s.topperMediaWidth);
686 s.topperMediaHeight = j.value(
"MediaDimensions", nlohmann::json{}).value(
"topperMediaHeight", s.topperMediaHeight);
687 s.topperMediaX = j.value(
"MediaDimensions", nlohmann::json{}).value(
"topperMediaX", s.topperMediaX);
688 s.topperMediaY = j.value(
"MediaDimensions", nlohmann::json{}).value(
"topperMediaY", s.topperMediaY);
689 s.topperRotation = j.value(
"MediaDimensions", nlohmann::json{}).value(
"topperRotation", s.topperRotation);
692 s.masterMute = j.value(
"AudioSettings", nlohmann::json{}).value(
"masterMute", s.masterMute);
693 s.masterVol = j.value(
"AudioSettings", nlohmann::json{}).value(
"masterVol", s.masterVol);
694 s.mediaAudioMute = j.value(
"AudioSettings", nlohmann::json{}).value(
"mediaAudioMute", s.mediaAudioMute);
695 s.mediaAudioVol = j.value(
"AudioSettings", nlohmann::json{}).value(
"mediaAudioVol", s.mediaAudioVol);
696 s.tableMusicMute = j.value(
"AudioSettings", nlohmann::json{}).value(
"tableMusicMute", s.tableMusicMute);
697 s.tableMusicVol = j.value(
"AudioSettings", nlohmann::json{}).value(
"tableMusicVol", s.tableMusicVol);
698 s.interfaceAudioMute = j.value(
"AudioSettings", nlohmann::json{}).value(
"interfaceAudioMute", s.interfaceAudioMute);
699 s.interfaceAudioVol = j.value(
"AudioSettings", nlohmann::json{}).value(
"interfaceAudioVol", s.interfaceAudioVol);
700 s.interfaceAmbienceMute = j.value(
"AudioSettings", nlohmann::json{}).value(
"interfaceAmbienceMute", s.interfaceAmbienceMute);
701 s.interfaceAmbienceVol = j.value(
"AudioSettings", nlohmann::json{}).value(
"interfaceAmbienceVol", s.interfaceAmbienceVol);
704 s.scrollNormalSound = j.value(
"UISounds", nlohmann::json{}).value(
"scrollNormalSound", s.scrollNormalSound);
705 s.scrollFastSound = j.value(
"UISounds", nlohmann::json{}).value(
"scrollFastSound", s.scrollFastSound);
706 s.scrollJumpSound = j.value(
"UISounds", nlohmann::json{}).value(
"scrollJumpSound", s.scrollJumpSound);
707 s.scrollRandomSound = j.value(
"UISounds", nlohmann::json{}).value(
"scrollRandomSound", s.scrollRandomSound);
708 s.launchTableSound = j.value(
"UISounds", nlohmann::json{}).value(
"launchTableSound", s.launchTableSound);
709 s.launchScreenshotSound = j.value(
"UISounds", nlohmann::json{}).value(
"launchScreenshotSound", s.launchScreenshotSound);
710 s.panelToggleSound = j.value(
"UISounds", nlohmann::json{}).value(
"panelToggleSound", s.panelToggleSound);
711 s.screenshotTakeSound = j.value(
"UISounds", nlohmann::json{}).value(
"screenshotTakeSound", s.screenshotTakeSound);
712 s.ambienceSound = j.value(
"UISounds", nlohmann::json{}).value(
"ambienceSound", s.ambienceSound);
715 s.exeDir = j.value(
"Internal", nlohmann::json{}).value(
"exeDir", s.exeDir);
716 s.vpxSubCmd = j.value(
"Internal", nlohmann::json{}).value(
"vpxSubCmd", s.vpxSubCmd);
717 s.vpsDbPath = j.value(
"Internal", nlohmann::json{}).value(
"vpsDbPath", s.vpsDbPath);
718 s.vpsDbUpdateFrequency = j.value(
"Internal", nlohmann::json{}).value(
"vpsDbUpdateFrequency", s.vpsDbUpdateFrequency);
719 s.vpsDbLastUpdated = j.value(
"Internal", nlohmann::json{}).value(
"vpsDbLastUpdated", s.vpsDbLastUpdated);
720 s.vpxtoolIndex = j.value(
"Internal", nlohmann::json{}).value(
"vpxtoolIndex", s.vpxtoolIndex);
721 s.vpxtoolBin = j.value(
"Internal", nlohmann::json{}).value(
"vpxtoolBin", s.vpxtoolBin);
722 s.indexPath = j.value(
"Internal", nlohmann::json{}).value(
"indexPath", s.indexPath);
723 s.configUIWidth = j.value(
"Internal", nlohmann::json{}).value(
"configUIWidth", s.configUIWidth);
724 s.configUIHeight = j.value(
"Internal", nlohmann::json{}).value(
"configUIHeight", s.configUIHeight);
725 s.screenshotWait = j.value(
"Internal", nlohmann::json{}).value(
"screenshotWait", s.screenshotWait);
728 if (j.contains(
"Keybinds") && j[
"Keybinds"].is_object()) {
730 for (
auto& [key, value] : j[
"Keybinds"].items()) {
731 if (value.is_string()) {
732 s.keybinds_[key] = value.get<std::string>();