ASAPCabinetFE
 
Loading...
Searching...
No Matches
settings.h
1#ifndef SETTINGS_H
2#define SETTINGS_H
3
4#include <string>
5#include <SDL2/SDL.h>
6#include <nlohmann/json.hpp>
7#include <unordered_map>
8#include <vector>
9#include <cstdlib>
10#include <filesystem>
11
12struct Settings {
13 // Enum to define reload requirements for settings changes
14 enum class ReloadType {
15 None, // No reload needed
16 Title, // Title position
17 Tables, // Reload table related files
18 Assets, // Full table reloads/renderers
19 Windows, // Window reloads
20 Overlay, // UI widget reloads
21 Audio, // Audio settings reload
22 Font // Font for title display reload
23 };
24
25 // Metadata for each setting: field name -> reload type
26 // static const std::unordered_map<std::string, ReloadType> settingsMetadata;
27 static const std::map<std::string, std::pair<ReloadType, std::string>> settingsMetadata;
28
29 // [VPX]
30 std::string VPXTablesPath = "$HOME/Games/VPX_Tables/"; // imguifiledialog
31 std::string VPinballXPath = "$HOME/Games/vpinball/build/VPinballX_BGFX"; // imguifiledialog
32 std::string vpxIniPath = ""; // imguifiledialog
33 std::string vpxStartArgs = "";
34 std::string vpxEndArgs = "";
35 bool autoPatchTables = false;
36
37 // [DPISettings]
38 float dpiScale = 1.0f; // 0.1-1.0
39 bool enableDpiScaling = true;
40
41 // [DefaultMedia]
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";
51
52 // [CustomMedia]
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";
64
65 // [WindowSettings]
66 std::string videoBackend = "ffmpeg"; // + 'vlc', 'novideo', 'software'
67 // When true, prefer SDL software renderers for all windows (diagnostic/testing)
68 bool forceSoftwareRenderer = false;
69 bool useVPinballXIni = false; // use vpx_ini_reader
70 int playfieldWindowWidth = 1080;
71 int playfieldWindowHeight = 1920;
72 int playfieldX = -1;
73 int playfieldY = -1;
74
75 bool showBackglass = true;
76 int backglassWindowWidth = 1024;
77 int backglassWindowHeight = 768;
78 int backglassX = -1;
79 int backglassY = -1;
80
81 bool showDMD = true;
82 int dmdWindowWidth = 1024;
83 int dmdWindowHeight = 256;
84 int dmdX = -1;
85 int dmdY = -1;
86
87 bool showTopper = false;
88 int topperWindowWidth = 512;
89 int topperWindowHeight = 128;
90 int topperWindowX = -1;
91 int topperWindowY = -1;
92
93 // [TableMetadata]
94 std::string titleSource = "filename"; // + 'metadata'
95 bool fetchVPSdb = false;
96 bool useVpxtool = false;
97 bool ignoreScanners = false;
98 bool forceRebuildMetadata = false;
99 std::string titleSortBy = "title"; // + 'year', 'author', 'manufacturer', 'type'
100
101 float titleWeight = 0.6f; //0.2 - 0.8
102 float yearWeight = 0.2f; //0 - 0.4
103 float manufacturerWeight = 0.1f; //0 - 0.3
104 float romWeight = 0.25f; // 0 - 0.5
105 float titleThreshold = 0.55f; //0.3 - 0.8
106 float confidenceThreshold = 0.6f; //0.4 0.9
107
108 // [UIWidgets]
109 bool showMetadata = false;
110 float metadataPanelWidth = 0.7f; // 0.1-1.0
111 float metadataPanelHeight = 0.5f; // 0.1-1.0
112 float metadataPanelAlpha = 0.6f; // 0.1-1.0
113
114 bool showArrowHint = true;
115 float arrowHintWidth = 20.0f;
116 float arrowHintHeight = 100.0f;
117 float arrowThickness = 4.0f;
118 float arrowAlpha = 0.6f; // 0.1-1.0
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};
123
124 bool showScrollbar = true;
125 float scrollbarWidth = 12.0f;
126 float thumbWidth = 15.0f;
127 float scrollbarLength = 0.5f; // 0.1-1.0
128 SDL_Color scrollbarColor = {50, 50, 50, 200};
129 SDL_Color scrollbarThumbColor = {50, 150, 150, 255};
130
131 // [TitleDisplay]
132 bool showWheel = true;
133 std::string wheelWindow = "playfield"; // + 'backglass', 'dmd', 'topper'
134 bool showTitle = true;
135 std::string titleWindow = "playfield"; // + 'backglass', 'dmd', 'topper'
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};
139 int fontSize = 28; // 1-80
140 int titleX = 30;
141 int titleY = 1850;
142
143 // [MediaDimensions]
144 bool fetchVpinMediaDb = false; // download images from vpinmedia
145 bool resizeToWindows = false; // resize images to windows sizes
146 bool forceImagesOnly = false;
147
148 int wheelMediaHeight = 350;
149 int wheelMediaWidth = 350;
150 int wheelMediaX = 720;
151 int wheelMediaY = 1550;
152
153 int playfieldMediaWidth = 1080;
154 int playfieldMediaHeight = 1920;
155 int playfieldMediaX = 0;
156 int playfieldMediaY = 0;
157 int playfieldRotation = 0; // 0-90-180-270-360 +/-
158
159 int backglassMediaWidth = 1024;
160 int backglassMediaHeight = 768;
161 int backglassMediaX = 0;
162 int backglassMediaY = 0;
163 int backglassRotation = 0; // 0-90-180-270-360 +/-
164
165 int dmdMediaWidth = 1024;
166 int dmdMediaHeight = 256;
167 int dmdMediaX = 0;
168 int dmdMediaY = 0;
169 int dmdRotation = 0; // 0-90-180-270-360 +/-
170
171 int topperMediaWidth = 512;
172 int topperMediaHeight = 128;
173 int topperMediaX = 0;
174 int topperMediaY = 0;
175 int topperRotation = 0; // 0-90-180-270-360 +/-
176
177 // [AudioSettings]
178 bool masterMute = false;
179 float masterVol = 100.0f; // 0-100
180 bool mediaAudioMute = false;
181 float mediaAudioVol = 60.0f; // 0-100
182 bool tableMusicMute = false;
183 float tableMusicVol = 60.0f; // 0-100
184 bool interfaceAudioMute = false;
185 float interfaceAudioVol = 60.0f; // 0-100
186 bool interfaceAmbienceMute = false;
187 float interfaceAmbienceVol = 60.0f; // 0-100
188
189 // [UiSounds]
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";
199
200 // [Internal]
201 std::string exeDir;
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; // 0-60
210 // defaults in ConfigUI::drawGUI()
211 float configUIWidth = 0.7f;
212 float configUIHeight = 0.5f;
213
214 // [Keybinds]
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"},
225 {"Quit", "Q"},
226 {"Screenshot Mode", "S"},
227 {"Screenshot Key", "S"},
228 {"Screenshot Quit", "Q"},
229 {"Toggle Editor", "M"},
230 {"Toggle Catalog", "N"}
231 };
232
233 // Apply post-processing (e.g., DPI scaling, path resolution)
234 void applyPostProcessing(const std::string& exeDirectory) {
235 // Store exeDir
236 this->exeDir = exeDirectory;
237
238 // Resolve VPX paths
239 VPXTablesPath = resolvePath(VPXTablesPath, exeDirectory);
240 VPinballXPath = resolvePath(VPinballXPath, exeDirectory);
241
242 // List all paths that need resolution
243 std::vector<std::string> pathFields = {
244 // Default Media Paths
245 "defaultPlayfieldImage", "defaultBackglassImage", "defaultDmdImage",
246 "defaultWheelImage", "defaultTopperImage", "defaultPlayfieldVideo",
247 "defaultBackglassVideo", "defaultDmdVideo", "defaultTopperVideo",
248
249 // UI Sounds Paths ("default sounds")
250 "scrollNormalSound", "scrollFastSound", "scrollJumpSound",
251 "scrollRandomSound", "launchTableSound", "launchScreenshotSound",
252 "panelToggleSound", "screenshotTakeSound", "ambienceSound",
253
254 // Other internal/external paths
255 "vpsDbPath", "vpsDbLastUpdated", "indexPath",//"vpxtoolBin", "vpxtoolIndex", "vpxIniPath" // From Internal section
256 };
257
258 // Iterate through the list and resolve each path
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);
269 // UI Sounds
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);
279 // Other paths
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);
283 }
284
285 // Apply DPI scaling to fontSize if enabled
286 if (enableDpiScaling) {
287 fontSize = static_cast<int>(static_cast<float>(fontSize) * dpiScale);
288 }
289
290 // Resolve font path
291 if (fontPath.empty() || !std::filesystem::exists(fontPath)) {
292 std::vector<std::string> candidates = {
293 "/usr/share/fonts/truetype/dejavu/Arial.ttf", // Debian/Ubuntu
294 "/usr/share/fonts/truetype/dejavu/FreeSans.ttf",
295 "/usr/share/fonts/truetype/dejavu/FreeMono.ttf",
296 "/usr/share/fonts/TTF/DejaVuSans.ttf", // Arch
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", // Fedora/openSUSE
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", // Custom system install
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" // Mine =]
309 };
310 for (const auto& path : candidates) {
311 if (std::filesystem::exists(path)) {
312 fontPath = path;
313 break;
314 }
315 }
316 }
317 }
318
319public:
320 // Resolve relative paths and environment variables
321 std::string resolvePath(const std::string& value, const std::string& exeDirectory) const {
322 std::string result = value;
323 // Replace $HOME with actual home directory
324 const char* home = std::getenv("HOME");
325 if (home) {
326 size_t pos;
327 while ((pos = result.find("$HOME")) != std::string::npos) {
328 result.replace(pos, 5, home);
329 }
330 if (!result.empty() && result[0] == '~') {
331 result.replace(0, 1, home);
332 }
333 }
334 // Resolve relative paths
335 if (result.empty()) return exeDirectory + value;
336 if (result[0] == '/' || result[0] == '\\') return result;
337 return exeDirectory + result;
338 }
339
340private:
341 // JSON serialization
342 friend void to_json(nlohmann::json& j, const Settings& s) {
343 j = nlohmann::json{
344 {"VPX", {
345 {"VPXTablesPath", s.VPXTablesPath},
346 {"VPinballXPath", s.VPinballXPath},
347 {"vpxIniPath", s.vpxIniPath},
348 {"vpxStartArgs", s.vpxStartArgs},
349 {"vpxEndArgs", s.vpxEndArgs},
350 {"autoPatchTables", s.autoPatchTables}
351 }},
352 {"DPISettings", {
353 {"dpiScale", s.dpiScale},
354 {"enableDpiScaling", s.enableDpiScaling}
355 }},
356 {"DefaultMedia", {
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}
366 }},
367 {"CustomMedia", {
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}
379 }},
380 {"WindowSettings", {
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},
395 {"dmdX", s.dmdX},
396 {"dmdY", s.dmdY},
397 {"showTopper", s.showTopper},
398 {"topperWindowWidth", s.topperWindowWidth},
399 {"topperWindowHeight", s.topperWindowHeight},
400 {"topperWindowX", s.topperWindowX},
401 {"topperWindowY", s.topperWindowY}
402 }},
403 {"TableMetadata", {
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}
416 }},
417 {"UIWidgets", {
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}}
437 }},
438 {"TitleDisplay", {
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},
448 {"titleY", s.titleY}
449 }},
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}
478 }},
479 {"AudioSettings", {
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}
490 }},
491 {"UISounds", {
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}
501 }},
502 {"Internal", {
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}
514 }},
515 {"Keybinds", s.keybinds_}
516 };
517 }
518
519 friend void from_json(const nlohmann::json& j, Settings& s) {
520 // VPX
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);
527
528 // DPISettings
529 s.dpiScale = j.value("DPISettings", nlohmann::json{}).value("dpiScale", s.dpiScale);
530 s.enableDpiScaling = j.value("DPISettings", nlohmann::json{}).value("enableDpiScaling", s.enableDpiScaling);
531
532 // DefaultMedia
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);
542
543 // CustomMedia
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);
555
556 // WindowSettings
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);
578
579 // TableMetadata
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);
586
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);
593
594 // UIWidgets
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])};
610 }
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])};
616 }
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])};
622 }
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])};
632 }
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])};
638 }
639
640 // TitleDisplay
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])};
651 }
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])};
657 }
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);
661
662 // MediaDimensions
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);
690
691 // AudioSettings
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);
702
703 // UISounds
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);
713
714 // Internal
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);
726
727 // [Keybinds]
728 if (j.contains("Keybinds") && j["Keybinds"].is_object()) {
729 s.keybinds_.clear();
730 for (auto& [key, value] : j["Keybinds"].items()) {
731 if (value.is_string()) {
732 s.keybinds_[key] = value.get<std::string>();
733 }
734 }
735 }
736 }
737};
738
739// Define settingsMetadata (user-facing help strings)
740inline const std::map<std::string, std::pair<Settings::ReloadType, std::string>> Settings::settingsMetadata = {
741 // VPX (table playback paths and helpers)
742 {"VPXTablesPath", {Settings::ReloadType::Tables, "Absolute path to your VPX table folders.\n\n"
743 "Enter the full path where your .vpx table folders are stored.\n"
744 "Example: /home/you/Games/VPX_Tables/\n\n"
745 "Used when launching tables with VPinballX."}},
746 {"VPinballXPath", {Settings::ReloadType::None, "Absolute path to the VPinballX executable.\n\n"}},
747 {"vpxIniPath", {Settings::ReloadType::None, "Path to VPinballX.ini (optional).\n\n"
748 "Leave empty to use the default location (~/.vpinball/VPinballX.ini)."}},
749 {"vpxStartArgs", {Settings::ReloadType::None, "Extra command-line arguments to prepend when launching VPinballX.\n\n"
750 "These start the command line, added before the launch command."}},
751 {"vpxEndArgs", {Settings::ReloadType::None, "Extra command-line arguments to append when launching VPinballX.\n\n"
752 "These end the command line, added after the launch command."}},
753 {"autoPatchTables",{Settings::ReloadType::Tables, "Automatically download and apply community VBScript patch files when scanning tables."}},
754
755 // DPI settings
756 {"dpiScale", {Settings::ReloadType::Windows, "Manual DPI scale (used only when automatic DPI scaling is disabled).\n\n"
757 "Use 1.0 for 100%, 1.5 for 150%, etc. Adjust if UI elements look too small or too large."}},
758 {"enableDpiScaling", {Settings::ReloadType::Windows, "When enabled, the frontend will scale the UI automatically based on your monitor DPI.\n\n"
759 "Disable this to set a custom DPI with 'dpiScale'."}},
760
761 // Default media files
762 {"defaultPlayfieldImage", {Settings::ReloadType::Tables, "Absolute path to the default playfield preview image.\n\n"
763 "Shown when a table has no custom preview image."}},
764 {"defaultBackglassImage", {Settings::ReloadType::Tables, "Absolute path to the default backglass image used when none is provided by a table."}},
765 {"defaultDmdImage", {Settings::ReloadType::Tables, "Absolute path to the default DMD image used when a table provides none."}},
766 {"defaultWheelImage", {Settings::ReloadType::Tables, "Absolute path to the default wheel image used when a table provides none."}},
767 {"defaultTopperImage", {Settings::ReloadType::Tables, "Absolute path to the default Topper image used when a table provides none."}},
768 {"defaultPlayfieldVideo", {Settings::ReloadType::Tables, "Absolute path to the default preview video for playfields (used when table lacks video)."}},
769 {"defaultBackglassVideo", {Settings::ReloadType::Tables, "Absolute path to the default backglass preview video (used when table lacks video)."}},
770 {"defaultDmdVideo", {Settings::ReloadType::Tables, "Absolute path to the default DMD preview video (used when table lacks video)."}},
771 {"defaultTopperVideo", {Settings::ReloadType::Tables, "Absolute path to the default Topper preview video (used when table lacks video)."}},
772
773 // Custom media per-table (relative to each table folder)
774 {"customPlayfieldImage", {Settings::ReloadType::Tables, "Relative path (inside a table folder) to the playfield preview image."}},
775 {"customBackglassImage", {Settings::ReloadType::Tables, "Relative path (inside a table folder) to the backglass image."}},
776 {"customDmdImage", {Settings::ReloadType::Tables, "Relative path (inside a table folder) to the DMD image."}},
777 {"customWheelImage", {Settings::ReloadType::Tables, "Relative path (inside a table folder) to the wheel image."}},
778 {"customTopperImage", {Settings::ReloadType::Tables, "Relative path (inside a table folder) to the Topper image."}},
779 {"customPlayfieldVideo", {Settings::ReloadType::Tables, "Relative path (inside a table folder) to the playfield preview video."}},
780 {"customBackglassVideo", {Settings::ReloadType::Tables, "Relative path (inside a table folder) to the backglass preview video."}},
781 {"customDmdVideo", {Settings::ReloadType::Tables, "Relative path (inside a table folder) to the DMD preview video."}},
782 {"customTopperVideo", {Settings::ReloadType::Tables, "Relative path (inside a table folder) to the Topper preview video."}},
783 {"tableMusic", {Settings::ReloadType::Tables, "Relative path (inside a table folder) to optional table music."}},
784 {"customLaunchSound", {Settings::ReloadType::Tables, "Relative path (inside a table folder) to a custom launch sound file."}},
785
786 // Window and renderer settings
787 {"videoBackend", {Settings::ReloadType::Assets, "Choose which video backend to use for in-app previews and table media:\n\n"
788 "vlc - libVLC-based playback: broad codec support and good compatibility.\n"
789 "ffmpeg - internal FFmpeg playback: usually faster and lighter on modern Linux systems.\n"
790 "novideo - disable all in-app video (useful for debugging or low-power setups).\n"
791 "software - force SDL software rendering for window renderers (diagnostic/testing only)."}},
792 {"useVPinballXIni", {Settings::ReloadType::Windows, "If enabled, read sizes and positions from ~/.vpinball/VPinballX.ini.\n\n"
793 "NOTE: This will override the manual window position/size settings below."}},
794 {"playfieldWindowWidth", {Settings::ReloadType::Assets, "Playfield window width in pixels (match this to your playfield media for best results)."}},
795 {"playfieldWindowHeight", {Settings::ReloadType::Assets, "Playfield window height in pixels (match this to your playfield media for best results)."}},
796 {"playfieldX", {Settings::ReloadType::Assets, "Playfield window X position on screen.\n\nDrag a window and double-click to save its position."}},
797 {"playfieldY", {Settings::ReloadType::Assets, "Playfield window Y position on screen.\n\nDrag a window and double-click to save its position."}},
798 {"showBackglass", {Settings::ReloadType::Assets, "Show or hide the Backglass window."}},
799 {"backglassWindowWidth", {Settings::ReloadType::Assets, "Backglass window width in pixels."}},
800 {"backglassWindowHeight", {Settings::ReloadType::Assets, "Backglass window height in pixels."}},
801 {"backglassX", {Settings::ReloadType::Assets, "Backglass window X position on screen.\n\nDrag and double-click a window to save position."}},
802 {"backglassY", {Settings::ReloadType::Assets, "Backglass window Y position on screen.\n\nDrag and double-click a window to save position."}},
803 {"showDMD", {Settings::ReloadType::Assets, "Show or hide the DMD window."}},
804 {"dmdWindowWidth", {Settings::ReloadType::Assets, "DMD window width in pixels."}},
805 {"dmdWindowHeight", {Settings::ReloadType::Assets, "DMD window height in pixels."}},
806 {"dmdX", {Settings::ReloadType::Assets, "DMD window X position on screen."}},
807 {"dmdY", {Settings::ReloadType::Assets, "DMD window Y position on screen."}},
808 {"showTopper", {Settings::ReloadType::Assets, "Show or hide the Topper window."}},
809 {"topperWindowWidth", {Settings::ReloadType::Assets, "Topper window width in pixels."}},
810 {"topperWindowHeight", {Settings::ReloadType::Assets, "Topper window height in pixels."}},
811 {"topperWindowX", {Settings::ReloadType::Assets, "Topper window X position on screen."}},
812 {"topperWindowY", {Settings::ReloadType::Assets, "Topper window Y position on screen."}},
813
814 // Table metadata matching and scanning
815 {"titleSource", {Settings::ReloadType::Tables, "Choose how table metadata is found:\n\n"
816 "filename' - use filename-based heuristics;\n"
817 "metadata' - run the scanners to extract embedded metadata."}},
818 {"fetchVPSdb", {Settings::ReloadType::Tables, "Download and use the Virtual Pinball Spreadsheet db (community database)\nto improve automatic table matching and metadata accuracy."}},
819 {"useVpxtool",{Settings::ReloadType::Tables, "Use an external vpxtool index (if available) or run vpxtool to extract table metadata\ninstead of built-in VPin scanner."}},
820 {"ignoreScanners", {Settings::ReloadType::Tables, "Skip local scanners and read metadata only from the index file.\n\nUseful for when you have a trusted pre-built index."}},
821 {"forceRebuildMetadata", {Settings::ReloadType::Tables, "Rebuild all table metadata from scratch.\n\nUse this if metadata is incorrect or you changed scanner settings."}},
822 {"titleSortBy", {Settings::ReloadType::Tables, "Choose the field used to sort the table list (requires VPSdb metadata for some options)."}},
823 {"titleWeight", {Settings::ReloadType::None, "How much title similarity influences automatic matching (higher = stronger influence)."}},
824 {"yearWeight", {Settings::ReloadType::None, "How much year similarity influences automatic matching (higher = stronger influence)."}},
825 {"manufacturerWeight", {Settings::ReloadType::None, "How much manufacturer similarity influences automatic matching (higher = stronger influence)."}},
826 {"romWeight", {Settings::ReloadType::None, "How much ROM name similarity influences automatic matching (higher = stronger influence)."}},
827 {"titleThreshold", {Settings::ReloadType::None, "Minimum similarity score required for a title to be considered a match (higher = stricter)."}},
828 {"confidenceThreshold", {Settings::ReloadType::None, "Overall confidence threshold required to accept a metadata match (higher = stricter)."}},
829
830 // UI widget settings
831 {"showMetadata", {Settings::ReloadType::Overlay, "Show or hide the metadata overlay panel on the playfield window."}},
832 {"metadataPanelWidth", {Settings::ReloadType::Overlay, "Metadata panel width as a fraction of the screen (0.1-1.0)."}},
833 {"metadataPanelHeight", {Settings::ReloadType::Overlay, "Metadata panel height as a fraction of the screen (0.1-1.0)."}},
834 {"metadataPanelAlpha", {Settings::ReloadType::Overlay, "Transparency of the metadata panel (0.0-1.0)."}},
835 {"showArrowHint", {Settings::ReloadType::None, "Toggle the small arrow hint widget in the UI."}},
836 {"arrowHintWidth", {Settings::ReloadType::None, "Width of the arrow hint in pixels."}},
837 {"arrowHintHeight", {Settings::ReloadType::None, "Height of the arrow hint in pixels."}},
838 {"arrowThickness", {Settings::ReloadType::None, "Thickness of the arrow hint in pixels."}},
839 {"arrowAlpha", {Settings::ReloadType::None, "Transparency of the arrow hint (0.0-1.0)."}},
840 {"arrowGlow", {Settings::ReloadType::None, "Glow size for the arrow hint."}},
841 {"arrowGlowColor", {Settings::ReloadType::None, "Glow color for the arrow hint (RGBA)."}},
842 {"arrowColorTop", {Settings::ReloadType::None, "Top gradient color for the arrow hint (RGBA)."}},
843 {"arrowColorBottom", {Settings::ReloadType::None, "Bottom gradient color for the arrow hint (RGBA)."}},
844 {"showScrollbar", {Settings::ReloadType::None, "Show or hide the sidebar scrollbar."}},
845 {"scrollbarWidth", {Settings::ReloadType::None, "Scrollbar width in pixels."}},
846 {"thumbWidth", {Settings::ReloadType::None, "Scrollbar thumb width in pixels."}},
847 {"scrollbarLength", {Settings::ReloadType::None, "Scrollbar length as a fraction of the window (0.1-1.0)."}},
848 {"scrollbarColor", {Settings::ReloadType::None, "Scrollbar color (RGBA)."}},
849 {"scrollbarThumbColor", {Settings::ReloadType::None, "Scrollbar thumb color (RGBA)."}},
850
851 // Title display
852 {"showWheel", {Settings::ReloadType::None, "Show or hide the wheel artwork in it's window."}},
853 {"wheelWindow", {Settings::ReloadType::Tables, "Which window should display the wheel artwork."}},
854 {"showTitle", {Settings::ReloadType::None, "Show or hide the table title text in it's window."}},
855 {"titleWindow", {Settings::ReloadType::Tables, "Which window should display the table title text."}},
856 {"fontPath", {Settings::ReloadType::Font, "Path to the font used for table titles."}},
857 {"fontColor", {Settings::ReloadType::Font, "Color used to render table title text."}},
858 {"fontBgColor", {Settings::ReloadType::Font, "Background color behind table title text (RGBA)."}},
859 {"fontSize", {Settings::ReloadType::Font, "Font size (points) used for the table title."}},
860 {"titleX", {Settings::ReloadType::Title, "Horizontal position (X) for the title text."}},
861 {"titleY", {Settings::ReloadType::Title, "Vertical position (Y) for the title text."}},
862
863 // Media dimensions and behavior
864 {"fetchVpinMediaDb", {Settings::ReloadType::Tables, "Download images from the VPin Media Database (requires VPSdb metadata)."}},
865 {"resizeToWindows", {Settings::ReloadType::Tables, "Automatically resize VPin Media Database downloaded images to match your\ncurrent window dimensions to save memory and keep layout consistent."}},
866 {"forceImagesOnly", {Settings::ReloadType::Tables, "If enabled, the frontend will load images only and skip videos."}},
867 {"wheelMediaHeight", {Settings::ReloadType::None, "Wheel image height in pixels."}},
868 {"wheelMediaWidth", {Settings::ReloadType::None, "Wheel image width in pixels."}},
869 {"wheelMediaX", {Settings::ReloadType::None, "Wheel image X coordinate inside its window."}},
870 {"wheelMediaY", {Settings::ReloadType::None, "Wheel image Y coordinate inside its window."}},
871 {"playfieldMediaWidth", {Settings::ReloadType::None, "Playfield media width in pixels."}},
872 {"playfieldMediaHeight", {Settings::ReloadType::None, "Playfield media height in pixels."}},
873 {"playfieldMediaX", {Settings::ReloadType::None, "Playfield media X coordinate (relative to playfield window)."}},
874 {"playfieldMediaY", {Settings::ReloadType::None, "Playfield media Y coordinate (relative to playfield window)."}},
875 {"playfieldRotation", {Settings::ReloadType::None, "Rotation to apply to playfield media (degrees)."}},
876 {"backglassMediaWidth", {Settings::ReloadType::None, "Backglass media width in pixels."}},
877 {"backglassMediaHeight", {Settings::ReloadType::None, "Backglass media height in pixels."}},
878 {"backglassMediaX", {Settings::ReloadType::None, "Backglass media X coordinate (relative to backglass window)."}},
879 {"backglassMediaY", {Settings::ReloadType::None, "Backglass media Y coordinate (relative to backglass window)."}},
880 {"backglassRotation", {Settings::ReloadType::None, "Rotation to apply to backglass media (degrees)."}},
881 {"dmdMediaWidth", {Settings::ReloadType::None, "DMD media width in pixels."}},
882 {"dmdMediaHeight", {Settings::ReloadType::None, "DMD media height in pixels."}},
883 {"dmdMediaX", {Settings::ReloadType::None, "DMD media X coordinate (relative to DMD window)."}},
884 {"dmdMediaY", {Settings::ReloadType::None, "DMD media Y coordinate (relative to DMD window)."}},
885 {"dmdRotation", {Settings::ReloadType::None, "Rotation to apply to DMD media (degrees)."}},
886 {"topperMediaWidth", {Settings::ReloadType::None, "Topper media width in pixels."}},
887 {"topperMediaHeight", {Settings::ReloadType::None, "Topper media height in pixels."}},
888 {"topperMediaX", {Settings::ReloadType::None, "Topper media X coordinate (relative to Topper window)."}},
889 {"topperMediaY", {Settings::ReloadType::None, "Topper media Y coordinate (relative to Topper window)."}},
890 {"topperRotation", {Settings::ReloadType::None, "Rotation to apply to Topper media (degrees)."}},
891
892 // Audio
893 {"masterMute", {Settings::ReloadType::Audio, "Mute all audio output."}},
894 {"masterVol", {Settings::ReloadType::Audio, "Master volume for all audio output (0-100)."}},
895 {"mediaAudioMute", {Settings::ReloadType::Audio, "Mute playfield, backglass, and DMD audio."}},
896 {"mediaAudioVol", {Settings::ReloadType::Audio, "Volume for playfield/backglass/DMD audio (0-100)."}},
897 {"tableMusicMute", {Settings::ReloadType::Audio, "Mute background table music."}},
898 {"tableMusicVol", {Settings::ReloadType::Audio, "Volume for background table music (0-100)."}},
899 {"interfaceAudioMute", {Settings::ReloadType::Audio, "Mute UI/interface sounds."}},
900 {"interfaceAudioVol", {Settings::ReloadType::Audio, "Volume for UI/interface sounds (0-100)."}},
901 {"interfaceAmbienceMute", {Settings::ReloadType::Audio, "Mute ambient interface sounds."}},
902 {"interfaceAmbienceVol", {Settings::ReloadType::Audio, "Volume for ambient interface sounds (0-100)."}},
903
904 // UI sound clips
905 {"scrollNormalSound", {Settings::ReloadType::Tables, "Sound played when moving the selection by one."}},
906 {"scrollFastSound", {Settings::ReloadType::Tables, "Sound played when fast-scrolling through the list."}},
907 {"scrollJumpSound", {Settings::ReloadType::Tables, "Sound played when jumping by letter."}},
908 {"scrollRandomSound", {Settings::ReloadType::Tables, "Sound played when selecting a random table."}},
909 {"launchTableSound", {Settings::ReloadType::Tables, "Sound played when launching a table."}},
910 {"launchScreenshotSound", {Settings::ReloadType::Tables, "Sound played when entering screenshot mode."}},
911 {"panelToggleSound", {Settings::ReloadType::Tables, "Sound played when opening or closing UI panels."}},
912 {"screenshotTakeSound", {Settings::ReloadType::Tables, "Sound played when taking a screenshot."}},
913 {"ambienceSound", {Settings::ReloadType::Tables, "Ambient background sound used when a table has no music."}},
914
915 // Internal paths and timing
916 {"exeDir", {Settings::ReloadType::None, "Path to the application executable directory."}},
917 {"vpxSubCmd", {Settings::ReloadType::None, "VPinballX command used to play .vpx tables (internal helper)."}},
918 {"vpsDbPath", {Settings::ReloadType::None, "Path to the VPS database file (relative to the executable directory)."}},
919 {"vpsDbUpdateFrequency", {Settings::ReloadType::None, "When to check for VPSdb updates (e.g., 'startup')."}},
920 {"vpsDbLastUpdated", {Settings::ReloadType::None, "Path to the VPS database timestamp file (relative to executable)."}},
921 {"vpxtoolIndex", {Settings::ReloadType::None, "Path to the vpxtool index file (defaults to vpxtool_index.json)."}},
922 {"vpxtoolBin", {Settings::ReloadType::None, "Path to the vpxtool binary, if it is not on your PATH."}},
923 {"indexPath", {Settings::ReloadType::None, "Path to the main table index file (relative to the executable)."}},
924 {"screenshotWait", {Settings::ReloadType::None, "Seconds to wait for visible windows when using the screenshot tool."}},
925 {"configUIWidth", {Settings::ReloadType::None, "Configuration UI width (fraction of screen)."}},
926 {"configUIHeight", {Settings::ReloadType::None, "Configuration UI height (fraction of screen)."}},
927
928 // Keybind descriptions
929 {"Previous Table", {Settings::ReloadType::None, "Key to select the previous table."}},
930 {"Next Table", {Settings::ReloadType::None, "Key to select the next table."}},
931 {"Fast Previous Table", {Settings::ReloadType::None, "Key to jump back by 10 tables."}},
932 {"Fast Next Table", {Settings::ReloadType::None, "Key to jump forward by 10 tables."}},
933 {"Jump Next Letter", {Settings::ReloadType::None, "Key to jump to the next table starting with a different letter."}},
934 {"Jump Previous Letter", {Settings::ReloadType::None, "Key to jump to the previous table starting with a different letter."}},
935 {"Random Table", {Settings::ReloadType::None, "Key to select a random table from the list."}},
936 {"Launch Table", {Settings::ReloadType::None, "Key to launch the currently selected table."}},
937 {"Toggle Config", {Settings::ReloadType::None, "Key to open or close the configuration menu."}},
938 {"Quit", {Settings::ReloadType::None, "Key to quit the application or close menus."}},
939 {"Screenshot Mode", {Settings::ReloadType::None, "Key to open screenshot launch mode for capturing table images."}},
940 {"Screenshot Key", {Settings::ReloadType::None, "Key to capture a screenshot while in screenshot mode."}},
941 {"Screenshot Quit", {Settings::ReloadType::None, "Key to exit screenshot mode."}},
942 {"Toggle Editor", {Settings::ReloadType::None, "Key to open the metadata editor."}},
943 {"Toggle Catalog", {Settings::ReloadType::None, "Key to open the metadata catalog."}}
944};
945
946#endif // SETTINGS_H
Definition settings.h:12