File size: 86,291 Bytes
b6a38d7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
--- Defines the global namespace for options-related functions.
Options = {}

--- Defines the configuration data for options, including per-project options and video presets.
OptionsData = {
    --- The options configuration per-project.
    Options = {}
}

--- Stores the video presets data.
OptionsData.VideoPresetsData = {}

--- Initializes options-related data structures on first load.
if FirstLoad then
    --- Stores the preset video options.
    PresetVideoOptions = {}

    --- Namespace for engine option fixups.
    EngineOptionFixups = {}

    --- Namespace for account option fixups.
    AccountOptionFixups = {}
end


---
--- Returns a list of available graphics APIs.
---
--- If `config.OfficialPCGraphicsApis` is set and the current platform is PC, and not in developer mode, and not inside HG, the returned list will be filtered to only include the official PC graphics APIs.
---
--- @return table The list of available graphics APIs.
---
local function GetAvailableGraphicsApis()
	local available = GetSupportedGraphicsApis()
	if config.OfficialPCGraphicsApis and Platform.pc and not Platform.developer and not insideHG() then
		available = table.intersection(available, config.OfficialPCGraphicsApis)
	end
	return available
end

---
--- Returns the valid video mode for the given display index and resolution.
---
--- If valid video modes are available for the given display index and resolution, the function will return the width and height of the best mode (sorted by height, then width).
--- If no valid video modes are available, the function will return the current display mode's width and height.
---
--- @param displayIndex The index of the display to get the video modes for.
--- @param width The desired width of the video mode.
--- @param height The desired height of the video mode.
--- @return number The valid width of the video mode.
--- @return number The valid height of the video mode.
---
function GetValidVideoMode(displayIndex, width, height)
	local modes = GetVideoModes(displayIndex, width, height)
	if #modes > 0 then
		table.sort(modes, function(a, b)
			if a.Height ~= b.Height then
				return a.Height < b.Height
			end
			return a.Width < b.Width
		end)
		local best = modes[1]
		return best.Width, best.Height
	end
	local current = GetDisplayMode(displayIndex)
	return current.displayWidth, current.displayHeight
end

---
--- Initializes the engine options and sets up the graphics API and adapter.
---
--- This function is called during startup to initialize the engine options. It performs the following tasks:
---
--- 1. Sets the display index if it hasn't been set already.
--- 2. Checks if the requested graphics API is supported, and if not, uses the default graphics API.
--- 3. Selects the appropriate graphics adapter based on the chosen graphics API.
--- 4. Autodetects the engine display options if the graphics adapter has changed.
--- 5. Applies any developer-specified display options (fullscreen mode, resolution, etc.).
--- 6. Updates the config with the final display options.
---
--- @return nil
---
function Options.Startup()
	if config.DisableOptions then return end

	local options = EngineOptions
	if not options.DisplayIndex then
		options.DisplayIndex = config.DisplayIndex
	end

	if config.GraphicsApi ~= "" and not table.find(GetSupportedGraphicsApis(), config.GraphicsApi) then
		config.GraphicsApi = "" -- Requested API is not supported, use options' value or default.
	end
	if config.GraphicsApi == "" then -- Do not override, if manually picked.
		local availableGraphicsApis = GetAvailableGraphicsApis()
		config.GraphicsApi = type(options.GraphicsApi) == "string" and options.GraphicsApi or ""
		if not config.GraphicsApi or not table.find(availableGraphicsApis, config.GraphicsApi) then
			config.GraphicsApi = GetDefaultGraphicsApi()
		end
		if not table.find(availableGraphicsApis, config.GraphicsApi) and #availableGraphicsApis > 0 then
			config.GraphicsApi = availableGraphicsApis[1]
		end
	end
	options.GraphicsApi = config.GraphicsApi
	 
	local prevAdapter = options.GraphicsAdapter or { vendorId = 0, deviceId = 0 }
	config.GraphicsAdapterIndex = GetRenderDeviceAdapterIndex(config.GraphicsApi, prevAdapter)
	options.GraphicsAdapter = GetRenderDeviceAdapterData(config.GraphicsApi, config.GraphicsAdapterIndex)
	options.GraphicsAdapterIndex = config.GraphicsAdapterIndex
	if not options.GraphicsAdapter or prevAdapter.vendorId ~= options.GraphicsAdapter.vendorId or prevAdapter.deviceId ~= options.GraphicsAdapter.deviceId then
		Options.Autodetect(options)		
		if Platform.developer and config.Width ~= 0 then
			-- Set engine display options using the values in the config
			options.FullscreenMode = config.FullscreenMode
			options.DisplayIndex = config.DisplayIndex
			
			-- Don't override Resolution if it was already set in DefaultEngineOptions
			if not IsPoint(options.Resolution) then
				options.Resolution = point(config.Width, config.Height)
			end
		end
		SaveEngineOptions()
	end
	
	if options.GraphicsAdapter then
		local gpu = string.lower(options.GraphicsAdapter.name)
		if Platform.pc and (options.GraphicsAdapter.vendorId == const.VendorIds.AMD or string.find(gpu, "amd") or string.find(gpu, "radeon")) then
			hr.D3D12PresentWaitOnAcquire = 1
			hr.SwapchainBuffers = 3
			hr.SSRFullTile8x8 = 1
		end
	end

	-- Update the config display options using the engine options values
	config.DisplayIndex = options.DisplayIndex
	config.FullscreenMode = options.FullscreenMode or 0
	
	if IsPoint(options.Resolution) then
		if options.FullscreenMode and not Platform.console then
			options.Resolution = point(GetValidVideoMode(options.DisplayIndex, options.Resolution:xy()))
		end
		config.Width, config.Height = options.Resolution:xy()
	elseif not config.Width or not config.Height then
		config.Width, config.Height = 0, 0
	end
	
	if options.Vsync ~= nil then
		config.Vsync = options.Vsync
	else
		config.Vsync = true
	end
end

--[[
Option Fixups 

Option fixups can be used to force change option values for all users. They work similarly to 
savegame fixups but hey are separated into two namespaces: EngineOptionFixups and 
AccountOptionFixups. As the names suggest one is for EngineOptions options and the other is 
for AccountOptions.

The fixup function will receive the corresponding table with options as the first parameter.
This is EngineOptions for engine option fixups or AccountStorage.Options for account option 
fixups. The second parameter is the last lua revision where a fixup was applied.

Fixup examples:

function EngineOptionFixups.SMAAAntialiasing(engine_options, last_applied_fixup_revision)
	engine_options.Antialiasing = "SMAA"
end

function AccountOptionFixups.Autosave(account_options, last_applied_fixup_revision)
	account_options.Autosave = true
end
--]]

-- Engine option fixups
-- Executed after the first call to Options.Startup() and before InitRenderEngine()
---
--- Applies any pending engine option fixups to the EngineOptions table.
---
--- Engine option fixups can be used to force changes to engine option values for all users.
--- They work similarly to savegame fixups, but are separated into two namespaces:
--- EngineOptionFixups and AccountOptionFixups.
---
--- This function iterates through the EngineOptionFixups table, calling any functions
--- that have not yet been applied. It then saves the updated EngineOptions and prints
--- a debug message indicating which fixups were applied.
---
--- @return number The number of fixups that were applied.
function Options.FixupEngineOptions()
	EngineOptions.fixups_meta = EngineOptions.fixups_meta or GetDefaultOptionFixupMeta()
	local meta = EngineOptions.fixups_meta
	meta.AppliedOptionFixups = meta.AppliedOptionFixups or {}
	local count, applied = 0, {}

	for fixup, func in sorted_pairs(EngineOptionFixups) do
		if not meta.AppliedOptionFixups[fixup] and type(func) == "function" then
			procall(func, EngineOptions, meta.last_applied_fixup_revision)
			count = count + 1
			applied[#applied + 1] = fixup
			meta.AppliedOptionFixups[fixup] = true
			meta.last_applied_fixup_revision = LuaRevision
		end
	end
	
	if count > 0 then
		SaveEngineOptions()
		DebugPrint(string.format("Applied %d engine option fixup(s): %s\n", count, table.concat(applied, ", ")))
	end
	
	return count
end

-- Account option fixups
-- Executed after a new AccountStorage is loaded
---
--- Applies any pending account option fixups to the AccountStorage.Options table.
---
--- Account option fixups can be used to force changes to account option values for all users.
--- They work similarly to savegame fixups, but are separated into two namespaces:
--- EngineOptionFixups and AccountOptionFixups.
---
--- This function iterates through the AccountOptionFixups table, calling any functions
--- that have not yet been applied. It then saves the updated AccountStorage.Options and prints
--- a debug message indicating which fixups were applied.
---
--- @return number The number of fixups that were applied.
function Options.FixupAccountOptions()
	AccountStorage.Options = AccountStorage.Options or {}
	local opt = AccountStorage.Options
	opt.fixups_meta = opt.fixups_meta or GetDefaultOptionFixupMeta()
	local meta = opt.fixups_meta
	meta.AppliedOptionFixups = meta.AppliedOptionFixups or {}
	local count, applied = 0, {}
	
	for fixup, func in sorted_pairs(AccountOptionFixups) do
		if not meta.AppliedOptionFixups[fixup] and type(func) == "function" then
			procall(func, AccountStorage.Options, meta.last_applied_fixup_revision)
			count = count + 1
			applied[#applied + 1] = fixup
			meta.AppliedOptionFixups[fixup] = true
			meta.last_applied_fixup_revision = LuaRevision
		end
	end
	
	if count > 0 then
		SaveAccountStorage()
		DebugPrint(string.format("Applied %d account option fixup(s): %s\n", count, table.concat(applied, ", ")))
	end
	
	return count
end

local option_groups = config.SoundOptionGroups or {}
config.SoundOptionGroups = option_groups
local option_sound_groups = {}
for option, groups in pairs(option_groups) do
	for _, group in ipairs(groups) do
		option_sound_groups[group] = true
	end
end
if config.SoundGroups then
	for _, group in ipairs(config.SoundGroups) do
		if not option_sound_groups[group] then
			option_groups[group] = option_groups[group] or {group}
		end
	end
else
	config.SoundGroups = table.keys(option_sound_groups, true)
end

if not config.DisableOptions then

---
--- Handles the autorun logic for options, including:
--- - Configuring the TAA option based on the graphics adapter and available temporal AA techniques
--- - Overriding option values from the `ProjectOptions` table
--- - Sorting the options in the `OptionsData.Options` table
--- - Applying the video preset and saving the `EngineOptions`
--- - Reloading account options when Lua is reloaded
---
--- @return nil
function OnMsg.Autorun()
	local taa_option = table.find_value(OptionsData.Options.Antialiasing, "value", "TAA")
	if Platform.pc then
		local graphics_adapter = GetRenderDeviceAdapterData(config.GraphicsApi, config.GraphicsAdapterIndex)
		local gpu_name = string.lower(graphics_adapter.name)
		local is_intel =
			graphics_adapter.vendorId == const.VendorIds.Intel or
			string.find(gpu_name, "intel") or
			string.find(gpu_name, "arc")

		local dlss = hr.TemporalIsTypeSupported("dlss")
		local fsr2 = hr.TemporalIsTypeSupported("fsr2")
		local xess = hr.TemporalIsTypeSupported("xess")

		if dlss then
			taa_option.hr = table.find_value(OptionsData.Options.Antialiasing, "value", "DLSS").hr
		elseif (is_intel or not fsr2) and xess then
			taa_option.hr = table.find_value(OptionsData.Options.Antialiasing, "value", "XESS").hr
		elseif fsr2 then
			taa_option.hr = table.find_value(OptionsData.Options.Antialiasing, "value", "FSR2").hr
		end
	end

	if taa_option.hr == empty_table then
		 -- if for some reason TAA is selected while not_selectable, naming the option "Off" will inform the user of the current situation.
		taa_option.text = T(392695272733, --[[options:Antialiasing is turned off]] "Off")
	end

	local OverrideOptionValues = function(baseValues, overrideValues, skipKeys)
		for overrideKey,overrideValue in pairs(overrideValues) do
			if not table.find(skipKeys, overrideKey) then
				if type(overrideValue) == "table" then
					for key, value in pairs(overrideValue) do
						baseValues[overrideKey][key] = value
					end
				else
					baseValues[overrideKey] = overrideValue
				end
			end
		end
	end

	for option,projectOptionValues in pairs(rawget(_G, "ProjectOptions") or empty_table) do
		if OptionsData.Options[option] then
			for _,projectOptionValue in ipairs(projectOptionValues) do
				local options = OptionsData.Options[option]
				local baseOptionValue = table.find_value(options, "value", projectOptionValue.value)
				if baseOptionValue then
					OverrideOptionValues(baseOptionValue, projectOptionValue, { "value", "text" })
				else
					options[#options + 1] = projectOptionValue
				end
			end
		else
			OptionsData.Options[option] = projectOptionValues
		end
	end

	for option,optionValues in pairs(OptionsData.Options) do
		table.stable_sort(optionValues, function(a, b)
			if a.SortKey and b.SortKey then
				return a.SortKey < b.SortKey
			else
				return a.SortKey
			end
		end)
	end

	if EngineOptions then
		-- Apply video options and save EngineOptions
		local preset = EngineOptions.VideoPreset
		ApplyVideoPreset(preset)
		SaveEngineOptions()
		
		-- Reload account options when reloading Lua
		ApplyAccountOptions()
	end
	Options.InitGraphicsApiCombo()
end

--reload account options when we get new account storage, something that happens on the xbox
---
--- Handles changes to the account storage, reloading shortcuts and applying the language option.
---
--- This function is called when the account storage changes, such as when the user logs in or out.
--- It first checks if there are any shortcuts in the account storage, and if so, calls `ReloadShortcuts` after a short delay.
--- It then calls `ApplyLanguageOption` after a short delay to apply any changes to the language settings.
---
--- @function OnMsg.AccountStorageChanged
--- @return nil
function OnMsg.AccountStorageChanged()
	if AccountStorage and next(AccountStorage.Shortcuts) then
		DelayedCall(0, ReloadShortcuts)
	end
	DelayedCall(0, ApplyLanguageOption)
end

---
--- Handles changes to the local storage, reapplying engine options and updating the system size.
---
--- This function is called when the local storage changes, such as when the user logs in or out.
--- It first calls `Options.ApplyEngineOptions` to reapply any changes to the engine options.
--- It then calls `terminal.desktop:OnSystemSize` to update the system size, which may be necessary if the display settings have changed.
---
--- @function OnMsg.LocalStorageChanged
--- @return nil
function OnMsg.LocalStorageChanged()
	Options.ApplyEngineOptions(EngineOptions)
	terminal.desktop:OnSystemSize(UIL.GetScreenSize())
end

---
--- Handles changes to the system size, updating the engine options and UI.
---
--- This function is called when the system size changes, such as when the window is resized or the display mode changes.
--- It updates the `EngineOptions.Resolution` and `EngineOptions.DisplayIndex` properties to reflect the new system size, and then calls `Options.UpdateVideoModesCombo` to update the video mode options UI.
--- If an `OptionsObj` is available, it also updates the `Resolution` property of that object and marks it as modified.
---
--- @function OnMsg.SystemSize
--- @param pt point The new system size
--- @return nil
function OnMsg.SystemSize(pt)
	EngineOptions.Resolution = pt
	EngineOptions.DisplayIndex = GetMainWindowDisplayIndex()
	Options.UpdateVideoModesCombo()
	if OptionsObj then
		OptionsObj:SetProperty("Resolution", pt)
		ObjModified(OptionsObj)
	end
end

if Platform.desktop then

---
--- Handles the application quit event by saving the current display index if it has changed.
---
--- This function is called when the application is about to quit. It checks if the current display index
--- is different from the one stored in `EngineOptions.DisplayIndex`. If so, it updates the
--- `EngineOptions.DisplayIndex` property and saves the engine options.
---
--- @function OnMsg.ApplicationQuit
--- @return nil
function OnMsg.ApplicationQuit()
	local display_index =  GetMainWindowDisplayIndex()
	if EngineOptions.DisplayIndex ~= display_index then
		EngineOptions.DisplayIndex = display_index
		SaveEngineOptions()
	end
end

end -- if Platform.pc

end -- if not config.DisableOptions then

---
--- Picks the appropriate video preset based on the given GPU name.
---
--- This function takes a table of preset regexes and a GPU name, and returns the video preset that matches the GPU name. If no match is found, it tries to determine the preset based on the platform (Xbox One, PS4, etc.).
---
--- @param preset_regexes table A table of preset regexes, where each entry is a table with two elements: a list of regexes and the corresponding preset name.
--- @param gpu string The name of the GPU.
--- @return string The video preset that matches the GPU name, or a default preset if no match is found.
---
function Options.PickVideoPreset(preset_regexes, gpu)
	for _, v in ipairs(preset_regexes) do
		for _, re in ipairs(v[1]) do
			if gpu:match(re) then
				return v.preset
			end
		end
	end
	
	
	if Platform.xbox_one then
		preset = Platform.xbox_one_x and "xbox_one_x" or "xbox_one"
	elseif Platform.xbox_series then
		preset = Platform.xbox_series_x and "xbox_series_x" or "xbox_series_s"
	elseif Platform.ps4 then
		preset = Platform.xbox_ps4_pro and "ps4_pro" or "ps4"
	elseif Platform.ps5 then
		preset = "ps5"
	end
	if preset then
		return DefaultEngineOptions[preset].VideoPreset
	end
	
	return "High"
end

-- this uses data coming from config table, as ProjectOptions and its data isn't yet loaded
---
--- Automatically detects and sets the appropriate video options based on the user's hardware.
---
--- This function checks the user's display resolution and graphics adapter, and sets the appropriate video preset and texture quality based on the detected hardware.
---
--- @param options table The table of engine options to be updated.
--- @return nil
---
function Options.Autodetect(options)
	if Platform.pc or Platform.steamdeck then
		if not IsPoint(options.Resolution) then
			local currentMode = GetDisplayMode(options.DisplayIndex)
			options.Resolution = point(currentMode.displayWidth, currentMode.displayHeight)
		end
	end

	if not options.GraphicsAdapter then
		options.VideoPreset = "Low"
		return
	end
	
	options.VideoPreset = Options.PickVideoPreset(config.VideoPresetAutodetect or empty_table, string.lower(options.GraphicsAdapter.name))
	
	options.Textures = "High"
	for _,v in ipairs(config.TextureMemoryThresholds or empty_table) do
		if options.GraphicsAdapter.videoRam < v.threshold * 1024 * 1024 then
			options.Textures = v.value
			break
		end
	end
end

---
--- Applies the engine options to the local options.
---
--- This function takes the local options and applies them to the engine options defaults. It also handles any HR (high resolution) overrides that may be defined in the options data.
---
--- @param local_options table The local options to be applied.
--- @return nil
---
function Options.ApplyEngineOptions(local_options)
	local engine_options_defaults = GetTableWithStorageDefaults("local")
	local options_data = OptionsData.Options
	local hr_override

	for k,v in pairs(engine_options_defaults) do
		-- Keys from the defaults
		-- Values from the local EngineOptions
		v = local_options[k]

		local value = table.find_value(options_data[k], "value", v)
		local hr = options_data[k] and options_data[k].hr or value and value.hr
		for hrk, hrv in pairs(hr or empty_table) do
			if type(hrv) == "function" then
				hrv = hrv(v, hrk)
			end
			if hrv ~= nil then
				hr_override = hr_override or {}
				if hr_override[hrk] then
					printf("once", "OptionsData.Options.%s sets hr.%s which was already set by another table", k, hrk)
				end
				hr_override[hrk] = hrv
			end
		end
	end

	if hr_override then
		hr.TR_ReloadSuspended = hr.TR_ReloadSuspended | 1
		table.change_base(hr, hr_override)
		hr.TR_ReloadSuspended = hr.TR_ReloadSuspended & ~1
	end
	
	ApplySoundOptions(local_options)
end

---
--- Sets the volume for a specific sound option.
---
--- @param option string The name of the sound option to set the volume for.
--- @param volume number The volume to set, between 0 and 1000.
---
function SetOptionVolume(option, volume)
	for _, group in ipairs(config.SoundOptionGroups[option]) do
		SetOptionsGroupVolume(group, volume)
	end
end

---
--- Applies the sound options specified in the given local_options table.
---
--- @param local_options table The local options to be applied.
---
function ApplySoundOptions(local_options)
	local master_volume = local_options.MasterVolume or 1000
	for option in pairs(config.SoundOptionGroups) do
		SetOptionVolume(option, master_volume * (local_options[option] or 1000)/ 1000)
	end
	config.DontMuteWhenInactive = local_options.MuteWhenMinimized ~= nil and not local_options.MuteWhenMinimized or false
end

---
--- Initializes the video modes combo box in the options menu.
---
--- This function populates the `OptionsData.Options.Resolution` table with the available video modes,
--- sorted by resolution. It also adds a custom resolution option if the current resolution is not
--- in the list of available modes.
---
--- @function Options.InitVideoModesCombo
--- @return nil
function Options.InitVideoModesCombo()
	local modes = {}
	for i, mode in ipairs(GetVideoModes(EngineOptions.DisplayIndex, 1024, 720)) do
		local resolution = point(mode.Width, mode.Height)
		local key = tostring(resolution)
		modes[key] = resolution
	end
	if Platform.developer then
		local ultrawide = point(120*21, 120*9)
		modes[tostring(ultrawide)] = ultrawide
	end
	local sorted_modes = table.values(modes)
	table.sort(sorted_modes, function(a, b) return a:x() * a:y() < b:x() * b:y() end)
	OptionsData.Options.Resolution = {}
	for i, v in ipairs(sorted_modes) do
		OptionsData.Options.Resolution[i] = { value = v, text = T{664014484626, "<FormatResolution(pt)>", pt = v}}
	end
	--add custom option if needed
	Options.UpdateVideoModesCombo()
end

---
--- Initializes the graphics API combo box in the options menu.
---
--- This function populates the `OptionsData.Options.GraphicsApi` table with the available graphics APIs,
--- including DirectX 11 (deprecated) and DirectX 12. If only one graphics API is available, the option
--- is marked as not editable and not saved.
---
--- @function Options.InitGraphicsApiCombo
--- @return nil
function Options.InitGraphicsApiCombo()
	local available = GetAvailableGraphicsApis()	
	OptionsData.Options.GraphicsApi = {
		{ value = "d3d11", text = Untranslated("DirectX 11 (deprecated)"), not_selectable = not table.find(available, "d3d11") },
		{ value = "d3d12", text = Untranslated("DirectX 12"), not_selectable = not table.find(available, "d3d12") },
	}

	if table.count(OptionsData.Options.GraphicsApi, "not_selectable", false) <= 1 then
		local graphicsApiOption = table.find_value(OptionsObject.properties, "id", "GraphicsApi")
		graphicsApiOption.dont_save = true
		graphicsApiOption.no_edit = true
	end
end

--- Initializes the graphics adapter combo box in the options menu.
---
--- This function populates the `OptionsData.Options.GraphicsAdapterIndex` table with the available graphics adapters,
--- including their index and name. The graphics adapter index is used to select the appropriate adapter when
--- initializing the graphics API.
---
--- @function Options.InitGraphicsAdapterCombo
--- @param graphicsApi string The graphics API to use for enumerating the available adapters.
--- @return nil
function Options.InitGraphicsAdapterCombo(graphicsApi)
	local adapters = {}
	for i = 0,GetNumRenderDeviceAdapters(graphicsApi)-1 do
		local adapterData = GetRenderDeviceAdapterData(graphicsApi, i)
		adapters[i+1] = { value = i, text = Untranslated(adapterData.name) }
	end
	OptionsData.Options.GraphicsAdapterIndex = adapters
end

---
--- Updates the video modes combo box in the options menu.
---
--- This function checks the current resolution and ensures it is present in the `OptionsData.Options.Resolution` table. If the current resolution is not found, it is added to the table in the correct sorted position.
---
--- If a custom resolution entry already exists in the table, it is removed before adding the new entry.
---
--- @function Options.UpdateVideoModesCombo
--- @return nil
function Options.UpdateVideoModesCombo()
	local v = point(GetResolution())
	--remove any custom items
	local custom_idx = table.find(OptionsData.Options.Resolution, "custom", true)
	local idx = table.find(OptionsData.Options.Resolution, "value", v)
	if custom_idx and custom_idx ~= idx then
		table.remove(OptionsData.Options.Resolution, custom_idx)
	end
	if not idx then
		--insert current value in correct position
		local entry = { value = v, text = T{664014484626, "<FormatResolution(pt)>", pt = v}, custom = true}
		table.insert_sorted(OptionsData.Options.Resolution, entry, "value")
	end
end

---
--- Defines the available video presets for the game options.
---
--- The `OptionsData.Options.VideoPreset` table contains a list of video preset options that can be selected in the game's options menu. Each preset has a `value` field that represents the internal identifier for the preset, a `text` field that contains the display name for the preset, and a `not_selectable` field that determines whether the preset should be hidden from the user interface based on the current platform.
---
--- The available presets include:
--- - `Low`: A low-quality video preset.
--- - `Medium`: A medium-quality video preset.
--- - `High`: A high-quality video preset.
--- - `Ultra`: An ultra-high-quality video preset.
--- - `XboxOne`: A preset for the Xbox One console.
--- - `XboxOneX`: A preset for the Xbox One X console.
--- - `XboxSeriesS`: A preset for the Xbox Series S console.
--- - `XboxSeriesXQuality`: A quality-focused preset for the Xbox Series X console.
--- - `XboxSeriesXPerformance`: A performance-focused preset for the Xbox Series X console.
--- - `PS4`: A preset for the PlayStation 4 console.
--- - `PS4Pro`: A preset for the PlayStation 4 Pro console.
--- - `PS5Quality`: A quality-focused preset for the PlayStation 5 console.
--- - `PS5Performance`: A performance-focused preset for the PlayStation 5 console.
--- - `Switch`: A preset for the Nintendo Switch console.
--- - `SteamDeck`: A preset for the Steam Deck handheld console.
--- - `Custom`: A custom video preset that allows the user to configure the settings manually.
---
OptionsData.Options.VideoPreset = {
	{ value = "Low", text = T(644, "Low"), not_selectable = Platform.console },
	{ value = "Medium", text = T(645, "Medium"), not_selectable = Platform.console },
	{ value = "High", text = T(7375, "High"), not_selectable = Platform.console },
	{ value = "Ultra", text = T(3551, "Ultra"), not_selectable = Platform.console },
	{ value = "XboxOne", text = Untranslated("*XboxOne"), not_selectable = not Platform.developer },
	{ value = "XboxOneX", text = Untranslated("*XboxOneX"), not_selectable = not (Platform.xbox_one_x or Platform.developer) },
	{ value = "XboxSeriesS", text = Untranslated("*XboxSeriesS"), not_selectable = not (Platform.xbox_series_s or Platform.developer) },
	{ value = "XboxSeriesXQuality", text = Platform.developer and Untranslated("*XboxSeriesXQuality") or T(709029849246, "Quality"), not_selectable = not (Platform.xbox_series_x or Platform.developer) },
	{ value = "XboxSeriesXPerformance", text = Platform.developer and Untranslated("*XboxSeriesXPerformance") or T(731233321844, "Performance"), not_selectable = not (Platform.xbox_series_x or Platform.developer) },
	{ value = "PS4", text = Untranslated("*PS4"), not_selectable = not Platform.developer },
	{ value = "PS4Pro", text = Untranslated("*PS4Pro"), not_selectable = not Platform.developer },
	{ value = "PS5Quality", text = Platform.developer and Untranslated("*PS5Quality") or T(709029849246, "Quality"), not_selectable = not (Platform.ps5 or Platform.developer) },
	{ value = "PS5Performance", text = Platform.developer and Untranslated("*PS5Performance") or T(731233321844, "Performance"), not_selectable = not (Platform.ps5 or Platform.developer) },
	{ value = "Switch", text = Untranslated("*Switch"), not_selectable = not Platform.developer },
	{ value = "SteamDeck", text = Untranslated("Steam Deck"), not_selectable = not (Platform.steamdeck or Platform.developer) },
	{ value = "Custom", text = T(6843, "*Custom"), not_selectable = Platform.console },
}

---
--- Defines the available fullscreen mode options for the game.
---
--- The `OptionsData.Options.FullscreenMode` table contains a list of fullscreen mode options that can be selected in the game's options menu. Each option has a `value` field that represents the internal identifier for the mode, and a `text` field that contains the display name for the mode.
---
--- The available modes include:
--- - `0`: Windowed mode
--- - `1`: Fullscreen mode
---
OptionsData.Options.FullscreenMode = {
	{ value = 0, text = T(443238066363, --[[Options dialog fullscreen mode]] "Windowed"), },
	{ value = 1, text = T(873558273070, --[[Options dialog fullscreen mode]] "Fullscreen"), },
}

---
--- Defines the available maximum FPS options for the game.
---
--- The `OptionsData.Options.MaxFps` table contains a list of maximum FPS options that can be selected in the game's options menu. Each option has a `value` field that represents the internal identifier for the FPS limit, and a `text` field that contains the display name for the FPS limit.
---
--- The available FPS limits include:
--- - `30`: Limits the game to 30 FPS.
--- - `60`: Limits the game to 60 FPS.
--- - `120`: Limits the game to 120 FPS.
--- - `144`: Limits the game to 144 FPS.
--- - `240`: Limits the game to 240 FPS.
--- - `Unlimited`: Removes the FPS limit, allowing the game to run at the maximum possible frame rate.
---
OptionsData.Options.MaxFps = {
	{ value = "30", text = Untranslated("30 ") .. T(206424973826, --[[options: frame limit]] "FPS"), hr = { MaxFps = 30 } },
	{ value = "60", text = Untranslated("60 ") .. T(206424973826, --[[options: frame limit]] "FPS"), hr = { MaxFps = 60 } },
	{ value = "120", text = Untranslated("120 ") .. T(206424973826, --[[options: frame limit]] "FPS"), hr = { MaxFps = 120 } },
	{ value = "144", text = Untranslated("144 ") .. T(206424973826, --[[options: frame limit]] "FPS"), hr = { MaxFps = 144 } },
	{ value = "240", text = Untranslated("240 ") .. T(206424973826, --[[options: frame limit]] "FPS"), hr = { MaxFps = 240 } },
	{ value = "Unlimited", text = T(715166204973, --[[options: frame limit]] "Unlimited"), hr = { MaxFps = 0 } },
}
---
--- Defines the available options for Screen Space Ambient Occlusion (SSAO) in the game.
---
--- The `OptionsData.Options.SSAO` table contains a list of SSAO options that can be selected in the game's options menu. Each option has a `value` field that represents the internal identifier for the SSAO mode, and a `text` field that contains the display name for the SSAO mode.
---
--- The available SSAO modes include:
--- - `Off`: Disables SSAO.
--- - `On`: Enables SSAO.
---
--- The `hr` field for each option contains a table of settings that will be applied when the corresponding option is selected. In this case, the `EnableScreenSpaceAmbientObscurance` setting is used to control whether SSAO is enabled or disabled.
---
OptionsData.Options.SSAO = {
	{ value = "Off", text = T(549548241533, "Off"), hr = { EnableScreenSpaceAmbientObscurance = 0 } },
	{ value = "On", text = T(336462699824, "On"), hr = { EnableScreenSpaceAmbientObscurance = 1 } },}

---
--- Defines the available options for Screen Space Reflections (SSR) in the game.
---
--- The `OptionsData.Options.SSR` table contains a list of SSR options that can be selected in the game's options menu. Each option has a `value` field that represents the internal identifier for the SSR mode, and a `text` field that contains the display name for the SSR mode.
---
--- The available SSR modes include:
--- - `Off`: Disables SSR.
--- - `Low`: Enables SSR with low quality settings.
--- - `Medium`: Enables SSR with medium quality settings.
--- - `High`: Enables SSR with high quality settings.
--- - `Ultra`: Enables SSR with ultra quality settings.
---
--- The `hr` field for each option contains a table of settings that will be applied when the corresponding option is selected. These settings control various parameters of the SSR implementation, such as the downsampling coefficient, the number of pixels to skip, the number of pixels to pass behind, and the threshold for parent distance.
---
OptionsData.Options.SSR = {
	{ value = "Off", text = T(347421390938, --[[options:SSR is off]] "Off"), hr = { EnableScreenSpaceReflections = 0 } },
	{ value = "Low", text = T(967597583816, --[[options:SSR is turned to low]] "Low"), hr = { EnableScreenSpaceReflections = 1, SSRDownsampleCoef = 4, SSRSkipPixels = 2, SSRPassBehindPixels = -32, SSRThresholdParentDistance = 0 } },
	{ value = "Medium", text = T(711881918198, --[[options:SSR is turned to Medium]] "Medium"), hr = { EnableScreenSpaceReflections = 1, SSRDownsampleCoef = 2, SSRSkipPixels = 1, SSRPassBehindPixels = -96, SSRThresholdParentDistance = config.SSRThresholdParentDistance } },
	{ value = "High", text = T(350030693801, --[[options:SSR is turned to high]] "High"), hr = { EnableScreenSpaceReflections = 1, SSRDownsampleCoef = 1, SSRPassBehindPixels = -192, SSRThresholdParentDistance = config.SSRThresholdParentDistance } },
	{ value = "Ultra", text = T(363062651990, --[[options:SSR is turned to Ultra]] "Ultra"), hr = { EnableScreenSpaceReflections = 1, SSRDownsampleCoef = 1, SSRPassBehindPixels = -192, SSRThresholdParentDistance = 0 } },
}

---
--- Defines the available options for Bloom post-processing effect in the game.
---
--- The `OptionsData.Options.Bloom` table contains a list of Bloom options that can be selected in the game's options menu. Each option has a `value` field that represents the internal identifier for the Bloom mode, and a `text` field that contains the display name for the Bloom mode.
---
--- The available Bloom modes include:
--- - `Off`: Disables the Bloom effect.
--- - `On`: Enables the Bloom effect.
---
--- The `hr` field for each option contains a table of settings that will be applied when the corresponding option is selected. In this case, the `EnablePostProcBloom` setting is used to control whether the Bloom effect is enabled or disabled.
---
OptionsData.Options.Bloom = {
	{ value = "Off", text = T(897923163870, --[[options:Bloom is off]] "Off"), hr = { EnablePostProcBloom = 0 } },
	{ value = "On", text = T(962119091084, --[[options:Bloom is on]] "On"), hr = { EnablePostProcBloom = 1 } },
}

---
--- Defines the available options for Eye Adaptation post-processing effect in the game.
---
--- The `OptionsData.Options.EyeAdaptation` table contains a list of Eye Adaptation options that can be selected in the game's options menu. Each option has a `value` field that represents the internal identifier for the Eye Adaptation mode, and a `text` field that contains the display name for the Eye Adaptation mode.
---
--- The available Eye Adaptation modes include:
--- - `Off`: Disables the Eye Adaptation effect.
--- - `On`: Enables the Eye Adaptation effect.
---
--- The `hr` field for each option contains a table of settings that will be applied when the corresponding option is selected. In this case, the `AutoExposureMode` setting is used to control whether the Eye Adaptation effect is enabled or disabled.
---
OptionsData.Options.EyeAdaptation = {
	{ value = "Off", text = T(764902318645, --[[options:Eye Adaptation is off]] "Off"), hr = { AutoExposureMode = 0 } },
	{ value = "On", text = T(310678453010, --[[options:Eye Adaptation is on]] "On"), hr = { AutoExposureMode = 1 } },
}

---
--- Defines the available options for the Vignette post-processing effect in the game.
---
--- The `OptionsData.Options.Vignette` table contains a list of Vignette options that can be selected in the game's options menu. Each option has a `value` field that represents the internal identifier for the Vignette mode, and a `text` field that contains the display name for the Vignette mode.
---
--- The available Vignette modes include:
--- - `Off`: Disables the Vignette effect.
--- - `On`: Enables the Vignette effect.
---
--- The `hr` field for each option contains a table of settings that will be applied when the corresponding option is selected. In this case, the `EnablePostProcVignette` setting is used to control whether the Vignette effect is enabled or disabled.
OptionsData.Options.Vignette = {
	{ value = "Off", text = T(173515508906, --[[options:Vignette is off]] "Off"), hr = { EnablePostProcVignette = 0 } },
	{ value = "On", text = T(724497759523, --[[options:Vignette is on]] "On"), hr = { EnablePostProcVignette = 1} },
}

---
--- Defines the available options for the Chromatic Aberration post-processing effect in the game.
---
--- The `OptionsData.Options.ChromaticAberration` table contains a list of Chromatic Aberration options that can be selected in the game's options menu. Each option has a `value` field that represents the internal identifier for the Chromatic Aberration mode, and a `text` field that contains the display name for the Chromatic Aberration mode.
---
--- The available Chromatic Aberration modes include:
--- - `Off`: Disables the Chromatic Aberration effect.
--- - `On`: Enables the Chromatic Aberration effect.
---
--- The `hr` field for each option contains a table of settings that will be applied when the corresponding option is selected. In this case, the `PostProcChromaticAberration` setting is used to control whether the Chromatic Aberration effect is enabled or disabled.
OptionsData.Options.ChromaticAberration = {
	{ value = "Off", text = T(535014732326, --[[options:Chromatic Aberration is off]] "Off"), hr = { PostProcChromaticAberration = 0 } },
	{ value = "On", text = T(945084644408, --[[options:Chromatic Aberration is on]] "On"), hr = { PostProcChromaticAberration = 100 } },
}

---
--- Defines the available options for the FPS counter in the game.
---
--- The `OptionsData.Options.FPSCounter` table contains a list of FPS counter options that can be selected in the game's options menu. Each option has a `value` field that represents the internal identifier for the FPS counter mode, and a `text` field that contains the display name for the FPS counter mode.
---
--- The available FPS counter modes include:
--- - `Off`: Disables the FPS counter.
--- - `Fps`: Displays the current frames per second.
--- - `Ms`: Displays the current frame time in milliseconds.
---
--- The `hr` field for each option contains a table of settings that will be applied when the corresponding option is selected. In this case, the `FpsCounter` setting is used to control the FPS counter mode.
OptionsData.Options.FPSCounter = {
	{ value = "Off", text = T(290121664929, --[[options:FPS counter is off]] "Off"), hr = { FpsCounter = 0 } },
	{ value = "Fps", text = T(783476822556, --[[options:FPS counter shows frames per second]] "FPS"), hr = { FpsCounter = 1 } },
	{ value = "Ms", text = T(271886807258, --[[options:FPS counter shows miliseconds]] "ms"), hr = { FpsCounter = 2 } },
}

---
--- Defines the available options for the Texture quality setting in the game.
---
--- The `OptionsData.Options.Textures` table contains a list of Texture quality options that can be selected in the game's options menu. Each option has a `value` field that represents the internal identifier for the Texture quality mode, and a `text` field that contains the display name for the Texture quality mode.
---
--- The available Texture quality modes include:
--- - `Low`: Reduces the texture quality and streaming video memory usage.
--- - `Low (Consoles)`: Reduces the texture quality and streaming video memory usage for console platforms.
--- - `Medium (Consoles)`: Sets the texture quality and streaming video memory usage to a medium level for console platforms.
--- - `Medium`: Sets the texture quality and streaming video memory usage to a medium level.
--- - `High`: Increases the texture quality and streaming video memory usage.
--- - `Ultra`: Sets the texture quality and streaming video memory usage to the highest level.
---
--- The `hr` field for each option contains a table of settings that will be applied when the corresponding option is selected. These settings control the `StreamingVideoMemory` and `BillboardMaterialQualityReductionLevel` parameters, which affect the texture quality and memory usage.
---
--- The `not_selectable` field is used to mark certain options as not selectable, such as the "Low (Consoles)" and "Medium (Consoles)" options, which are only available on console platforms.
OptionsData.Options.Textures = {
	{ value = "Low", text = T(812680094837, --[[options:Texture quality is set to Low]] "Low"), hr = { StreamingVideoMemory = 384, BillboardMaterialQualityReductionLevel = 1 } },
	{ value = "Low (Consoles)", text = Untranslated("Low (Consoles)"), hr = { StreamingVideoMemory = 512, BillboardMaterialQualityReductionLevel = 1 }, not_selectable = Platform.pc and not Platform.developer },
	{ value = "Medium (Consoles)", text = Untranslated("Medium (Consoles)"), hr = { StreamingVideoMemory = 1024, BillboardMaterialQualityReductionLevel = 1 }, not_selectable = Platform.pc and not Platform.developer },
	{ value = "Medium", text = T(645, --[[options:Texture quality is set to Medium]] "Medium"), hr = { StreamingVideoMemory = 1024, BillboardMaterialQualityReductionLevel = 0 } },
	{ value = "High", text = T(396237728087, --[[options:Texture quality is set to High]] "High"), hr = { StreamingVideoMemory = 2048, BillboardMaterialQualityReductionLevel = 0 } },
	{ value = "Ultra", text =  T(324283091069, --[[options:Texture quality is set to Ultra]] "Ultra"), hr = { StreamingVideoMemory = 4096, BillboardMaterialQualityReductionLevel = 0 } },
}

---
--- Defines the available options for the Terrain detail setting in the game.
---
--- The `OptionsData.Options.Terrain` table contains a list of Terrain detail options that can be selected in the game's options menu. Each option has a `value` field that represents the internal identifier for the Terrain detail mode, and a `text` field that contains the display name for the Terrain detail mode.
---
--- The available Terrain detail modes include:
--- - `Low (Switch)`: Reduces the terrain detail and chunk size for the Nintendo Switch platform.
--- - `Low`: Reduces the terrain detail and chunk size.
--- - `Medium`: Sets the terrain detail and chunk size to a medium level.
--- - `High`: Increases the terrain detail and chunk size.
--- - `Ultra`: Sets the terrain detail and chunk size to the highest level.
---
--- The `hr` field for each option contains a table of settings that will be applied when the corresponding option is selected. These settings control the `TR_ChunkSize`, `TR_MaxChunks`, `TR_MaxChunksPerFrame`, `TR_MaterialQualityReductionLevel`, and `TR_UseQualityCompression` parameters, which affect the terrain detail and performance.
---
--- The `not_selectable` field is used to mark certain options as not selectable, such as the "Low (Switch)" option, which is only available on the Nintendo Switch platform.
OptionsData.Options.Terrain = {
	{ value = "Low (Switch)", text = Untranslated("*Low (Switch)"), hr = { TR_ChunkSize = 256, TR_MaxChunks = 64, TR_MaxChunksPerFrame = 1, TR_MaterialQualityReductionLevel = 2, TR_UseQualityCompression = 0 }, not_selectable = not Platform.developer },
	{ value = "Low", text = T(619416576830, --[[options:Terrain detail is set to Low]] "Low"), hr = { TR_ChunkSize = 256, TR_MaxChunks = 64, TR_MaxChunksPerFrame = 1, TR_MaterialQualityReductionLevel = 2 } },
	{ value = "Medium", text = T(482982848821, --[[options:Terrain detail is set to Medium]] "Medium"), hr = { TR_ChunkSize = 256, TR_MaxChunks = 128, TR_MaxChunksPerFrame = 2, TR_MaterialQualityReductionLevel = 1 } },
	{ value = "High", text = T(424607201144, --[[options:Terrain detail is set to High]] "High"), hr = { TR_ChunkSize = 512, TR_MaxChunks = 128, TR_MaxChunksPerFrame = 2, TR_MaterialQualityReductionLevel = 0 } },
	{ value = "Ultra", text = T(340208038771, --[[options:Terrain detail is set to Ultra]] "Ultra"), hr = { TR_ChunkSize = 512, TR_MaxChunks = 128, TR_MaxChunksPerFrame = 5, TR_MaterialQualityReductionLevel = 0 } },
}

---
--- Defines the available options for the Effects detail setting in the game.
---
--- The `OptionsData.Options.Effects` table contains a list of Effects detail options that can be selected in the game's options menu. Each option has a `value` field that represents the internal identifier for the Effects detail mode, and a `text` field that contains the display name for the Effects detail mode.
---
--- The available Effects detail modes include:
--- - `Low (Switch)`: Reduces the effects detail for the Nintendo Switch platform.
--- - `Low`: Reduces the effects detail.
--- - `Medium`: Sets the effects detail to a medium level.
--- - `High`: Increases the effects detail.
--- - `Ultra`: Sets the effects detail to the highest level.
---
--- The `hr` field for each option contains a table of settings that will be applied when the corresponding option is selected. These settings control the `FXDetailThreshold`, `RainQuality`, `RainStreaksCount`, `MaxParticles`, `TargetParticles`, and `MaxParticlesWithCollision` parameters, which affect the effects detail and performance.
---
--- The `not_selectable` field is used to mark certain options as not selectable, such as the "Low (Switch)" option, which is only available on the Nintendo Switch platform.
OptionsData.Options.Effects = {
	{ value = "Low (Switch)", text = Untranslated("*Low (Switch)"), hr = { FXDetailThreshold = 70, RainQuality = const.RainQualityVeryLow, RainStreaksCount = 16 * 1024, MaxParticles = 6000, TargetParticles = 5000, MaxParticlesWithCollision = 0, }, not_selectable = Platform.pc and not Platform.developer },
	{ value = "Low", text = T(921959811873, --[[options:Effects detail is set to Low]] "Low"), hr = { FXDetailThreshold = 70, RainQuality = const.RainQualityLow, MaxParticles = 6000, TargetParticles = 5000, MaxParticlesWithCollision = 50,  } },
	{ value = "Medium", text = T(177066169751, --[[options:Effects detail is set to Medium]] "Medium"), hr = { FXDetailThreshold = 50, RainQuality = const.RainQualityMedium, MaxParticles = 7500, TargetParticles = 6500, MaxParticlesWithCollision = 150,} },
	{ value = "High", text = T(354778733499, --[[options:Effects detail is set to High]] "High"), hr = { FXDetailThreshold = 0, RainQuality = const.RainQualityHigh, MaxParticles = 30000, TargetParticles = 29000, MaxParticlesWithCollision = 300, } },
	{ value = "Ultra", text = T(107890243019, --[[options:Effects detail is set to Ultra]] "Ultra"), hr = { FXDetailThreshold = 0, RainQuality = const.RainQualityUltra, MaxParticles = 100000, TargetParticles = 95000, MaxParticlesWithCollision = 500,  } },
}

---
--- Defines the available options for the View Distance setting in the game.
---
--- The `OptionsData.Options.ViewDistance` table contains a list of View Distance options that can be selected in the game's options menu. Each option has a `value` field that represents the internal identifier for the View Distance mode, and a `text` field that contains the display name for the View Distance mode.
---
--- The available View Distance modes include:
--- - `Low (Switch)`: Reduces the view distance for the Nintendo Switch platform.
--- - `Low`: Reduces the view distance.
--- - `Medium`: Sets the view distance to a medium level.
--- - `High`: Increases the view distance.
--- - `Ultra`: Sets the view distance to the highest level.
---
--- The `hr` field for each option contains a table of settings that will be applied when the corresponding option is selected. These settings control the `LODDistanceModifier`, `BillboardDistanceModifier`, and `DistanceModifier` parameters, which affect the view distance and performance.
---
--- The `not_selectable` field is used to mark certain options as not selectable, such as the "Low (Switch)" option, which is only available on the Nintendo Switch platform.
OptionsData.Options.ViewDistance = {
	{ value = "Low (Switch)", text = Untranslated("*Low (Switch)"), hr = { LODDistanceModifier = 10, BillboardDistanceModifier = 25, DistanceModifier = 30 }, not_selectable = Platform.pc and not Platform.developer },
	{ value = "Low", text = T(157135452050, --[[options:View distance is set to Low]] "Low"), hr = { LODDistanceModifier = 50, BillboardDistanceModifier = 30, DistanceModifier = 54 } },
	{ value = "Medium", text = T(108271291689, --[[options:View distance is set to Medium]] "Medium"), hr = { LODDistanceModifier = 75, BillboardDistanceModifier = 40, DistanceModifier = 72 } },
	{ value = "High", text = T(215175247095, --[[options:View distance is set to High]] "High"), hr = { LODDistanceModifier = 100, BillboardDistanceModifier = 50, DistanceModifier = 100 } },
	{ value = "Ultra", text = T(239271893639, --[[options:View distance is set to Ultra]] "Ultra"), hr = { LODDistanceModifier = 120, BillboardDistanceModifier = 50, DistanceModifier = 150 } },
}

---
--- Defines the available options for the Shadows setting in the game.
---
--- The `OptionsData.Options.Shadows` table contains a list of Shadows options that can be selected in the game's options menu. Each option has a `value` field that represents the internal identifier for the Shadows mode, and a `text` field that contains the display name for the Shadows mode.
---
--- The available Shadows modes include:
--- - `Off`: Disables shadows.
--- - `Low`: Sets the shadows to a low quality level.
--- - `Medium (PS4,XboxOne)`: Sets the shadows to a medium quality level, optimized for PS4 and Xbox One platforms.
--- - `Medium`: Sets the shadows to a medium quality level.
--- - `High`: Sets the shadows to a high quality level.
--- - `High (PS4Pro)`: Sets the shadows to a high quality level, optimized for PS4 Pro platform.
--- - `Ultra`: Sets the shadows to the highest quality level.
---
--- The `hr` field for each option contains a table of settings that will be applied when the corresponding option is selected. These settings control various shadow-related parameters, such as `Shadowmap`, `ShadowmapSize`, `ShadowPCFSize`, `ShadowCSMProjectionFit`, `ShadowCSMResolutionPercent`, `ShadowReceiversRatio`, `ShadowSDSMEnable`, `ShadowCSMUpdateFrequency`, `LightShadows`, `LightShadowsSize`, `LightShadowsHighQuality`, and `LightShadowsLowQuality`.
---
--- The `not_selectable` field is used to mark certain options as not selectable, such as the "Medium (PS4,XboxOne)" option, which is only available on the PS4 and Xbox One platforms.
OptionsData.Options.Shadows = {
	{ value = "Off", text = T(642008481801, --[[options:Shadows are turned off]] "Off"), hr = { Shadowmap = 0, ShadowmapSize = 0, LightShadows = 0 } },
	{ value = "Low", text = T(770274668602, --[[options:Shadows quality is set to Low]] "Low"), hr = {
		Shadowmap = 1,
		ShadowmapSize = 1536,
		ShadowPCFSize = 1,
		ShadowCSMProjectionFit = 1,
		ShadowCSMResolutionPercent = -65,
		ShadowReceiversRatio = 1,
		ShadowSDSMEnable = 1,
		ShadowCSMUpdateFrequency = "0",
		LightShadows = 1,
		LightShadowsSize = 1024,
		LightShadowsHighQuality = 2,
		LightShadowsLowQuality = 32 }},
	{ value = "Medium (PS4,XboxOne)", text = Untranslated("*Medium (PS4,XboxOne)"), hr = {
		Shadowmap = 1,
		ShadowmapSize = 2048,
		ShadowPCFSize = 2,
		ShadowCSMProjectionFit = 2,
		ShadowCSMResolutionPercent = -50,
		ShadowReceiversRatio = 1,
		ShadowSDSMEnable = 1,
		ShadowCSMUpdateFrequency = "0",
		LightShadows = 0,
		LightShadowsSize = 0 }, not_selectable = Platform.pc and not Platform.developer },
	{ value = "Medium", text = T(955331005438, --[[options:Shadows quality is set to Medium]] "Medium"), hr = {
		Shadowmap = 1,
		ShadowmapSize = 2048,
		ShadowPCFSize = 2,
		ShadowCSMProjectionFit = 2,
		ShadowCSMResolutionPercent = -50,
		ShadowReceiversRatio = 1,
		ShadowSDSMEnable = 1,
		ShadowCSMUpdateFrequency = "0",
		LightShadows = 1,
		LightShadowsSize = 2048,
		LightShadowsHighQuality = 8,
		LightShadowsLowQuality = 32 }},
	{ value = "High", text = T(875151214288, --[[options:Shadows quality is set to High]] "High"), hr = {
		Shadowmap = 1,
		ShadowmapSize = 4096,
		ShadowPCFSize = 3,
		ShadowCSMProjectionFit = 2,
		ShadowCSMResolutionPercent = 0,
		ShadowReceiversRatio = 100,
		ShadowSDSMEnable = 1,
		ShadowCSMUpdateFrequency = "0",
		LightShadows = 1,
		LightShadowsSize = 4096,
		LightShadowsHighQuality = 8,
		LightShadowsLowQuality = 128 }},
	{ value = "High (PS4Pro)", text = Untranslated("*High (PS4Pro)"), hr = {
		Shadowmap = 1,
		ShadowmapSize = 4096,
		ShadowPCFSize = 3,
		ShadowCSMProjectionFit = 2,
		ShadowCSMResolutionPercent = -50,
		ShadowReceiversRatio = 1,
		ShadowSDSMEnable = 1,
		ShadowCSMUpdateFrequency = "0",
		LightShadows = 0,
		LightShadowsSize = 0 }, not_selectable = Platform.pc and not Platform.developer },
	{ value = "Ultra", text = T(3551, --[[options:Shadows quality is set to Ultra]] "Ultra"), hr = {
		Shadowmap = 1,
		ShadowmapSize = 6144,
		ShadowPCFSize = 3,
		ShadowCSMProjectionFit = 2,
		ShadowCSMResolutionPercent=0,
		ShadowReceiversRatio = 100,
		ShadowSDSMEnable = 1,
		ShadowCSMUpdateFrequency = "0",
		LightShadows = 1,
		LightShadowsSize = 8192,
		LightShadowsHighQuality = 8,
		LightShadowsLowQuality = 128 }},
}

---
--- Resolves the appropriate anti-aliasing option based on the provided `antialiasing` value.
---
--- If the `antialiasing` value is "TAA" (Temporal Anti-Aliasing), this function will find the next best anti-aliasing option that uses the same resolution upscale method as TAA.
---
--- @param antialiasing string The anti-aliasing value to resolve.
--- @return string The resolved anti-aliasing option.
---
function ResolveAntialiasingOption(antialiasing)
	-- Implementation details
end
local function ResolveAntialiasingOption(antialiasing)
	if antialiasing == "TAA" then
		local antialiasing_option = table.find_value(OptionsData.Options.Antialiasing, "value", antialiasing)
		local antialiasing_index = table.findfirst(OptionsData.Options.Antialiasing, function(idx, item)
			return item.value ~= antialiasing_option.value and item.hr.ResolutionUpscale == antialiasing_option.hr.ResolutionUpscale
		end)
		return OptionsData.Options.Antialiasing[antialiasing_index].value
	end
	return antialiasing
end

---
--- Determines if the provided `antialiasing_value` is a temporal anti-aliasing option.
---
--- This function is used to check if the current anti-aliasing option uses a temporal resolution upscale method, such as DLSS, FSR2, or XESS.
---
--- @param antialiasing_value string The anti-aliasing value to check.
--- @return boolean True if the anti-aliasing option uses a temporal resolution upscale method, false otherwise.
---
function NotSelectableTemporalUpscalingOption(self, options_obj)
	return ResolveAntialiasingOption(options_obj.Antialiasing) ~= self.value
end
local function NotSelectableTemporalUpscalingOption(self, options_obj)
	return ResolveAntialiasingOption(options_obj.Antialiasing) ~= self.value
end

---
--- Determines if the provided `antialiasing_value` is a temporal anti-aliasing option.
---
--- This function is used to check if the current anti-aliasing option uses a temporal resolution upscale method, such as DLSS, FSR2, or XESS.
---
--- @param antialiasing_value string The anti-aliasing value to check.
--- @return boolean True if the anti-aliasing option uses a temporal resolution upscale method, false otherwise.
---
function IsTemporalAntialiasingOption(antialiasing_value)
	local antialiasing_option = table.find_value(OptionsData.Options.Antialiasing, "value", antialiasing_value)
	local method = antialiasing_option.hr.ResolutionUpscale
	return method and hr.IsTemporalResolutionUpscale(method)
end

---
--- Defines the available upscaling options for the game.
---
--- The `Upscaling` table contains a list of upscaling options that can be used to improve the game's resolution and image quality. Each option has a `value`, `text`, `hr`, and `not_selectable` field.
---
--- The `value` field is the unique identifier for the upscaling option.
--- The `text` field is the display name for the upscaling option.
--- The `hr` field contains hardware-related settings for the upscaling option.
--- The `not_selectable` field is a function that determines if the upscaling option should be selectable based on the current anti-aliasing option.
---
--- The available upscaling options are:
--- - "Off": Turns off upscaling.
--- - "DLSS": NVIDIA DLSS 2, a temporal upscaling technique.
--- - "FSR2": AMD FidelityFX Super Resolution 2, a temporal upscaling technique.
--- - "XESS": Intel XeSS, a temporal upscaling technique.
--- - "FSR": AMD FidelityFX Super Resolution 1.0, a spatial upscaling technique.
--- - "Bilinear": A simple bilinear upscaling filter.
---
--- The `NotSelectableTemporalUpscalingOption` function is used to determine if a temporal upscaling option should be selectable based on the current anti-aliasing option.
---
--- The `IsTemporalAntialiasingOption` function is used to determine if the current anti-aliasing option uses a temporal resolution upscale method.
---
OptionsData.Options.Upscaling = {
	{ value = "Off", text = T(392695272733, --[[options:Upscaling is turned off]] "Off"), hr = { ResolutionUpscale = "none" }, not_selectable = true },
	{ value = "DLSS", text = Untranslated("NVIDIA DLSS 2"), not_selectable = NotSelectableTemporalUpscalingOption,
		help_text = T(727329502546, "This option is forced based on the current anti-aliasing option.") },
	{ value = "FSR2", text = Untranslated("AMD FSR 2"), not_selectable = NotSelectableTemporalUpscalingOption,
		help_text = T(727329502546, "This option is forced based on the current anti-aliasing option.") },
	{ value = "XESS", text = Untranslated("Intel XeSS"), not_selectable = NotSelectableTemporalUpscalingOption,
		help_text = T(727329502546, "This option is forced based on the current anti-aliasing option.") },
	{ value = "FSR", text = Untranslated("AMD FSR 1.0"), hr = { ResolutionUpscale = "fsr" },
		not_selectable = function(self, options_obj) return IsTemporalAntialiasingOption(options_obj.Antialiasing) end,
		help_text = T(237540095950, "AMD FidelityFX Super Resolution 1.0 is a cutting edge super-optimized spatial upscaling technology that produces impressive image quality at fast framerates.") },
	{ value = "Bilinear", text = T(663907815524, --[[options:Bilinear upscaling method]] "Bilinear"), hr = { ResolutionUpscale = "none", },
		not_selectable = function(self, options_obj) return IsTemporalAntialiasingOption(options_obj.Antialiasing) end,
		help_text = T(168111358410, "The final image is upscaled using a quick bilinear filter.") },
}

---
--- Defines the available resolution percentage options for the game.
---
--- The `ResolutionPercent` table contains a list of resolution percentage options that can be used to adjust the game's resolution. Each option has a `value`, `text`, `hr`, and `not_selectable` field.
---
--- The `value` field is the unique identifier for the resolution percentage option.
--- The `text` field is the display name for the resolution percentage option.
--- The `hr` field contains hardware-related settings for the resolution percentage option.
--- The `not_selectable` field is a function that determines if the resolution percentage option should be selectable based on the current anti-aliasing option.
---
--- The available resolution percentage options are:
--- - "100": Native resolution (100%)
--- - "77": Ultra Quality (77%)
--- - "67": Quality (67%)
--- - "59": Balanced (59%)
--- - "50": Performance (50%)
--- - "33": Ultra Performance (33%)
---
--- The `not_selectable` function is used to determine if a resolution percentage option should be selectable based on the current anti-aliasing option. For example, the "33%" option is not selectable if the current anti-aliasing option is not a temporal upscaling method, or if the anti-aliasing option is "XESS".
---
OptionsData.Options.ResolutionPercent = {
	{ value = "100", text = T(372575555234, --[[options:Resolution percent 100%]] "Native (<percent(100)>)"), hr = { ResolutionPercent = 100, },
		not_selectable = function(self, options_obj) return options_obj.Antialiasing == "XESS" end, },
	{ value = "77", text = T(908658168865, --[[options:Resolution percent 77%]] "Ultra Quality (<percent(77)>)"), hr = { ResolutionPercent = 77, }, },
	{ value = "67", text = T(924914589055, --[[options:Resolution percent 67%]] "Quality (<percent(67)>)"), hr = { ResolutionPercent = 67, }, },
	{ value = "59", text = T(359371270894, --[[options:Resolution percent 59%]] "Balanced (<percent(59)>)"), hr = { ResolutionPercent = 59, }, },
	{ value = "50", text = T(326717026030, --[[options:Resolution percent 50%]] "Performance (<percent(50)>)"), hr = { ResolutionPercent = 50, }, },
	{ value = "33", text = T(243189993265, --[[options:Resolution percent 33%]] "Ultra Performance (<percent(33)>)"), hr = { ResolutionPercent = 33, },
		not_selectable = function(self, options_obj) return not IsTemporalAntialiasingOption(options_obj.Antialiasing) or options_obj.Antialiasing == "XESS" end, },
}

---
--- Defines the available anti-aliasing options for the game.
---
--- The `Antialiasing` table contains a list of anti-aliasing options that can be used to improve the visual quality of the game. Each option has a `value`, `text`, `hr`, `not_selectable`, and `help_text` field.
---
--- The `value` field is the unique identifier for the anti-aliasing option.
--- The `text` field is the display name for the anti-aliasing option.
--- The `hr` field contains hardware-related settings for the anti-aliasing option.
--- The `not_selectable` field is a function that determines if the anti-aliasing option should be selectable based on the current resolution percentage option.
--- The `help_text` field provides a description of the anti-aliasing option.
---
--- The available anti-aliasing options are:
--- - "Off": Disables anti-aliasing.
--- - "FXAA": Fast Approximate Anti-Aliasing, a high-performance and high-quality screen-space software approximation to anti-aliasing.
--- - "SMAA": Enhanced Subpixel Morphological Anti-Aliasing, an image-based, post-processing anti-aliasing technique.
--- - "TAA": Automatically picks a temporal anti-aliasing technique based on the machine's GPU.
--- - "DLSS": NVIDIA DLSS uses AI Super Resolution to provide the highest possible frame rates at maximum graphics settings. DLSS requires an NVIDIA RTX graphics card.
--- - "FSR2": AMD FidelityFX Super Resolution 2, a cutting-edge temporal upscaling algorithm that produces high resolution frames from lower resolution inputs.
--- - "XESS": Intel Xe Super Sampling (XeSS) technology uses machine learning to deliver higher performance with exceptional image quality.
---
OptionsData.Options.Antialiasing = {
	{ value = "Off", text = T(392695272733, --[[options:Antialiasing is turned off]] "Off"), hr = { EnablePostProcAA = 0, } },
	{ value = "FXAA", text = Untranslated("FXAA"), hr = { EnablePostProcAA = 1 },
		help_text = T(261152948496, "Fast Approximate Anti-Aliasing (FXAA) is a high performance and high quality screen-space software approximation to anti-aliasing.") },
	{ value = "SMAA", text = Untranslated("SMAA"), hr = { EnablePostProcAA = 2 },
		help_text = T(941529887409, "Enhanced Subpixel Morphological Anti-Aliasing (SMAA) is an image-based, post-processing anti-aliasing technique.") },
	{ value = "TAA",
		text = Untranslated("TAA"), hr = empty_table,
		not_selectable = function(self) return self.hr == empty_table end,
		help_text = T(872180326804, "Automatically picks a temporal anti-aliasing technique based on the machine's GPU.") },
	{ value = "DLSS",
		text = T{629765447024, "<name>", name = function() return Untranslated(OptionsObj and OptionsObj.ResolutionPercent == "100" and "NVIDIA DLAA 2" or "NVIDIA DLSS 2") end},
		hr = { ResolutionUpscale = "dlss" },
		not_selectable = function(self) return not hr.TemporalIsTypeSupported(self.hr.ResolutionUpscale) end,
		help_text = T(931850450677, "NVIDIA DLSS uses AI Super Resolution to provide the highest possible frame rates at maximum graphics settings. DLSS requires an NVIDIA RTX graphics card.") },
	{ value = "FSR2",
		text = Untranslated("AMD FSR 2"),
		hr = { ResolutionUpscale = "fsr2" },
		not_selectable = function(self) return not hr.TemporalIsTypeSupported(self.hr.ResolutionUpscale) end,
		help_text = T(266757012718, "AMD FidelityFX Super Resolution 2 is a cutting-edge temporal upscaling algorithm that produces high resolution frames from lower resolution inputs.") },
	{ value = "XESS",
		text = Untranslated("Intel XeSS"),
		hr = { ResolutionUpscale = "xess" },
		not_selectable = function(self) return not hr.TemporalIsTypeSupported(self.hr.ResolutionUpscale) end,
		help_text = T(825363827167, "Intel Xe Super Sampling (XeSS) technology uses machine learning to deliver higher performance with exceptional image quality.") },
}

---
--- Defines the available anisotropic filtering options for the game.
---
--- The `Anisotropy` table contains a list of anisotropic filtering options that can be used to improve the visual quality of textures. Each option has a `value`, `text`, and `hr` field.
---
--- The `value` field is the unique identifier for the anisotropic filtering option.
--- The `text` field is the display name for the anisotropic filtering option.
--- The `hr` field contains hardware-related settings for the anisotropic filtering option.
---
--- The available anisotropic filtering options are:
--- - "Off": Disables anisotropic filtering.
--- - "2x": Enables 2x anisotropic filtering.
--- - "4x": Enables 4x anisotropic filtering.
--- - "8x": Enables 8x anisotropic filtering.
--- - "16x": Enables 16x anisotropic filtering.
---
OptionsData.Options.Anisotropy = {
	{ value = "Off", text = T(692210423102, --[[options:Anisotropy is turned off]] "Off"), hr = { Anisotropy = 0 } },
	{ value = "2x", text = Untranslated("2x"), hr = { Anisotropy = 1 } },
	{ value = "4x", text = Untranslated("4x"), hr = { Anisotropy = 2 } },
	{ value = "8x", text = Untranslated("8x"), hr = { Anisotropy = 3 } },
	{ value = "16x", text = Untranslated("16x"), hr = { Anisotropy = 4 } },
}

---
--- Defines the available lighting options for the game.
---
--- The `Lights` table contains a list of lighting options that can be used to adjust the visual quality and performance of lights in the game. Each option has a `value`, `text`, and `hr` field.
---
--- The `value` field is the unique identifier for the lighting option.
--- The `text` field is the display name for the lighting option.
--- The `hr` field contains hardware-related settings for the lighting option.
---
--- The available lighting options are:
--- - "Low": Reduces the radius of lights by 90%.
--- - "Medium": Reduces the radius of lights by 95%.
--- - "High": Uses the full radius of lights.
---
OptionsData.Options.Lights = {
	{ value = "Low", text = T(709410953049, --[[options:Lights are turned to Low]] "Low"), hr = { LightsRadiusModifier = 90 } },
	{ value = "Medium", text = T(943866004028, --[[options:Lights are turned to Medium]] "Medium"), hr = { LightsRadiusModifier = 95} },
	{ value = "High", text = T(364201072641, --[[options:Lights are turned to High]] "High"), hr = { LightsRadiusModifier = 100 } },
}

---
--- Defines the available object detail options for the game.
---
--- The `ObjectDetail` table contains a list of object detail options that can be used to adjust the visual quality and performance of objects in the game. Each option has a `value`, `SortKey`, `text`, and `hr` field.
---
--- The `value` field is the unique identifier for the object detail option.
--- The `SortKey` field is used to sort the options in the UI.
--- The `text` field is the display name for the object detail option.
--- The `hr` field contains hardware-related settings for the object detail option.
---
--- The available object detail options are:
--- - "Very Low": Reduces object LOD, optionals, and eye candies to a minimum.
--- - "Low": Reduces object LOD, optionals, and eye candies moderately.
--- - "Medium": Reduces object LOD, optionals, and eye candies less aggressively.
--- - "High": Uses the full object LOD, optionals, and eye candies.
---
OptionsData.Options.ObjectDetail = {
	{ value = "Very Low", SortKey = 1000, text = T(717573023955, --[[options:Object detail is turned to Very Low]] "Very Low"),
		ObjectLODPercents = 50, Optionals = 50, EyeCandies = 0,
		hr = { ObjectLODCapMin = 1, LightShadowsDetailLevel = 0, LightShadowsMinContribDistance = 0, ClutterDetail = 0.0, UpdatedInstancesBudget = -1, BillboardDirectionsMaxDistance = 0,
			AnimUpdateF_Dist0 = 150000, AnimUpdateF_Dist1 = 600000, },
	},
	{ value = "Low", SortKey = 2000, text = T(215633457448, --[[options:Object detail is turned to Low]] "Low"),
		ObjectLODPercents = 50, Optionals = 50, EyeCandies = 33,
		hr = { ObjectLODCapMin = 0, LightShadowsDetailLevel = 1, LightShadowsMinContribDistance = 40, ClutterDetail = 0.25, UpdatedInstancesBudget = -1, BillboardDirectionsMaxDistance = 400,
			AnimUpdateF_Dist0 = 200000, AnimUpdateF_Dist1 = 800000, },
	},
	{ value = "Medium", SortKey = 3000, text = T(679289081998, --[[options:Object detail is turned to Medium]] "Medium"),
		ObjectLODPercents = 75, Optionals = 75, EyeCandies = 66,
		hr = { ObjectLODCapMin = 0, LightShadowsDetailLevel = 2, LightShadowsMinContribDistance = 60, ClutterDetail = 0.50, UpdatedInstancesBudget = -1, BillboardDirectionsMaxDistance = 600,
			AnimUpdateF_Dist0 = 300000, AnimUpdateF_Dist1 = 900000, },
	},
	{ value = "High", SortKey = 4000, text = T(564085803851, --[[options:Object detail is turned to High]] "High"),
		ObjectLODPercents = 100,  Optionals = 100, EyeCandies = 100,
		hr = { ObjectLODCapMin = 0, LightShadowsDetailLevel = 3, LightShadowsMinContribDistance = 100, ClutterDetail = 1.0, UpdatedInstancesBudget = -1, BillboardDirectionsMaxDistance = 750,
			AnimUpdateF_Dist0 = 500000, AnimUpdateF_Dist1 = 2000000, },
	},
}

---
--- Defines the available postprocessing options for the game.
---
--- The `Postprocess` table contains a list of postprocessing options that can be used to adjust the visual quality and performance of postprocessing effects in the game. Each option has a `value`, `text`, and `hr` field.
---
--- The `value` field is the unique identifier for the postprocessing option.
--- The `text` field is the display name for the postprocessing option.
--- The `hr` field contains hardware-related settings for the postprocessing option.
---
--- The available postprocessing options are:
--- - "Low": Reduces the quality of postprocessing effects.
--- - "Medium": Uses a medium quality level for postprocessing effects.
--- - "High": Uses a high quality level for postprocessing effects.
--- - "Ultra": Uses the highest quality level for postprocessing effects.
---
OptionsData.Options.Postprocess = {
	{ value = "Low", text = T(432248124587, --[[options:Postprocessing is turned to Low]] "Low"), hr = { SAOQuality = 0, SAOMipBase = 2 } },
	{ value = "Medium", text = T(550662244805, --[[options:Postprocessing is turned to Medium]] "Medium"), hr = { SAOQuality = 0, SAOMipBase = 1, } },
	{ value = "High", text = T(157332897114, --[[options:Postprocessing is turned to High]] "High"), hr = { SAOQuality = 1, SAOMipBase = 1, } },
	{ value = "Ultra", text = T(591239139006, --[[options:Postprocessing is turned to Ultra]] "Ultra"), hr = { SAOQuality = 2, SAOMipBase = 0, } },
}

---
--- Defines the available sharpness options for the game.
---
--- The `Sharpness` table contains a list of sharpness options that can be used to adjust the visual quality and performance of the sharpness effect in the game. Each option has a `value`, `text`, and `hr` field.
---
--- The `value` field is the unique identifier for the sharpness option.
--- The `text` field is the display name for the sharpness option.
--- The `hr` field contains hardware-related settings for the sharpness option.
---
--- The available sharpness options are:
--- - "Off": Disables the sharpness effect.
--- - "Low": Applies a low level of sharpness.
--- - "Medium": Applies a medium level of sharpness.
--- - "High": Applies a high level of sharpness.
---
OptionsData.Options.Sharpness = {
	{ value = "Off", text = T(571191621995, --[[options:Sharpness is turned Off]] "Off"), hr = { Sharpness = 0 } },
	{ value = "Low", text = T(291279322471, --[[options:Sharpness is turned to Low]] "Low"), hr = { Sharpness = 0.2 } },
	{ value = "Medium", text = T(548203462360, --[[options:Sharpness is turned to Medium]] "Medium"), hr = { Sharpness = 0.5 } },
	{ value = "High", text = T(213880173168, --[[options:Sharpness is turned to High]] "High"), hr = { Sharpness = 0.8 } },
}

---
--- Defines the available options categories for the game.
---
--- The `OptionsCategories` table contains a list of options categories that can be used to group and organize the various options available in the game. Each category has an `id`, `display_name`, `caps_name`, and optional `no_edit` and `run` fields.
---
--- The `id` field is the unique identifier for the options category.
--- The `display_name` field is the display name for the options category.
--- The `caps_name` field is the capitalized display name for the options category.
--- The `no_edit` field is an optional function that returns a boolean indicating whether the options category should be hidden from the user interface.
--- The `run` field is an optional function that is called when the options category is selected.
---
--- The available options categories are:
--- - "Display": Adjusts display-related settings.
--- - "Video": Adjusts video-related settings.
--- - "Audio": Adjusts audio-related settings.
--- - "Controls": Adjusts control-related settings.
--- - "Gameplay": Adjusts gameplay-related settings.
--- - "Keybindings": Adjusts keybinding-related settings.
--- - "ModOptions": Adjusts mod-related settings.
--- - "ChangeUser": Allows the user to change their profile.
--- - "Credits": Displays the game's credits.
---
OptionsCategories = {
	{id = "Display",     display_name = T(412409389789, "Display"),        caps_name = T(517337015408, "DISPLAY"), no_edit = Platform.console and Platform.goldmaster, },
	{id = "Video",       display_name = T(255390845026, "Video"),          caps_name = T(325469437176, "VIDEO")},
	{id = "Audio",       display_name = T(973319776875, "Audio"),          caps_name = T(278460229053, "AUDIO")},
	{id = "Controls",    display_name = T(437489721989, "Controls"),       caps_name = T(431903983139, "CONTROLS")},
	{id = "Gameplay",    display_name = T(350787334289, "Gameplay"),       caps_name = T(858632259775, "GAMEPLAY")},
	{id = "Keybindings", display_name = T(867036363190, "Key Bindings"),   caps_name = T(852769320242, "KEY BINDINGS"), 
		no_edit = function() return Platform.console end, },
	{id = "ModOptions",  display_name = T(454731851212, "Mod Options"),             caps_name = T(655539268008, "MOD OPTIONS"), 
		no_edit = function() return not config.Mods or not HasModsWithOptions() end },
	{id = "ChangeUser",  display_name = T(173037664401, "Change Profile"), caps_name = T(584707216514, "CHANGE PROFILE"), 
		no_edit = function() return HideChangeUserCategory() or not (Platform.xbox or Platform.windows_store) or GameState.gameplay end, 
		run = function() CreateRealTimeThread(function() if Platform.xbox then XboxChangeProfile() else WindowsStoreSignInUser() end end) end, },
	{id = "Credits",     display_name = T(283802894796, "Credits"),           caps_name = T(465539577876, "CREDITS"),
		no_edit = function() return GameState.gameplay or HideCreditsInOptions() end,},
}

---
--- Determines whether the "Change Profile" options category should be hidden.
---
--- @return boolean
---   Returns `true` if the "Change Profile" options category should be hidden, `false` otherwise.
function HideChangeUserCategory()
	return false
end

---
--- Determines whether the credits should be hidden in the options menu.
---
--- @return boolean
---   Returns `true` if the credits should be hidden in the options menu, `false` otherwise.
function HideCreditsInOptions()
	return false
end

---
--- Applies the user's account options.
---
--- This function calls `ApplyProjectAccountOptions()` to apply any project-specific account options.
---
--- @function ApplyAccountOptions
--- @return nil
function ApplyAccountOptions()
	Msg("ApplyAccountOptions")
	ApplyProjectAccountOptions()
end

---
--- Applies any project-specific account options.
---
--- This function is a placeholder that must be overridden in the project-specific `ProjectOptions.lua` file.
---
function ApplyProjectAccountOptions()
end

---
--- Applies map-specific engine settings.
---
--- This function is a placeholder that must be overridden in the project-specific code to tweak engine options per map.
---
function ApplyMapEngineSettings(map)
end


---
--- Applies the user's account options and regenerates the clutter.
---
--- This function is called when the options are applied. It calls `ApplyAccountOptions()` to apply the user's account options, and then calls `clutter.Regenerate()` to regenerate the clutter.
---
--- @function OnMsg.OptionsApply
--- @return nil
function OnMsg.OptionsApply()
	ApplyAccountOptions()
	clutter.Regenerate()
end

OnMsg.AccountStorageChanged = ApplyAccountOptions

---
--- Returns the name of the current object detail setting.
---
--- @return string
---   The name of the current object detail setting.
function GetObjectDetailsName()
	OptionsObj = OptionsObj or OptionsCreateAndLoad()
	return OptionsObj.ObjectDetail
end

---
--- Sets the object detail level for the current map.
---
--- This function is used to set the object detail level for the current map. It updates the `hr` table with the appropriate values for the specified detail level, and hides objects based on the optional and eye candy settings for that detail level.
---
--- @param details string
---   The name of the object detail level to set.
--- @param all_hrs boolean
---   If true, all high-resolution values are set. If false, only the light shadows detail level is set.
--- @param dont_apply_filters boolean
---   If true, the editor filters are not applied after setting the object detail.
--- @return number, number
---   The percentage of optional objects and eye candy objects that are hidden, respectively.
function SetObjectDetail(details, all_hrs, dont_apply_filters)
	local on_map = (GetMap() ~= "")
	
	if on_map then
		SuspendPassEdits("SetObjectDetail")
	end
	
	local params = {}
	Msg("SetObjectDetail", "init", params)
	
	local entry = table.find_value(OptionsData.Options.ObjectDetail, "value", details)
	if all_hrs then
		for hr_name, hr_value in pairs(entry.hr) do
			hr[hr_name] = hr_value
		end
	else
		hr.LightShadowsDetailLevel = entry.hr.LightShadowsDetailLevel
	end
	
	if on_map then
		HideObjectsByDetailClass(entry.Optionals, 100, entry.EyeCandies)

		if not dont_apply_filters and IsEditorActive() then
			XEditorFiltersApply()
		end
	end
	
	Msg("SetObjectDetail", "done", params)	
	
	if on_map then
		ResumePassEdits("SetObjectDetail")
	end
	
	return entry.Optionals, entry.EyeCandies
end

---
--- Sets the object detail level for the current map.
---
--- This function is used to set the object detail level for the current map. It updates the `EngineOptions.ObjectDetail` table with the appropriate values for the specified detail level, saves the engine options, saves the account storage, and hides objects based on the optional and eye candy settings for that detail level. It also prints information about the object details, optionals, eye candies, and lights.
---
--- @param details string
---   The name of the object detail level to set.
--- @param dont_apply_filters boolean
---   If true, the editor filters are not applied after setting the object detail.
function EngineSetObjectDetail(details, dont_apply_filters)
	if EngineOptions.ObjectDetail == details then return end
	
	EngineOptions.ObjectDetail = details
	SaveEngineOptions()
	SaveAccountStorage(5000)
	local optionals, eye_candies = SetObjectDetail(EngineOptions.ObjectDetail, "all hrs", dont_apply_filters)
	Msg("GameOptionsChanged", "Video")
	print(string.format("Object details: %s, Optionals: %d%%, Eye candies: %d%%, Lights: %d",
		EngineOptions.ObjectDetail, optionals, eye_candies, #GetLights()
	))
	XEditorUpdateStatusText()
end

local s_PreSaveMapDetails = false

---
--- Saves the current object detail level and sets the detail level to "High" before saving the map.
---
--- This function is called before the map is saved. It checks the current object detail level and if it is not "High", it saves the current detail level in `s_PreSaveMapDetails` and sets the detail level to "High" with the `"dont_apply_filters"` option. This ensures that the map is saved with the highest object detail level, regardless of the current setting.
---
--- After the map is saved, the `OnMsg.PostSaveMap()` function is called, which restores the original object detail level from `s_PreSaveMapDetails`.
---
--- @function OnMsg.PreSaveMap
--- @return nil
function OnMsg.PreSaveMap()
	local current_details = GetObjectDetailsName()
	if current_details ~= "High" then
		s_PreSaveMapDetails = current_details
		SetObjectDetail("High", nil, "dont_apply_filters")
	end
end

---
--- Restores the original object detail level after the map is saved.
---
--- This function is called after the map is saved. It checks if the `s_PreSaveMapDetails` variable is set, which indicates that the object detail level was changed before saving the map. If so, it restores the original object detail level using the `SetObjectDetail` function with the `"dont_apply_filters"` option.
---
--- @function OnMsg.PostSaveMap
--- @return nil
function OnMsg.PostSaveMap()
	if s_PreSaveMapDetails then
		SetObjectDetail(s_PreSaveMapDetails, nil, "dont_apply_filters")
		s_PreSaveMapDetails = false
	end
end

---
--- Called after a new map is loaded. This function sets the object detail level to the current setting.
---
--- This function is called as a game time thread, which ensures that it runs after the map has finished loading. It sets the object detail level to the current setting using the `SetObjectDetail` function.
---
--- @function OnMsg.PostNewMapLoaded
--- @return nil
function OnMsg.PostNewMapLoaded()
	CreateGameTimeThread(function()
		SetObjectDetail(GetObjectDetailsName(), "all hrs")
	end)
end

---
--- Changes the game language based on the user's account settings.
---
--- This function checks if the platform is a console, and if so, returns without doing anything. Otherwise, it retrieves the user's selected language from the account storage, sets the language using `SetLanguage()`, saves the language option permanently in the registry, mounts the language, loads the translation tables, and initializes the Windows IME state.
---
--- @function ApplyLanguageOption
--- @return nil
function ApplyLanguageOption()
	--cannot change language on consoles
	if Platform.console then
		return
	end
	local new_lang = GetAccountStorageOptionValue("Language")
	if SetLanguage(new_lang) then --set global variable
		SaveLanguageOption(GetLanguage()) --save permanently the result in the registry (e.g. "Auto" would be resolved into a specific lang)
		MountLanguage()
		LoadTranslationTables() --reload loc table
		InitWindowsImeState()
	end
end

---
--- Applies the specified brightness value to the display.
---
--- This function sets the display gamma value based on the provided brightness value. If no brightness value is provided, it uses the value from the `EngineOptions.Brightness` setting.
---
--- @param val number|nil The brightness value to apply, or `nil` to use the value from `EngineOptions.Brightness`.
--- @return nil
function ApplyBrightness(val)
	val = val or EngineOptions.Brightness
	if val then
		hr.DisplayGamma = 1500 - val
	end
end

---
--- Updates the UI style based on whether a gamepad is connected.
---
--- This function checks if a gamepad is connected and updates the UI style accordingly. If a gamepad is connected or the platform is a console, the UI style is set to gamepad mode. Otherwise, the UI style is set to non-gamepad mode.
---
--- @param gamepad boolean|nil Whether a gamepad is connected. If not provided, the function will automatically detect if a gamepad is connected.
--- @return nil
function UpdateUIStyleGamepad(gamepad)
	gamepad = gamepad and (Platform.console or IsXInputControllerConnected())
	ChangeGamepadUIStyle({ [1] = gamepad })
end

---
--- Applies the user's account options related to the UI style and gamepad usage.
---
--- This function checks if the user's account storage is available, and if so, it retrieves the user's gamepad setting and updates the UI style accordingly using `UpdateUIStyleGamepad()`. If the account storage is not available, it defaults to using the platform's console setting to determine the UI style.
---
--- @function OnMsg.ApplyAccountOptions
--- @return nil
function OnMsg.ApplyAccountOptions()
	if AccountStorage then
		UpdateUIStyleGamepad(GetAccountStorageOptionValue("Gamepad"))
	else
		UpdateUIStyleGamepad(not not Platform.console)
	end
end

---
--- Gets the display area margin based on the platform.
---
--- On PlayStation platforms, this function calculates the display area margin based on the safe area and screen size. On other platforms, it returns the value from the `EngineOptions.DisplayAreaMargin` setting, or 0 if the setting is not available.
---
--- @return number The display area margin
function GetDisplayAreaMargin()
	if Platform.playstation then
		local safe_w, safe_h = UIL.GetSafeArea()
		
		local screen_size = UIL.GetScreenSize()
		local screen_w, screen_h = screen_size:xy()
		local margin = MulDivRound(safe_w, 100, screen_w)
		
		return margin
	else
		return EngineOptions and EngineOptions.DisplayAreaMargin or 0
	end
end