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; //TODO: nyi
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 = "vlc"; // + 'ffmpeg', 'novideo'
67 bool useVPinballXIni = true; // use vpx_ini_reader
68 int playfieldWindowWidth = 1080;
69 int playfieldWindowHeight = 1920;
70 int playfieldX = -1;
71 int playfieldY = -1;
72
73 bool showBackglass = true;
74 int backglassWindowWidth = 1024;
75 int backglassWindowHeight = 768;
76 int backglassX = -1;
77 int backglassY = -1;
78
79 bool showDMD = true;
80 int dmdWindowWidth = 1024;
81 int dmdWindowHeight = 256;
82 int dmdX = -1;
83 int dmdY = -1;
84
85 bool showTopper = false;
86 int topperWindowWidth = 512;
87 int topperWindowHeight = 128;
88 int topperWindowX = -1;
89 int topperWindowY = -1;
90
91 // [TableMetadata]
92 std::string titleSource = "filename"; // + 'metadata'
93 bool fetchVPSdb = false;
94 bool useVpxtool = false;
95 bool fetchVpinMediaDb = false; // download images from vpinmedia
96 bool resizeToWindows = false; // resize images to windows sizes
97 bool ignoreScanners = false;
98 bool forceRebuildMetadata = false;
99 std::string titleSortBy = "title"; // + 'year', 'author', 'manufacturer', 'type'
100 bool showMetadata = false;
101
102 float metadataPanelWidth = 0.7f; // 0.1-1.0
103 float metadataPanelHeight = 0.5f; // 0.1-1.0
104 float metadataPanelAlpha = 0.6f; // 0.1-1.0
105
106 float titleWeight = 0.6f; //0.2 - 0.8
107 float yearWeight = 0.2f; //0 - 0.4
108 float manufacturerWeight = 0.1f; //0 - 0.3
109 float romWeight = 0.25f; // 0 - 0.5
110 float titleThreshold = 0.55f; //0.3 - 0.8
111 float confidenceThreshold = 0.6f; //0.4 0.9
112
113 // [UIWidgets]
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 forceImagesOnly = false;
145
146 int wheelMediaHeight = 350;
147 int wheelMediaWidth = 350;
148 int wheelMediaX = 720;
149 int wheelMediaY = 1550;
150
151 int playfieldMediaWidth = 1080;
152 int playfieldMediaHeight = 1920;
153 int playfieldMediaX = 0;
154 int playfieldMediaY = 0;
155 int playfieldRotation = 0; // 0-90-180-270-360 +/-
156
157 int backglassMediaWidth = 1024;
158 int backglassMediaHeight = 768;
159 int backglassMediaX = 0;
160 int backglassMediaY = 0;
161 int backglassRotation = 0; // 0-90-180-270-360 +/-
162
163 int dmdMediaWidth = 1024;
164 int dmdMediaHeight = 256;
165 int dmdMediaX = 0;
166 int dmdMediaY = 0;
167 int dmdRotation = 0; // 0-90-180-270-360 +/-
168
169 int topperMediaWidth = 512;
170 int topperMediaHeight = 128;
171 int topperMediaX = 0;
172 int topperMediaY = 0;
173 int topperRotation = 0; // 0-90-180-270-360 +/-
174
175 // [AudioSettings]
176 bool masterMute = false;
177 float masterVol = 100.0f; // 0-100
178 bool mediaAudioMute = false;
179 float mediaAudioVol = 60.0f; // 0-100
180 bool tableMusicMute = false;
181 float tableMusicVol = 60.0f; // 0-100
182 bool interfaceAudioMute = false;
183 float interfaceAudioVol = 60.0f; // 0-100
184 bool interfaceAmbienceMute = false;
185 float interfaceAmbienceVol = 60.0f; // 0-100
186
187 // [UiSounds]
188 std::string scrollNormalSound = "snd/scroll_normal.mp3";
189 std::string scrollFastSound = "snd/scroll_fast.mp3";
190 std::string scrollJumpSound = "snd/scroll_jump.mp3";
191 std::string scrollRandomSound = "snd/scroll_random.mp3";
192 std::string launchTableSound = "snd/launch_table.mp3";
193 std::string launchScreenshotSound = "snd/launch_screenshot.mp3";
194 std::string panelToggleSound = "snd/panel_toggle.mp3";
195 std::string screenshotTakeSound = "snd/screenshot_take.mp3";
196 std::string ambienceSound = "snd/interface_ambience.mp3";
197
198 // [Internal]
199 std::string exeDir;
200 std::string vpxSubCmd = "-Play";
201 std::string vpsDbPath = "data/vpsdb.json";
202 std::string vpsDbUpdateFrequency = "startup";
203 std::string vpsDbLastUpdated = "data/lastUpdated.json";
204 std::string vpxtoolBin = "";
205 std::string vpxtoolIndex = "vpxtool_index.json";
206 std::string indexPath = "data/asapcab_index.json";
207 int screenshotWait = 4; // 0-60
208 // defaults in ConfigUI::drawGUI()
209 float configUIWidth = 0.7f;
210 float configUIHeight = 0.5f;
211
212 // [Keybinds]
213 std::map<std::string, std::string> keybinds_ = {
214 {"Previous Table", "Left Shift"},
215 {"Next Table", "Right Shift"},
216 {"Fast Previous Table", "Left Ctrl"},
217 {"Fast Next Table", "Right Ctrl"},
218 {"Jump Next Letter", "/"},
219 {"Jump Previous Letter", "Z"},
220 {"Random Table", "R"},
221 {"Launch Table", "Return"},
222 {"Toggle Config", "C"},
223 {"Quit", "Q"},
224 {"Screenshot Mode", "S"},
225 {"Screenshot Key", "S"},
226 {"Screenshot Quit", "Q"},
227 {"Toggle Editor", "M"},
228 {"Toggle Catalog", "N"}
229 };
230
231 // Apply post-processing (e.g., DPI scaling, path resolution)
232 void applyPostProcessing(const std::string& exeDir) {
233 // Store exeDir
234 this->exeDir = exeDir;
235
236 // Resolve VPX paths
237 VPXTablesPath = resolvePath(VPXTablesPath, exeDir);
238 VPinballXPath = resolvePath(VPinballXPath, exeDir);
239
240 // List all paths that need resolution
241 std::vector<std::string> pathFields = {
242 // Default Media Paths (already in your original code, correctly resolved)
243 "defaultPlayfieldImage", "defaultBackglassImage", "defaultDmdImage",
244 "defaultWheelImage", "defaultTopperImage", "defaultPlayfieldVideo",
245 "defaultBackglassVideo", "defaultDmdVideo", "defaultTopperVideo",
246
247 // UI Sounds Paths (NOT resolved in your original code, need to be added - these are your "default sounds")
248 "scrollNormalSound", "scrollFastSound", "scrollJumpSound",
249 "scrollRandomSound", "launchTableSound", "launchScreenshotSound",
250 "panelToggleSound", "screenshotTakeSound", "ambienceSound",
251
252 // Other internal/external paths (NOT resolved in your original code, need to be added)
253 "vpsDbPath", "vpsDbLastUpdated", "indexPath",//"vpxtoolBin", "vpxtoolIndex", "vpxIniPath" // From Internal section
254 };
255
256 // Iterate through the list and resolve each path
257 for (const auto& field : pathFields) {
258 if (field == "defaultPlayfieldImage") defaultPlayfieldImage = resolvePath(defaultPlayfieldImage, exeDir);
259 else if (field == "defaultBackglassImage") defaultBackglassImage = resolvePath(defaultBackglassImage, exeDir);
260 else if (field == "defaultDmdImage") defaultDmdImage = resolvePath(defaultDmdImage, exeDir);
261 else if (field == "defaultWheelImage") defaultWheelImage = resolvePath(defaultWheelImage, exeDir);
262 else if (field == "defaultTopperImage") defaultTopperImage = resolvePath(defaultTopperImage, exeDir);
263 else if (field == "defaultPlayfieldVideo") defaultPlayfieldVideo = resolvePath(defaultPlayfieldVideo, exeDir);
264 else if (field == "defaultBackglassVideo") defaultBackglassVideo = resolvePath(defaultBackglassVideo, exeDir);
265 else if (field == "defaultDmdVideo") defaultDmdVideo = resolvePath(defaultDmdVideo, exeDir);
266 else if (field == "defaultTopperVideo") defaultTopperVideo = resolvePath(defaultTopperVideo, exeDir);
267 // UI Sounds
268 else if (field == "scrollNormalSound") scrollNormalSound = resolvePath(scrollNormalSound, exeDir);
269 else if (field == "scrollFastSound") scrollFastSound = resolvePath(scrollFastSound, exeDir);
270 else if (field == "scrollJumpSound") scrollJumpSound = resolvePath(scrollJumpSound, exeDir);
271 else if (field == "scrollRandomSound") scrollRandomSound = resolvePath(scrollRandomSound, exeDir);
272 else if (field == "launchTableSound") launchTableSound = resolvePath(launchTableSound, exeDir);
273 else if (field == "launchScreenshotSound") launchScreenshotSound = resolvePath(launchScreenshotSound, exeDir);
274 else if (field == "panelToggleSound") panelToggleSound = resolvePath(panelToggleSound, exeDir);
275 else if (field == "screenshotTakeSound") screenshotTakeSound = resolvePath(screenshotTakeSound, exeDir);
276 else if (field == "ambienceSound") ambienceSound = resolvePath(ambienceSound, exeDir);
277 // Other paths
278 else if (field == "vpsDbPath") vpsDbPath = resolvePath(vpsDbPath, exeDir);
279 else if (field == "vpsDbLastUpdated") vpsDbLastUpdated = resolvePath(vpsDbLastUpdated, exeDir);
280 else if (field == "indexPath") indexPath = resolvePath(indexPath, exeDir);
281 }
282
283 // Apply DPI scaling to fontSize if enabled (already handled correctly)
284 if (enableDpiScaling) {
285 fontSize = static_cast<int>(fontSize * dpiScale);
286 }
287 }
288
289public:
290 // Resolve relative paths and environment variables (e.g., $USER)
291 std::string resolvePath(const std::string& value, const std::string& exeDir) const {
292 std::string result = value;
293 // Replace $HOME with actual home directory
294 const char* home = std::getenv("HOME");
295 if (home) {
296 size_t pos;
297 while ((pos = result.find("$HOME")) != std::string::npos) {
298 result.replace(pos, 5, home);
299 }
300 if (!result.empty() && result[0] == '~') {
301 result.replace(0, 1, home);
302 }
303 }
304 // Resolve relative paths
305 if (result.empty()) return exeDir + value;
306 if (result[0] == '/' || result[0] == '\\') return result;
307 return exeDir + result;
308 }
309
310private:
311 // JSON serialization
312 friend void to_json(nlohmann::json& j, const Settings& s) {
313 j = nlohmann::json{
314 {"VPX", {
315 {"VPXTablesPath", s.VPXTablesPath},
316 {"VPinballXPath", s.VPinballXPath},
317 {"vpxIniPath", s.vpxIniPath},
318 {"vpxStartArgs", s.vpxStartArgs},
319 {"vpxEndArgs", s.vpxEndArgs},
320 {"autoPatchTables", s.autoPatchTables}
321 }},
322 {"DPISettings", {
323 {"dpiScale", s.dpiScale},
324 {"enableDpiScaling", s.enableDpiScaling}
325 }},
326 {"DefaultMedia", {
327 {"defaultPlayfieldImage", s.defaultPlayfieldImage},
328 {"defaultBackglassImage", s.defaultBackglassImage},
329 {"defaultDmdImage", s.defaultDmdImage},
330 {"defaultWheelImage", s.defaultWheelImage},
331 {"defaultTopperImage", s.defaultTopperImage},
332 {"defaultPlayfieldVideo", s.defaultPlayfieldVideo},
333 {"defaultBackglassVideo", s.defaultBackglassVideo},
334 {"defaultDmdVideo", s.defaultDmdVideo},
335 {"defaultTopperVideo", s.defaultTopperVideo}
336 }},
337 {"CustomMedia", {
338 {"customPlayfieldImage", s.customPlayfieldImage},
339 {"customBackglassImage", s.customBackglassImage},
340 {"customDmdImage", s.customDmdImage},
341 {"customWheelImage", s.customWheelImage},
342 {"customTopperImage", s.customTopperImage},
343 {"customPlayfieldVideo", s.customPlayfieldVideo},
344 {"customBackglassVideo", s.customBackglassVideo},
345 {"customDmdVideo", s.customDmdVideo},
346 {"customTopperVideo", s.customTopperVideo},
347 {"tableMusic", s.tableMusic},
348 {"customLaunchSound", s.customLaunchSound}
349 }},
350 {"WindowSettings", {
351 {"videoBackend", s.videoBackend},
352 {"useVPinballXIni", s.useVPinballXIni},
353 {"playfieldWindowWidth", s.playfieldWindowWidth},
354 {"playfieldWindowHeight", s.playfieldWindowHeight},
355 {"playfieldX", s.playfieldX},
356 {"playfieldY", s.playfieldY},
357 {"showBackglass", s.showBackglass},
358 {"backglassWindowWidth", s.backglassWindowWidth},
359 {"backglassWindowHeight", s.backglassWindowHeight},
360 {"backglassX", s.backglassX},
361 {"backglassY", s.backglassY},
362 {"showDMD", s.showDMD},
363 {"dmdWindowWidth", s.dmdWindowWidth},
364 {"dmdWindowHeight", s.dmdWindowHeight},
365 {"dmdX", s.dmdX},
366 {"dmdY", s.dmdY},
367 {"showTopper", s.showTopper},
368 {"topperWindowWidth", s.topperWindowWidth},
369 {"topperWindowHeight", s.topperWindowHeight},
370 {"topperWindowX", s.topperWindowX},
371 {"topperWindowY", s.topperWindowY}
372 }},
373 {"TableMetadata", {
374 {"titleSource", s.titleSource},
375 {"fetchVPSdb", s.fetchVPSdb},
376 {"useVpxtool", s.useVpxtool},
377 {"fetchVpinMediaDb", s.fetchVpinMediaDb},
378 {"resizeToWindows", s.resizeToWindows},
379 {"ignoreScanners", s.ignoreScanners},
380 {"forceRebuildMetadata", s.forceRebuildMetadata},
381 {"titleSortBy", s.titleSortBy},
382 {"showMetadata", s.showMetadata},
383 {"metadataPanelWidth", s.metadataPanelWidth},
384 {"metadataPanelHeight", s.metadataPanelHeight},
385 {"metadataPanelAlpha", s.metadataPanelAlpha},
386 {"titleWeight", s.titleWeight},
387 {"yearWeight", s.yearWeight},
388 {"manufacturerWeight", s.manufacturerWeight},
389 {"romWeight", s.romWeight},
390 {"titleThreshold", s.titleThreshold},
391 {"confidenceThreshold", s.confidenceThreshold}
392 }},
393 {"UIWidgets", {
394 {"showArrowHint", s.showArrowHint},
395 {"arrowHintWidth", s.arrowHintWidth},
396 {"arrowHintHeight", s.arrowHintHeight},
397 {"arrowThickness", s.arrowThickness},
398 {"arrowAlpha", s.arrowAlpha},
399 {"arrowGlow", s.arrowGlow},
400 {"arrowGlowColor", {s.arrowGlowColor.r, s.arrowGlowColor.g, s.arrowGlowColor.b, s.arrowGlowColor.a}},
401 {"arrowColorTop", {s.arrowColorTop.r, s.arrowColorTop.g, s.arrowColorTop.b, s.arrowColorTop.a}},
402 {"arrowColorBottom", {s.arrowColorBottom.r, s.arrowColorBottom.g, s.arrowColorBottom.b, s.arrowColorBottom.a}},
403 {"showScrollbar", s.showScrollbar},
404 {"scrollbarWidth", s.scrollbarWidth},
405 {"thumbWidth", s.thumbWidth},
406 {"scrollbarLength", s.scrollbarLength},
407 {"scrollbarColor", {s.scrollbarColor.r, s.scrollbarColor.g, s.scrollbarColor.b, s.scrollbarColor.a}},
408 {"scrollbarThumbColor", {s.scrollbarThumbColor.r, s.scrollbarThumbColor.g, s.scrollbarThumbColor.b, s.scrollbarThumbColor.a}}
409 }},
410 {"TitleDisplay", {
411 {"showWheel", s.showWheel},
412 {"wheelWindow", s.wheelWindow},
413 {"showTitle", s.showTitle},
414 {"titleWindow", s.titleWindow},
415 {"fontPath", s.fontPath},
416 {"fontColor", {s.fontColor.r, s.fontColor.g, s.fontColor.b, s.fontColor.a}},
417 {"fontBgColor", {s.fontBgColor.r, s.fontBgColor.g, s.fontBgColor.b, s.fontBgColor.a}},
418 {"fontSize", s.fontSize},
419 {"titleX", s.titleX},
420 {"titleY", s.titleY}
421 }},
422 {"MediaDimensions", {
423 {"forceImagesOnly", s.forceImagesOnly},
424 {"wheelMediaHeight", s.wheelMediaHeight},
425 {"wheelMediaWidth", s.wheelMediaWidth},
426 {"wheelMediaX", s.wheelMediaX},
427 {"wheelMediaY", s.wheelMediaY},
428 {"playfieldMediaWidth", s.playfieldMediaWidth},
429 {"playfieldMediaHeight", s.playfieldMediaHeight},
430 {"playfieldMediaX", s.playfieldMediaX},
431 {"playfieldMediaY", s.playfieldMediaY},
432 {"playfieldRotation", s.playfieldRotation},
433 {"backglassMediaWidth", s.backglassMediaWidth},
434 {"backglassMediaHeight", s.backglassMediaHeight},
435 {"backglassMediaX", s.backglassMediaX},
436 {"backglassMediaY", s.backglassMediaY},
437 {"backglassRotation", s.backglassRotation},
438 {"dmdMediaWidth", s.dmdMediaWidth},
439 {"dmdMediaHeight", s.dmdMediaHeight},
440 {"dmdMediaX", s.dmdMediaX},
441 {"dmdMediaY", s.dmdMediaY},
442 {"dmdRotation", s.dmdRotation},
443 {"topperMediaWidth", s.topperMediaWidth},
444 {"topperMediaHeight", s.topperMediaHeight},
445 {"topperMediaX", s.topperMediaX},
446 {"topperMediaY", s.topperMediaY},
447 {"topperRotation", s.topperRotation}
448 }},
449 {"AudioSettings", {
450 {"masterMute", s.masterMute},
451 {"masterVol", s.masterVol},
452 {"mediaAudioMute", s.mediaAudioMute},
453 {"mediaAudioVol", s.mediaAudioVol},
454 {"tableMusicMute", s.tableMusicMute},
455 {"tableMusicVol", s.tableMusicVol},
456 {"interfaceAudioMute", s.interfaceAudioMute},
457 {"interfaceAudioVol", s.interfaceAudioVol},
458 {"interfaceAmbienceMute", s.interfaceAmbienceMute},
459 {"interfaceAmbienceVol", s.interfaceAmbienceVol}
460 }},
461 {"UISounds", {
462 {"scrollNormalSound", s.scrollNormalSound},
463 {"scrollFastSound", s.scrollFastSound},
464 {"scrollJumpSound", s.scrollJumpSound},
465 {"scrollRandomSound", s.scrollRandomSound},
466 {"launchTableSound", s.launchTableSound},
467 {"launchScreenshotSound", s.launchScreenshotSound},
468 {"panelToggleSound", s.panelToggleSound},
469 {"screenshotTakeSound", s.screenshotTakeSound},
470 {"ambienceSound", s.ambienceSound}
471 }},
472 {"Internal", {
473 {"exeDir", s.exeDir},
474 {"vpxSubCmd", s.vpxSubCmd},
475 {"vpsDbPath", s.vpsDbPath},
476 {"vpsDbUpdateFrequency", s.vpsDbUpdateFrequency},
477 {"vpsDbLastUpdated", s.vpsDbLastUpdated},
478 {"vpxtoolBin", s.vpxtoolBin},
479 {"vpxtoolIndex", s.vpxtoolIndex},
480 {"indexPath", s.indexPath},
481 {"screenshotWait", s.screenshotWait},
482 {"configUIWidth", s.configUIWidth},
483 {"configUIHeight", s.configUIHeight}
484 }},
485 {"Keybinds", s.keybinds_}
486 };
487 }
488
489 friend void from_json(const nlohmann::json& j, Settings& s) {
490 // VPX
491 s.VPXTablesPath = j.value("VPX", nlohmann::json{}).value("VPXTablesPath", s.VPXTablesPath);
492 s.VPinballXPath = j.value("VPX", nlohmann::json{}).value("VPinballXPath", s.VPinballXPath);
493 s.vpxIniPath = j.value("VPX", nlohmann::json{}).value("vpxIniPath", s.vpxIniPath);
494 s.vpxStartArgs = j.value("VPX", nlohmann::json{}).value("vpxStartArgs", s.vpxStartArgs);
495 s.vpxEndArgs = j.value("VPX", nlohmann::json{}).value("vpxEndArgs", s.vpxEndArgs);
496 s.autoPatchTables = j.value("VPX", nlohmann::json{}).value("autoPatchTables", s.autoPatchTables);
497
498 // DPISettings
499 s.dpiScale = j.value("DPISettings", nlohmann::json{}).value("dpiScale", s.dpiScale);
500 s.enableDpiScaling = j.value("DPISettings", nlohmann::json{}).value("enableDpiScaling", s.enableDpiScaling);
501
502 // DefaultMedia
503 s.defaultPlayfieldImage = j.value("DefaultMedia", nlohmann::json{}).value("defaultPlayfieldImage", s.defaultPlayfieldImage);
504 s.defaultBackglassImage = j.value("DefaultMedia", nlohmann::json{}).value("defaultBackglassImage", s.defaultBackglassImage);
505 s.defaultDmdImage = j.value("DefaultMedia", nlohmann::json{}).value("defaultDmdImage", s.defaultDmdImage);
506 s.defaultWheelImage = j.value("DefaultMedia", nlohmann::json{}).value("defaultWheelImage", s.defaultWheelImage);
507 s.defaultTopperImage = j.value("DefaultMedia", nlohmann::json{}).value("defaultTopperImage", s.defaultTopperImage);
508 s.defaultPlayfieldVideo = j.value("DefaultMedia", nlohmann::json{}).value("defaultPlayfieldVideo", s.defaultPlayfieldVideo);
509 s.defaultBackglassVideo = j.value("DefaultMedia", nlohmann::json{}).value("defaultBackglassVideo", s.defaultBackglassVideo);
510 s.defaultDmdVideo = j.value("DefaultMedia", nlohmann::json{}).value("defaultDmdVideo", s.defaultDmdVideo);
511 s.defaultTopperVideo = j.value("DefaultMedia", nlohmann::json{}).value("defaultTopperVideo", s.defaultTopperVideo);
512
513 // CustomMedia
514 s.customPlayfieldImage = j.value("CustomMedia", nlohmann::json{}).value("customPlayfieldImage", s.customPlayfieldImage);
515 s.customBackglassImage = j.value("CustomMedia", nlohmann::json{}).value("customBackglassImage", s.customBackglassImage);
516 s.customDmdImage = j.value("CustomMedia", nlohmann::json{}).value("customDmdImage", s.customDmdImage);
517 s.customWheelImage = j.value("CustomMedia", nlohmann::json{}).value("customWheelImage", s.customWheelImage);
518 s.customTopperImage = j.value("CustomMedia", nlohmann::json{}).value("customTopperImage", s.customTopperImage);
519 s.customPlayfieldVideo = j.value("CustomMedia", nlohmann::json{}).value("customPlayfieldVideo", s.customPlayfieldVideo);
520 s.customBackglassVideo = j.value("CustomMedia", nlohmann::json{}).value("customBackglassVideo", s.customBackglassVideo);
521 s.customDmdVideo = j.value("CustomMedia", nlohmann::json{}).value("customDmdVideo", s.customDmdVideo);
522 s.customTopperVideo = j.value("CustomMedia", nlohmann::json{}).value("customTopperVideo", s.customTopperVideo);
523 s.tableMusic = j.value("CustomMedia", nlohmann::json{}).value("tableMusic", s.tableMusic);
524 s.customLaunchSound = j.value("CustomMedia", nlohmann::json{}).value("customLaunchSound", s.customLaunchSound);
525
526 // WindowSettings
527 s.videoBackend = j.value("WindowSettings", nlohmann::json{}).value("videoBackend", s.videoBackend);
528 s.useVPinballXIni = j.value("WindowSettings", nlohmann::json{}).value("useVPinballXIni", s.useVPinballXIni);
529 s.playfieldWindowWidth = j.value("WindowSettings", nlohmann::json{}).value("playfieldWindowWidth", s.playfieldWindowWidth);
530 s.playfieldWindowHeight = j.value("WindowSettings", nlohmann::json{}).value("playfieldWindowHeight", s.playfieldWindowHeight);
531 s.playfieldX = j.value("WindowSettings", nlohmann::json{}).value("playfieldX", s.playfieldX);
532 s.playfieldY = j.value("WindowSettings", nlohmann::json{}).value("playfieldY", s.playfieldY);
533 s.showBackglass = j.value("WindowSettings", nlohmann::json{}).value("showBackglass", s.showBackglass);
534 s.backglassWindowWidth = j.value("WindowSettings", nlohmann::json{}).value("backglassWindowWidth", s.backglassWindowWidth);
535 s.backglassWindowHeight = j.value("WindowSettings", nlohmann::json{}).value("backglassWindowHeight", s.backglassWindowHeight);
536 s.backglassX = j.value("WindowSettings", nlohmann::json{}).value("backglassX", s.backglassX);
537 s.backglassY = j.value("WindowSettings", nlohmann::json{}).value("backglassY", s.backglassY);
538 s.showDMD = j.value("WindowSettings", nlohmann::json{}).value("showDMD", s.showDMD);
539 s.dmdWindowWidth = j.value("WindowSettings", nlohmann::json{}).value("dmdWindowWidth", s.dmdWindowWidth);
540 s.dmdWindowHeight = j.value("WindowSettings", nlohmann::json{}).value("dmdWindowHeight", s.dmdWindowHeight);
541 s.dmdX = j.value("WindowSettings", nlohmann::json{}).value("dmdX", s.dmdX);
542 s.dmdY = j.value("WindowSettings", nlohmann::json{}).value("dmdY", s.dmdY);
543 s.showTopper = j.value("WindowSettings", nlohmann::json{}).value("showTopper", s.showTopper);
544 s.topperWindowWidth = j.value("WindowSettings", nlohmann::json{}).value("topperWindowWidth", s.topperWindowWidth);
545 s.topperWindowHeight = j.value("WindowSettings", nlohmann::json{}).value("topperWindowHeight", s.topperWindowHeight);
546 s.topperWindowX = j.value("WindowSettings", nlohmann::json{}).value("topperWindowX", s.topperWindowX);
547 s.topperWindowY = j.value("WindowSettings", nlohmann::json{}).value("topperWindowY", s.topperWindowY);
548
549 // TableMetadata
550 s.titleSource = j.value("TableMetadata", nlohmann::json{}).value("titleSource", s.titleSource);
551 s.fetchVPSdb = j.value("TableMetadata", nlohmann::json{}).value("fetchVPSdb", s.fetchVPSdb);
552 s.useVpxtool = j.value("TableMetadata", nlohmann::json{}).value("useVpxtool", s.useVpxtool);
553 s.fetchVpinMediaDb = j.value("TableMetadata", nlohmann::json{}).value("fetchVpinMediaDb", s.fetchVpinMediaDb);
554 s.resizeToWindows = j.value("TableMetadata", nlohmann::json{}).value("resizeToWindows", s.resizeToWindows);
555 s.forceRebuildMetadata = j.value("TableMetadata", nlohmann::json{}).value("forceRebuildMetadata", s.forceRebuildMetadata);
556 s.ignoreScanners = j.value("TableMetadata", nlohmann::json{}).value("ignoreScanners", s.ignoreScanners);
557 s.titleSortBy = j.value("TableMetadata", nlohmann::json{}).value("titleSortBy", s.titleSortBy);
558 s.showMetadata = j.value("TableMetadata", nlohmann::json{}).value("showMetadata", s.showMetadata);
559 s.metadataPanelWidth = j.value("TableMetadata", nlohmann::json{}).value("metadataPanelWidth", s.metadataPanelWidth);
560 s.metadataPanelHeight = j.value("TableMetadata", nlohmann::json{}).value("metadataPanelHeight", s.metadataPanelHeight);
561 s.metadataPanelAlpha = j.value("TableMetadata", nlohmann::json{}).value("metadataPanelAlpha", s.metadataPanelAlpha);
562
563 s.titleWeight = j.value("TableMetadata", nlohmann::json{}).value("titleWeight", s.titleWeight);
564 s.yearWeight = j.value("TableMetadata", nlohmann::json{}).value("yearWeight", s.yearWeight);
565 s.manufacturerWeight = j.value("TableMetadata", nlohmann::json{}).value("manufacturerWeight", s.manufacturerWeight);
566 s.romWeight = j.value("TableMetadata", nlohmann::json{}).value("romWeight", s.romWeight);
567 s.titleThreshold = j.value("TableMetadata", nlohmann::json{}).value("titleThreshold", s.titleThreshold);
568 s.confidenceThreshold = j.value("TableMetadata", nlohmann::json{}).value("confidenceThreshold", s.confidenceThreshold);
569
570 // UIWidgets
571 s.showArrowHint = j.value("UIWidgets", nlohmann::json{}).value("showArrowHint", s.showArrowHint);
572 s.arrowHintWidth = j.value("UIWidgets", nlohmann::json{}).value("arrowHintWidth", s.arrowHintWidth);
573 s.arrowHintHeight = j.value("UIWidgets", nlohmann::json{}).value("arrowHintHeight", s.arrowHintHeight);
574 s.arrowThickness = j.value("UIWidgets", nlohmann::json{}).value("arrowThickness", s.arrowThickness);
575 s.arrowAlpha = j.value("UIWidgets", nlohmann::json{}).value("arrowAlpha", s.arrowAlpha);
576 s.arrowGlow = j.value("UIWidgets", nlohmann::json{}).value("arrowGlow", s.arrowGlow);
577 if (j.value("UIWidgets", nlohmann::json{}).contains("arrowGlowColor") && j["UIWidgets"]["arrowGlowColor"].is_array() && j["UIWidgets"]["arrowGlowColor"].size() == 4) {
578 s.arrowGlowColor = {static_cast<Uint8>(j["UIWidgets"]["arrowGlowColor"][0]),
579 static_cast<Uint8>(j["UIWidgets"]["arrowGlowColor"][1]),
580 static_cast<Uint8>(j["UIWidgets"]["arrowGlowColor"][2]),
581 static_cast<Uint8>(j["UIWidgets"]["arrowGlowColor"][3])};
582 }
583 if (j.value("UIWidgets", nlohmann::json{}).contains("arrowColorTop") && j["UIWidgets"]["arrowColorTop"].is_array() && j["UIWidgets"]["arrowColorTop"].size() == 4) {
584 s.arrowColorTop = {static_cast<Uint8>(j["UIWidgets"]["arrowColorTop"][0]),
585 static_cast<Uint8>(j["UIWidgets"]["arrowColorTop"][1]),
586 static_cast<Uint8>(j["UIWidgets"]["arrowColorTop"][2]),
587 static_cast<Uint8>(j["UIWidgets"]["arrowColorTop"][3])};
588 }
589 if (j.value("UIWidgets", nlohmann::json{}).contains("arrowColorBottom") && j["UIWidgets"]["arrowColorBottom"].is_array() && j["UIWidgets"]["arrowColorBottom"].size() == 4) {
590 s.arrowColorBottom = {static_cast<Uint8>(j["UIWidgets"]["arrowColorBottom"][0]),
591 static_cast<Uint8>(j["UIWidgets"]["arrowColorBottom"][1]),
592 static_cast<Uint8>(j["UIWidgets"]["arrowColorBottom"][2]),
593 static_cast<Uint8>(j["UIWidgets"]["arrowColorBottom"][3])};
594 }
595 s.showScrollbar = j.value("UIWidgets", nlohmann::json{}).value("showScrollbar", s.showScrollbar);
596 s.scrollbarWidth = j.value("UIWidgets", nlohmann::json{}).value("scrollbarWidth", s.scrollbarWidth);
597 s.thumbWidth = j.value("UIWidgets", nlohmann::json{}).value("thumbWidth", s.thumbWidth);
598 s.scrollbarLength = j.value("UIWidgets", nlohmann::json{}).value("scrollbarLength", s.scrollbarLength);
599 if (j.value("UIWidgets", nlohmann::json{}).contains("scrollbarColor") && j["UIWidgets"]["scrollbarColor"].is_array() && j["UIWidgets"]["scrollbarColor"].size() == 4) {
600 s.scrollbarColor = {static_cast<Uint8>(j["UIWidgets"]["scrollbarColor"][0]),
601 static_cast<Uint8>(j["UIWidgets"]["scrollbarColor"][1]),
602 static_cast<Uint8>(j["UIWidgets"]["scrollbarColor"][2]),
603 static_cast<Uint8>(j["UIWidgets"]["scrollbarColor"][3])};
604 }
605 if (j.value("UIWidgets", nlohmann::json{}).contains("scrollbarThumbColor") && j["UIWidgets"]["scrollbarThumbColor"].is_array() && j["UIWidgets"]["scrollbarThumbColor"].size() == 4) {
606 s.scrollbarThumbColor = {static_cast<Uint8>(j["UIWidgets"]["scrollbarThumbColor"][0]),
607 static_cast<Uint8>(j["UIWidgets"]["scrollbarThumbColor"][1]),
608 static_cast<Uint8>(j["UIWidgets"]["scrollbarThumbColor"][2]),
609 static_cast<Uint8>(j["UIWidgets"]["scrollbarThumbColor"][3])};
610 }
611
612 // TitleDisplay
613 s.showWheel = j.value("TitleDisplay", nlohmann::json{}).value("showWheel", s.showWheel);
614 s.wheelWindow = j.value("TitleDisplay", nlohmann::json{}).value("wheelWindow", s.wheelWindow);
615 s.showTitle = j.value("TitleDisplay", nlohmann::json{}).value("showTitle", s.showTitle);
616 s.titleWindow = j.value("TitleDisplay", nlohmann::json{}).value("titleWindow", s.titleWindow);
617 s.fontPath = j.value("TitleDisplay", nlohmann::json{}).value("fontPath", s.fontPath);
618 if (j.value("TitleDisplay", nlohmann::json{}).contains("fontColor") && j["TitleDisplay"]["fontColor"].is_array() && j["TitleDisplay"]["fontColor"].size() == 4) {
619 s.fontColor = {static_cast<Uint8>(j["TitleDisplay"]["fontColor"][0]),
620 static_cast<Uint8>(j["TitleDisplay"]["fontColor"][1]),
621 static_cast<Uint8>(j["TitleDisplay"]["fontColor"][2]),
622 static_cast<Uint8>(j["TitleDisplay"]["fontColor"][3])};
623 }
624 if (j.value("TitleDisplay", nlohmann::json{}).contains("fontBgColor") && j["TitleDisplay"]["fontBgColor"].is_array() && j["TitleDisplay"]["fontBgColor"].size() == 4) {
625 s.fontBgColor = {static_cast<Uint8>(j["TitleDisplay"]["fontBgColor"][0]),
626 static_cast<Uint8>(j["TitleDisplay"]["fontBgColor"][1]),
627 static_cast<Uint8>(j["TitleDisplay"]["fontBgColor"][2]),
628 static_cast<Uint8>(j["TitleDisplay"]["fontBgColor"][3])};
629 }
630 s.fontSize = j.value("TitleDisplay", nlohmann::json{}).value("fontSize", s.fontSize);
631 s.titleX = j.value("TitleDisplay", nlohmann::json{}).value("titleX", s.titleX);
632 s.titleY = j.value("TitleDisplay", nlohmann::json{}).value("titleY", s.titleY);
633
634 // MediaDimensions
635 s.forceImagesOnly = j.value("MediaDimensions", nlohmann::json{}).value("forceImagesOnly", s.forceImagesOnly);
636 s.wheelMediaHeight = j.value("MediaDimensions", nlohmann::json{}).value("wheelMediaHeight", s.wheelMediaHeight);
637 s.wheelMediaWidth = j.value("MediaDimensions", nlohmann::json{}).value("wheelMediaWidth", s.wheelMediaWidth);
638 s.wheelMediaX = j.value("MediaDimensions", nlohmann::json{}).value("wheelMediaX", s.wheelMediaX);
639 s.wheelMediaY = j.value("MediaDimensions", nlohmann::json{}).value("wheelMediaY", s.wheelMediaY);
640 s.playfieldMediaWidth = j.value("MediaDimensions", nlohmann::json{}).value("playfieldMediaWidth", s.playfieldMediaWidth);
641 s.playfieldMediaHeight = j.value("MediaDimensions", nlohmann::json{}).value("playfieldMediaHeight", s.playfieldMediaHeight);
642 s.playfieldMediaX = j.value("MediaDimensions", nlohmann::json{}).value("playfieldMediaX", s.playfieldMediaX);
643 s.playfieldMediaY = j.value("MediaDimensions", nlohmann::json{}).value("playfieldMediaY", s.playfieldMediaY);
644 s.playfieldRotation = j.value("MediaDimensions", nlohmann::json{}).value("playfieldRotation", s.playfieldRotation);
645 s.backglassMediaWidth = j.value("MediaDimensions", nlohmann::json{}).value("backglassMediaWidth", s.backglassMediaWidth);
646 s.backglassMediaHeight = j.value("MediaDimensions", nlohmann::json{}).value("backglassMediaHeight", s.backglassMediaHeight);
647 s.backglassMediaX = j.value("MediaDimensions", nlohmann::json{}).value("backglassMediaX", s.backglassMediaX);
648 s.backglassMediaY = j.value("MediaDimensions", nlohmann::json{}).value("backglassMediaY", s.backglassMediaY);
649 s.backglassRotation = j.value("MediaDimensions", nlohmann::json{}).value("backglassRotation", s.backglassRotation);
650 s.dmdMediaWidth = j.value("MediaDimensions", nlohmann::json{}).value("dmdMediaWidth", s.dmdMediaWidth);
651 s.dmdMediaHeight = j.value("MediaDimensions", nlohmann::json{}).value("dmdMediaHeight", s.dmdMediaHeight);
652 s.dmdMediaX = j.value("MediaDimensions", nlohmann::json{}).value("dmdMediaX", s.dmdMediaX);
653 s.dmdMediaY = j.value("MediaDimensions", nlohmann::json{}).value("dmdMediaY", s.dmdMediaY);
654 s.dmdRotation = j.value("MediaDimensions", nlohmann::json{}).value("dmdRotation", s.dmdRotation);
655 s.topperMediaWidth = j.value("MediaDimensions", nlohmann::json{}).value("topperMediaWidth", s.topperMediaWidth);
656 s.topperMediaHeight = j.value("MediaDimensions", nlohmann::json{}).value("topperMediaHeight", s.topperMediaHeight);
657 s.topperMediaX = j.value("MediaDimensions", nlohmann::json{}).value("topperMediaX", s.topperMediaX);
658 s.topperMediaY = j.value("MediaDimensions", nlohmann::json{}).value("topperMediaY", s.topperMediaY);
659 s.topperRotation = j.value("MediaDimensions", nlohmann::json{}).value("topperRotation", s.topperRotation);
660
661 // AudioSettings
662 s.masterMute = j.value("AudioSettings", nlohmann::json{}).value("masterMute", s.masterMute);
663 s.masterVol = j.value("AudioSettings", nlohmann::json{}).value("masterVol", s.masterVol);
664 s.mediaAudioMute = j.value("AudioSettings", nlohmann::json{}).value("mediaAudioMute", s.mediaAudioMute);
665 s.mediaAudioVol = j.value("AudioSettings", nlohmann::json{}).value("mediaAudioVol", s.mediaAudioVol);
666 s.tableMusicMute = j.value("AudioSettings", nlohmann::json{}).value("tableMusicMute", s.tableMusicMute);
667 s.tableMusicVol = j.value("AudioSettings", nlohmann::json{}).value("tableMusicVol", s.tableMusicVol);
668 s.interfaceAudioMute = j.value("AudioSettings", nlohmann::json{}).value("interfaceAudioMute", s.interfaceAudioMute);
669 s.interfaceAudioVol = j.value("AudioSettings", nlohmann::json{}).value("interfaceAudioVol", s.interfaceAudioVol);
670 s.interfaceAmbienceMute = j.value("AudioSettings", nlohmann::json{}).value("interfaceAmbienceMute", s.interfaceAmbienceMute);
671 s.interfaceAmbienceVol = j.value("AudioSettings", nlohmann::json{}).value("interfaceAmbienceVol", s.interfaceAmbienceVol);
672
673 // UISounds
674 s.scrollNormalSound = j.value("UISounds", nlohmann::json{}).value("scrollNormalSound", s.scrollNormalSound);
675 s.scrollFastSound = j.value("UISounds", nlohmann::json{}).value("scrollFastSound", s.scrollFastSound);
676 s.scrollJumpSound = j.value("UISounds", nlohmann::json{}).value("scrollJumpSound", s.scrollJumpSound);
677 s.scrollRandomSound = j.value("UISounds", nlohmann::json{}).value("scrollRandomSound", s.scrollRandomSound);
678 s.launchTableSound = j.value("UISounds", nlohmann::json{}).value("launchTableSound", s.launchTableSound);
679 s.launchScreenshotSound = j.value("UISounds", nlohmann::json{}).value("launchScreenshotSound", s.launchScreenshotSound);
680 s.panelToggleSound = j.value("UISounds", nlohmann::json{}).value("panelToggleSound", s.panelToggleSound);
681 s.screenshotTakeSound = j.value("UISounds", nlohmann::json{}).value("screenshotTakeSound", s.screenshotTakeSound);
682 s.ambienceSound = j.value("UISounds", nlohmann::json{}).value("ambienceSound", s.ambienceSound);
683
684 // Internal
685 s.exeDir = j.value("Internal", nlohmann::json{}).value("exeDir", s.exeDir);
686 s.vpxSubCmd = j.value("Internal", nlohmann::json{}).value("vpxSubCmd", s.vpxSubCmd);
687 s.vpsDbPath = j.value("Internal", nlohmann::json{}).value("vpsDbPath", s.vpsDbPath);
688 s.vpsDbUpdateFrequency = j.value("Internal", nlohmann::json{}).value("vpsDbUpdateFrequency", s.vpsDbUpdateFrequency);
689 s.vpsDbLastUpdated = j.value("Internal", nlohmann::json{}).value("vpsDbLastUpdated", s.vpsDbLastUpdated);
690 s.vpxtoolIndex = j.value("Internal", nlohmann::json{}).value("vpxtoolIndex", s.vpxtoolIndex);
691 s.vpxtoolBin = j.value("Internal", nlohmann::json{}).value("vpxtoolBin", s.vpxtoolBin);
692 s.indexPath = j.value("Internal", nlohmann::json{}).value("indexPath", s.indexPath);
693 s.configUIWidth = j.value("Internal", nlohmann::json{}).value("configUIWidth", s.configUIWidth);
694 s.configUIHeight = j.value("Internal", nlohmann::json{}).value("configUIHeight", s.configUIHeight);
695 s.screenshotWait = j.value("Internal", nlohmann::json{}).value("screenshotWait", s.screenshotWait);
696
697 // [Keybinds]
698 if (j.contains("Keybinds") && j["Keybinds"].is_object()) {
699 s.keybinds_.clear();
700 for (auto& [key, value] : j["Keybinds"].items()) {
701 if (value.is_string()) {
702 s.keybinds_[key] = value.get<std::string>();
703 }
704 }
705 }
706 }
707};
708
709// Define settingsMetadata
710inline const std::map<std::string, std::pair<Settings::ReloadType, std::string>> Settings::settingsMetadata = {
711 // VPX
712 {"VPXTablesPath", {Settings::ReloadType::Tables, "Defines the absolute path to the folder containing VPX table files.\n"
713 "\n"
714 "It must be a full path.\n"
715 "(e.g., /home/user/tables/).\n"
716 "\n"
717 "Final command:\n"
718 "StartArgs VPinballXPath -play VPXTablesPath/<selectedtable>.vpx EndArgs"}},
719 {"VPinballXPath", {Settings::ReloadType::None, "Defines the absolute path to the VPinballX executable.\n"
720 "\n"
721 "Final command:\n"
722 "StartArgs VPinballXPath -play VPXTablesPath/<selectedtable>.vpx EndArgs"}},
723 {"vpxIniPath", {Settings::ReloadType::None, "Defines the absolute path to the VPinballX.ini file.\n"
724 "If left empty it will search the default location\n"
725 "~/.vpinball/VPinballX.ini"}},
726 {"vpxStartArgs", {Settings::ReloadType::None, "Optional command-line arguments to prepend to the executable.\n"
727 "\n"
728 "Final command:\n"
729 "StartArgs VPinballXPath -play VPXTablesPath/<selectedtable>.vpx EndArgs"}},
730 {"vpxEndArgs", {Settings::ReloadType::None, "Optional arguments to append after the table file in the command.\n"
731 "\n"
732 "Final command:\n"
733 "StartArgs VPinballXPath -play VPXTablesPath/<selectedtable>.vpx EndArgs"}},
734 {"autoPatchTables",{Settings::ReloadType::Tables, "Automatically apply latest VBScript patches during table scans.\n"}},
735 // DPISettings (TODO: Should also reload fonts!)
736 {"dpiScale", {Settings::ReloadType::Windows, "Manual DPI scale override.\n"
737 "Only used when EnableDpiScaling is false.\n"
738 "1.0 = 100%, 1.5 = 150%, etc."}},
739 {"enableDpiScaling", {Settings::ReloadType::Windows, "Enable automatic DPI scaling based on system settings.\n"
740 "When enabled, the frontend will scale according to your monitor's DPI.\n"
741 "Disable for manual control via DpiScale."}},
742 // DefaultMedia
743 {"defaultPlayfieldImage", {Settings::ReloadType::Tables, "Relative path to the default table preview image.\n"
744 "Used when a table has no custom image."}},
745 {"defaultBackglassImage", {Settings::ReloadType::Tables, "Relative path to the default backglass image.\n"
746 "Used when a table has no custom backglass."}},
747 {"defaultDmdImage", {Settings::ReloadType::Tables, "Relative path to the default DMD image.\n"
748 "Used when a table has no custom DMD image."}},
749 {"defaultWheelImage", {Settings::ReloadType::Tables, "Relative path to the default wheel image.\n"
750 "Used when a table has no custom wheel art."}},
751 {"defaultTopperImage", {Settings::ReloadType::Tables, "Relative path to the default Topper image.\n"
752 "Used when a table has no custom Topper art."}},
753 {"defaultPlayfieldVideo", {Settings::ReloadType::Tables, "Relative path to the default table preview video.\n"
754 "Used when a table has no custom video."}},
755 {"defaultBackglassVideo", {Settings::ReloadType::Tables, "Relative path to the default backglass video.\n"
756 "Used when a table has no custom backglass video."}},
757 {"defaultDmdVideo", {Settings::ReloadType::Tables, "Relative path to the default DMD video.\n"
758 "Used when a table has no custom DMD video."}},
759 {"defaultTopperVideo", {Settings::ReloadType::Tables, "Relative path to the default Topper video.\n"
760 "Used when a table has no custom Topper video."}},
761 // CustomMedia
762 {"customPlayfieldImage", {Settings::ReloadType::Tables, "Relative path to the table's preview image.\n"
763 "These are relative to your table folder.\n"
764 "/path/to/tables/<table_folder>/"}},
765 {"customBackglassImage", {Settings::ReloadType::Tables, "Relative path to the backglass image.\n"
766 "These are relative to your table folder.\n"
767 "/path/to/tables/<table_folder>/"}},
768 {"customDmdImage", {Settings::ReloadType::Tables, "Relative path to the DMD image.\n"
769 "These are relative to your table folder.\n"
770 "/path/to/tables/<table_folder>/"}},
771 {"customWheelImage", {Settings::ReloadType::Tables, "Relative path to the wheel image for the table.\n"
772 "These are relative to your table folder.\n"
773 "/path/to/tables/<table_folder>/"}},
774 {"customTopperImage", {Settings::ReloadType::Tables, "Relative path to the topper image.\n"
775 "These are relative to your table folder.\n"
776 "/path/to/tables/<table_folder>/"}},
777 {"customPlayfieldVideo", {Settings::ReloadType::Tables, "Relative path to the table preview video.\n"
778 "These are relative to your table folder.\n"
779 "/path/to/tables/<table_folder>/"}},
780 {"customBackglassVideo", {Settings::ReloadType::Tables, "Relative path to the backglass video.\n"
781 "These are relative to your table folder.\n"
782 "/path/to/tables/<table_folder>/"}},
783 {"customDmdVideo", {Settings::ReloadType::Tables, "Relative path to the DMD video.\n"
784 "These are relative to your table folder.\n"
785 "/path/to/tables/<table_folder>/"}},
786 {"customTopperVideo", {Settings::ReloadType::Tables, "Relative path to the topper video.\n"
787 "These are relative to your table folder.\n"
788 "/path/to/tables/<table_folder>/"}},
789 {"tableMusic", {Settings::ReloadType::Tables, "Relative path to the table music file.\n"
790 "These are relative to your table folder.\n"
791 "/path/to/tables/<table_folder>/"}},
792 {"customLaunchSound", {Settings::ReloadType::Tables, "Relative path to the table launch audio file.\n"
793 "These are relative to your table folder.\n"
794 "/path/to/tables/<table_folder>/"}},
795 // WindowSettings
796 {"videoBackend", {Settings::ReloadType::Assets, "Select video playback backend:\n"
797 "VLC: Reliable software-based playback with broad format support. Ideal for compatibility.\n"
798 "FFmpeg: Efficient software-based playback with extensive codec support. Suitable for most systems.\n"
799 "GStreamer: Flexible playback with plugin-based architecture. Good for customized setups."}},
800 {"useVPinballXIni", {Settings::ReloadType::Windows, "Uses sizes and positions from ~/.vpinball/VPinballX.ini\n"
801 "Using this option will override options below."}},
802 {"playfieldWindowWidth", {Settings::ReloadType::Assets, "Width of the Playfield window in pixels.\n"
803 "This should be relative to your Playfield media width."}},
804 {"playfieldWindowHeight", {Settings::ReloadType::Assets, "Height of the Playfield window in pixels.\n"
805 "This should be relative to your Playfield media height."}},
806 {"playfieldX", {Settings::ReloadType::Assets, "X position of the Playfield window.\n"
807 "- You can drag and double-click a window to save its position"}},
808 {"playfieldY", {Settings::ReloadType::Assets, "Y position of the Playfield window.\n"
809 "- You can drag and double-click a window to save its position"}},
810 {"showBackglass", {Settings::ReloadType::Assets, "Show/hide the backglass window."}},
811 {"backglassWindowWidth", {Settings::ReloadType::Assets, "Width of the Backglass window in pixels.\n"
812 "This should be relative to your Backglass media width."}},
813 {"backglassWindowHeight", {Settings::ReloadType::Assets, "Height of the Backglass window in pixels.\n"
814 "This should be relative to your Backglass media height."}},
815 {"backglassX", {Settings::ReloadType::Assets, "X position of the Backglass window.\n"
816 "- You can drag and double-click a window to save its position"}},
817 {"backglassY", {Settings::ReloadType::Assets, "Y position of the Backglass window.\n"
818 "- You can drag and double-click a window to save its position"}},
819 {"showDMD", {Settings::ReloadType::Assets, "Show/hide the DMD window."}},
820 {"dmdWindowWidth", {Settings::ReloadType::Assets, "Width of the DMD window in pixels.\n"
821 "This should be relative to your DMD media width."}},
822 {"dmdWindowHeight", {Settings::ReloadType::Assets, "Height of the DMD window in pixels.\n"
823 "This should be relative to your DMD media height."}},
824 {"dmdX", {Settings::ReloadType::Assets, "X position of the DMD window.\n"
825 "- You can drag and double-click a window to save its position"}},
826 {"dmdY", {Settings::ReloadType::Assets, "Y position of the DMD window.\n"
827 "- You can drag and double-click a window to save its position"}},
828 {"showTopper", {Settings::ReloadType::Assets, "Show/hide the Topper window."}},
829 {"topperWindowWidth", {Settings::ReloadType::Assets, "Width of the Topper window in pixels.\n"
830 "This should be relative to your Topper media width."}},
831 {"topperWindowHeight", {Settings::ReloadType::Assets, "Height of the Topper window in pixels.\n"
832 "This should be relative to your Topper media height."}},
833 {"topperWindowX", {Settings::ReloadType::Assets, "X position of the Topper window.\n"
834 "- You can drag and double-click a window to save its position"}},
835 {"topperWindowY", {Settings::ReloadType::Assets, "Y position of the Topper window.\n"
836 "- You can drag and double-click a window to save its position"}},
837 // TableMetadata
838 {"titleSource", {Settings::ReloadType::Tables, "Select how table info will be displayed.\n"
839 "- filename: Scans the system for basic information.\n"
840 "- metadata: Extract metadata from files using several scanners."}},
841 {"fetchVPSdb", {Settings::ReloadType::Tables, "Fetches Virtual Pinball Spreadsheet database and\n"
842 "attempts to match with file metadata to improve information."}},
843 {"useVpxtool",{Settings::ReloadType::Tables, "Use an existing vpxtool_index.json or run vpxtool instead of internal VPin."}},
844
845 {"fetchVpinMediaDb", {Settings::ReloadType::Tables, "Download images from the Vpin Media Database.\n"
846 "- Requires VPSdb metadata."}},
847 {"resizeToWindows", {Settings::ReloadType::Tables, "Resize downloaded images to current windows configuration."}},
848
849 {"ignoreScanners", {Settings::ReloadType::Tables, "Ignore scanners and read straight from index.\n"
850 "- Rebuilding tables requires this to be uncheckd."}},
851 {"forceRebuildMetadata", {Settings::ReloadType::Tables, "Forces re-building metadata from scratch.\n"
852 "- Unchecking 'Fast Load' is required."}},
853 {"titleSortBy", {Settings::ReloadType::Tables, "Select the sorting of tables.\n"
854 "- Requires VPSdb metadata."}},
855 {"showMetadata", {Settings::ReloadType::Overlay, "Show/hide the metadata panel overlay on the playfield window."}},
856 {"metadataPanelWidth", {Settings::ReloadType::Overlay, "Width of the metadata panel as a fraction of the screen (0.1-1.0)"}},
857 {"metadataPanelHeight", {Settings::ReloadType::Overlay, "Height of the metadata panel as a fraction of the screen (0.1-1.0)"}},
858 {"metadataPanelAlpha", {Settings::ReloadType::Overlay, "Transparency of the metadata panel (0.1-1.0)"}},
859 {"titleWeight", {Settings::ReloadType::None, "Weight applied to title matching in the scoring process.\n"
860 "- Higher values prioritize title similarity when determining a match."}},
861 {"yearWeight", {Settings::ReloadType::None, "Weight applied to year matching in the scoring process.\n"
862 "- Higher values emphasize year consistency for better accuracy."}},
863 {"manufacturerWeight", {Settings::ReloadType::None, "Weight applied to manufacturer matching in the scoring process.\n"
864 "- Higher values increase the importance of manufacturer data."}},
865 {"romWeight", {Settings::ReloadType::None, "Weight applied to ROM matching in the scoring process.\n"
866 "- Higher values boost the score when ROM names align."}},
867 {"titleThreshold", {Settings::ReloadType::None, "Minimum similarity score (based on Levenshtein distance) required for a title to be considered a potential match."}},
868 {"confidenceThreshold", {Settings::ReloadType::None, "Minimum total score required to confirm a table as a match in the database.\n"
869 "- Higher values ensure stricter matching."}},
870
871 // UIWidgets
872 {"showArrowHint", {Settings::ReloadType::None, "Toggle visibility of the arrow hint widget."}},
873 {"arrowHintWidth", {Settings::ReloadType::None, "Width of the arrow hint in pixels."}},
874 {"arrowHintHeight", {Settings::ReloadType::None, "Height of the arrow hint in pixels."}},
875 {"arrowThickness", {Settings::ReloadType::None, "Thickness of the arrow hint in pixels."}},
876 {"arrowAlpha", {Settings::ReloadType::None, "Transparency of the arrow hint (0.1-1.0)"}},
877 {"arrowGlow", {Settings::ReloadType::None, "Glow effect size of the arrow hint."}},
878 {"arrowGlowColor", {Settings::ReloadType::None, "Color of the arrow hint glow."}},
879 {"arrowColorTop", {Settings::ReloadType::None, "Top color of the arrow hint gradient."}},
880 {"arrowColorBottom", {Settings::ReloadType::None, "Bottom color of the arrow hint gradient."}},
881 {"showScrollbar", {Settings::ReloadType::None, "Toggle visibility of the scrollbar."}},
882 {"scrollbarWidth", {Settings::ReloadType::None, "Width of the scrollbar in pixels."}},
883 {"thumbWidth", {Settings::ReloadType::None, "Width of the scrollbar thumb in pixels."}},
884 {"scrollbarLength", {Settings::ReloadType::None, "Length of the scrollbar as a fraction of the screen (0.1-1.0)"}},
885 {"scrollbarColor", {Settings::ReloadType::None, "Color of the scrollbar."}},
886 {"scrollbarThumbColor", {Settings::ReloadType::None, "Color of the scrollbar thumb."}},
887 // TitleDisplay
888 {"showWheel", {Settings::ReloadType::None, "Toggle visibility of the wheel image in the main window.\n"
889 "- Set to true to show the wheel, false to hide it."}},
890 {"wheelWindow", {Settings::ReloadType::Tables, "Select the window to display the wheel art."}},
891 {"showTitle", {Settings::ReloadType::None, "Toggle visibility of table titles in the main window.\n"
892 "- Set to true to show titles, false to hide them."}},
893 {"titleWindow", {Settings::ReloadType::Tables, "Select the window to display the table title."}},
894 {"fontPath", {Settings::ReloadType::Font, "Select a font for the table title display."}},
895 {"fontColor", {Settings::ReloadType::Font, "Color of the table title display text."}},
896 {"fontBgColor", {Settings::ReloadType::Font, "Background color behind the table title."}},
897 {"fontSize", {Settings::ReloadType::Font, "Font size in points for table title text rendering."}},
898 {"titleX", {Settings::ReloadType::Title, "X position of the table title"}},
899 {"titleY", {Settings::ReloadType::Title, "Y position of the table title"}},
900 // MediaDimensions
901 {"forceImagesOnly", {Settings::ReloadType::Tables, "Use only images (skip videos)."}},
902 {"wheelMediaHeight", {Settings::ReloadType::None, "Height of the wheel image in pixels."}},
903 {"wheelMediaWidth", {Settings::ReloadType::None, "Width of the wheel image in pixels."}},
904 {"wheelMediaX", {Settings::ReloadType::None, "X position of the wheel image."}},
905 {"wheelMediaY", {Settings::ReloadType::None, "Y position of the wheel image."}},
906 {"playfieldMediaWidth", {Settings::ReloadType::None, "Width of the playfield media in pixels."}},
907 {"playfieldMediaHeight", {Settings::ReloadType::None, "Height of the playfield media in pixels."}},
908 {"playfieldMediaX", {Settings::ReloadType::None, "X position of the playfield media.\n"
909 "- This position is relative to the playfield window."}},
910 {"playfieldMediaY", {Settings::ReloadType::None, "Y position of the playfield media.\n"
911 "- This position is relative to the playfield window."}},
912 {"playfieldRotation", {Settings::ReloadType::None, "Rotation of the Playfield media.\n"
913 "0 = no rotation\n"
914 "90, 180, 270, -90, etc"}},
915 {"backglassMediaWidth", {Settings::ReloadType::None, "Width of the backglass media in pixels."}},
916 {"backglassMediaHeight", {Settings::ReloadType::None, "Height of the backglass media in pixels."}},
917 {"backglassMediaX", {Settings::ReloadType::None, "X position of the backglass media.\n"
918 "- This position is relative to the backglass window."}},
919 {"backglassMediaY", {Settings::ReloadType::None, "Y position of the backglass media.\n"
920 "- This position is relative to the backglass window."}},
921 {"backglassRotation", {Settings::ReloadType::None, "Rotation of the Backglass media.\n"
922 "0 = no rotation\n"
923 "90, 180, 270, -90, etc"}},
924 {"dmdMediaWidth", {Settings::ReloadType::None, "Width of the DMD media in pixels."}},
925 {"dmdMediaHeight", {Settings::ReloadType::None, "Height of the DMD media in pixels.\n"
926 "- This should match your DMD window height."}},
927 {"dmdMediaX", {Settings::ReloadType::None, "X position of the DMD media.\n"
928 "- This position is relative to the DMD window."}},
929 {"dmdMediaY", {Settings::ReloadType::None, "Y position of the DMD media.\n"
930 "- This position is relative to the DMD window."}},
931 {"dmdRotation", {Settings::ReloadType::None, "Rotation of the DMD media.\n"
932 "0 = no rotation\n"
933 "90, 180, 270, -90, etc"}},
934 {"topperMediaWidth", {Settings::ReloadType::None, "Width of the Topper media in pixels."}},
935 {"topperMediaHeight", {Settings::ReloadType::None, "Height of the Topper media in pixels.\n"
936 "- This should match your Topper window height."}},
937 {"topperMediaX", {Settings::ReloadType::None, "X position of the Topper media.\n"
938 "- This position is relative to the Topper window."}},
939 {"topperMediaY", {Settings::ReloadType::None, "Y position of the Topper media.\n"
940 "- This position is relative to the Topper window."}},
941 {"topperRotation", {Settings::ReloadType::None, "Rotation of the Topper media.\n"
942 "0 = no rotation\n"
943 "90, 180, 270, -90, etc"}},
944 // AudioSettings
945 {"masterMute", {Settings::ReloadType::Audio, "Mute all audio"}},
946 {"masterVol", {Settings::ReloadType::Audio, "Adjust all volume."}},
947 {"mediaAudioMute", {Settings::ReloadType::Audio, "Mute playfield, backglass and DMD audio"}},
948 {"mediaAudioVol", {Settings::ReloadType::Audio, "Adjust playfield, backglass and DMD video volume."}},
949 {"tableMusicMute", {Settings::ReloadType::Audio, "Mute current table music."}},
950 {"tableMusicVol", {Settings::ReloadType::Audio, "Adjust current table music volume."}},
951 {"interfaceAudioMute", {Settings::ReloadType::Audio, "Mute interface sounds."}},
952 {"interfaceAudioVol", {Settings::ReloadType::Audio, "Adjust interface sounds volume."}},
953 {"interfaceAmbienceMute", {Settings::ReloadType::Audio, "Mute interface ambience."}},
954 {"interfaceAmbienceVol", {Settings::ReloadType::Audio, "Adjust interface ambience volume."}},
955 // UISounds
956 {"scrollNormalSound", {Settings::ReloadType::Tables, "Sound played when scrolling single tables."}},
957 {"scrollFastSound", {Settings::ReloadType::Tables, "Sound played when fast scrolling in 10's."}},
958 {"scrollJumpSound", {Settings::ReloadType::Tables, "Sound played when jumping by letter."}},
959 {"scrollRandomSound", {Settings::ReloadType::Tables, "Sound played when selecting a random table."}},
960 {"launchTableSound", {Settings::ReloadType::Tables, "Sound played when launching a table."}},
961 {"launchScreenshotSound", {Settings::ReloadType::Tables, "Sound played when entering screenshot mode."}},
962 {"panelToggleSound", {Settings::ReloadType::Tables, "Sound played when opening or closing panels."}},
963 {"screenshotTakeSound", {Settings::ReloadType::Tables, "Sound played when taking a screenshot."}},
964 {"ambienceSound", {Settings::ReloadType::Tables, "Sound played on the background if there is no table music."}},
965 // Internal
966 {"exeDir", {Settings::ReloadType::None, "Path to the executable directory."}},
967 {"vpxSubCmd", {Settings::ReloadType::None, "VPinballX internal command to play .vpx tables.\n"
968 "Use VPinballX --help command line menu to see more."}},
969 {"vpsDbPath", {Settings::ReloadType::None, "Path to the VPS database file, relative to exec dir."}},
970 {"vpsDbUpdateFrequency", {Settings::ReloadType::None, "Choose when to fetch for updates in VPS database.\n"
971 "- The only option for now is 'startup'."}},
972 {"vpsDbLastUpdated", {Settings::ReloadType::None, "Path to the VPS database update file, relative to exec dir."}},
973 {"vpxtoolIndex", {Settings::ReloadType::None, "Path to the vpxtool index file, relative to tables folder by default."}},
974 {"vpxtoolBin", {Settings::ReloadType::None, "Path to the vpxtool binary file if not in $PATH."}},
975 {"indexPath", {Settings::ReloadType::None, "Path to the main table index file, relative to exec dir."}},
976 {"screenshotWait", {Settings::ReloadType::None, "Time for the screenshot tool to wait until there are visible windows in VPX."}},
977 {"configUIWidth", {Settings::ReloadType::None, "Config window width."}},
978 {"configUIHeight", {Settings::ReloadType::None, "Config window height."}},
979 // Keybinds
980 {"Previous Table", {Settings::ReloadType::None, "Key to select the previous table in the list."}},
981 {"Next Table", {Settings::ReloadType::None, "Key to select the next table in the list."}},
982 {"Fast Previous Table", {Settings::ReloadType::None, "Key to quickly jump back 10 tables."}},
983 {"Fast Next Table", {Settings::ReloadType::None, "Key to quickly jump forward 10 tables."}},
984 {"Jump Next Letter", {Settings::ReloadType::None, "Key to jump to the next table starting with a different letter."}},
985 {"Jump Previous Letter", {Settings::ReloadType::None, "Key to jump to the previous table starting with a different letter."}},
986 {"Random Table", {Settings::ReloadType::None, "Key to jump to a random table."}},
987 {"Launch Table", {Settings::ReloadType::None, "Key to launch the selected table."}},
988 {"Toggle Config", {Settings::ReloadType::None, "Key to open or close the configuration menu."}},
989 {"Quit", {Settings::ReloadType::None, "Key to exit menus and application."}},
990 {"Screenshot Mode", {Settings::ReloadType::None, "Key to launch a table in screenshot mode."}},
991 {"Screenshot Key", {Settings::ReloadType::None, "Key to take a screenshot while in screenshot mode."}},
992 {"Screenshot Quit", {Settings::ReloadType::None, "Key to quit screenshot mode."}},
993 {"Toggle Editor", {Settings::ReloadType::None, "Key to open metadata editor."}},
994 {"Toggle Catalog", {Settings::ReloadType::None, "Key to open metadata catalog."}}
995};
996
997#endif // SETTINGS_H
Definition settings.h:12