Skip to content

Commit 30da29f

Browse files
committed
Harden legacy console raw save support
1 parent 03acce8 commit 30da29f

11 files changed

Lines changed: 628 additions & 75 deletions

backend/cmd/server/device_config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
)
1111

1212
var misterFPGAAllowedSystems = []string{
13+
"atari-lynx",
14+
"colecovision",
1315
"game-gear",
1416
"gameboy",
1517
"gba",
@@ -18,11 +20,14 @@ var misterFPGAAllowedSystems = []string{
1820
"n64",
1921
"neogeo",
2022
"nes",
23+
"pc-engine",
2124
"psx",
2225
"saturn",
2326
"sega-32x",
2427
"sega-cd",
28+
"sg-1000",
2529
"snes",
30+
"wonderswan",
2631
}
2732

2833
func normalizeDeviceConfigGlobal(input *deviceConfigGlobal) *deviceConfigGlobal {
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
package main
2+
3+
import "strings"
4+
5+
var legacyRawSaveProfiles = map[string]strictRawSaveValidationProfile{
6+
"pc-engine": {
7+
SystemSlug: "pc-engine",
8+
DisplayName: "pc engine / turbografx-16",
9+
ParserID: "pc-engine-raw-bram",
10+
AllowedExts: map[string]struct{}{
11+
"brm": {},
12+
"bkr": {},
13+
"ram": {},
14+
"sav": {},
15+
"srm": {},
16+
},
17+
AllowedSizes: map[int]struct{}{
18+
2000: {}, // PC Engine BRAM user bytes are often exposed without chip padding.
19+
2048: {},
20+
8192: {},
21+
32768: {},
22+
131072: {}, // Memory Base 128 style backup RAM.
23+
},
24+
RequireROMSHA1: true,
25+
RequireDeclared: true,
26+
RequireHelperOrStore: true,
27+
RejectBlank: true,
28+
SparseWarningCutoff: 16,
29+
Warning: "PC Engine support is raw backup RAM validation; no per-game structural decoder is available yet",
30+
},
31+
"atari-lynx": {
32+
SystemSlug: "atari-lynx",
33+
DisplayName: "atari lynx",
34+
ParserID: "atari-lynx-raw-eeprom",
35+
AllowedExts: map[string]struct{}{
36+
"eep": {},
37+
"eeprom": {},
38+
"ram": {},
39+
"sav": {},
40+
"srm": {},
41+
},
42+
AllowedSizes: map[int]struct{}{
43+
128: {},
44+
256: {},
45+
512: {},
46+
1024: {},
47+
2048: {},
48+
8192: {},
49+
},
50+
RequireROMSHA1: true,
51+
RequireDeclared: true,
52+
RequireHelperOrStore: true,
53+
RejectBlank: true,
54+
SparseWarningCutoff: 8,
55+
Warning: "Atari Lynx support is raw EEPROM validation; most commercial Lynx games do not carry persistent EEPROM data",
56+
},
57+
"wonderswan": {
58+
SystemSlug: "wonderswan",
59+
DisplayName: "wonderswan",
60+
ParserID: "wonderswan-raw-backup",
61+
AllowedExts: map[string]struct{}{
62+
"eep": {},
63+
"eeprom": {},
64+
"ram": {},
65+
"sav": {},
66+
"srm": {},
67+
},
68+
AllowedSizes: map[int]struct{}{
69+
128: {},
70+
256: {},
71+
512: {},
72+
1024: {},
73+
2048: {},
74+
8192: {},
75+
32768: {},
76+
65536: {},
77+
131072: {},
78+
},
79+
RequireROMSHA1: true,
80+
RequireDeclared: true,
81+
RequireHelperOrStore: true,
82+
RejectBlank: true,
83+
SparseWarningCutoff: 8,
84+
Warning: "WonderSwan support is raw cartridge backup validation; no per-game structural decoder is available yet",
85+
},
86+
"sg-1000": {
87+
SystemSlug: "sg-1000",
88+
DisplayName: "sg-1000",
89+
ParserID: "sg-1000-raw-backup",
90+
AllowedExts: map[string]struct{}{
91+
"ram": {},
92+
"sav": {},
93+
"srm": {},
94+
},
95+
AllowedSizes: map[int]struct{}{
96+
128: {},
97+
256: {},
98+
512: {},
99+
1024: {},
100+
2048: {},
101+
4096: {},
102+
8192: {},
103+
16384: {},
104+
32768: {},
105+
},
106+
RequireROMSHA1: true,
107+
RequireDeclared: true,
108+
RequireHelperOrStore: true,
109+
RejectBlank: true,
110+
SparseWarningCutoff: 8,
111+
Warning: "SG-1000 support is conservative raw save validation for cartridges or runtimes that expose persistent save RAM",
112+
},
113+
"colecovision": {
114+
SystemSlug: "colecovision",
115+
DisplayName: "colecovision",
116+
ParserID: "colecovision-raw-backup",
117+
AllowedExts: map[string]struct{}{
118+
"eep": {},
119+
"eeprom": {},
120+
"nv": {},
121+
"nvram": {},
122+
"ram": {},
123+
"sav": {},
124+
"srm": {},
125+
},
126+
AllowedSizes: map[int]struct{}{
127+
128: {},
128+
256: {},
129+
512: {},
130+
1024: {},
131+
2048: {},
132+
4096: {},
133+
8192: {},
134+
16384: {},
135+
32768: {},
136+
},
137+
RequireROMSHA1: true,
138+
RequireDeclared: true,
139+
RequireHelperOrStore: true,
140+
RejectBlank: true,
141+
SparseWarningCutoff: 8,
142+
Warning: "ColecoVision support is conservative raw NVRAM/EEPROM validation for runtimes that expose persistent save data",
143+
},
144+
"atari-jaguar": {
145+
SystemSlug: "atari-jaguar",
146+
DisplayName: "atari jaguar",
147+
ParserID: "atari-jaguar-raw-eeprom",
148+
AllowedExts: map[string]struct{}{
149+
"eep": {},
150+
"eeprom": {},
151+
"nv": {},
152+
"nvram": {},
153+
"ram": {},
154+
"sav": {},
155+
"srm": {},
156+
},
157+
AllowedSizes: map[int]struct{}{
158+
128: {},
159+
256: {},
160+
512: {},
161+
1024: {},
162+
2048: {},
163+
8192: {},
164+
32768: {},
165+
131072: {},
166+
},
167+
RequireROMSHA1: true,
168+
RequireDeclared: true,
169+
RequireHelperOrStore: true,
170+
RejectBlank: true,
171+
SparseWarningCutoff: 8,
172+
Warning: "Atari Jaguar support is raw EEPROM/NVRAM validation; no per-game structural decoder is available yet",
173+
},
174+
"3do": {
175+
SystemSlug: "3do",
176+
DisplayName: "3do",
177+
ParserID: "3do-raw-nvram",
178+
AllowedExts: map[string]struct{}{
179+
"nv": {},
180+
"nvram": {},
181+
"ram": {},
182+
"sav": {},
183+
"srm": {},
184+
},
185+
AllowedSizes: map[int]struct{}{
186+
32768: {},
187+
131072: {},
188+
},
189+
RequireROMSHA1: true,
190+
RequireDeclared: true,
191+
RequireHelperOrStore: true,
192+
RejectBlank: true,
193+
SparseWarningCutoff: 16,
194+
Warning: "3DO support is raw NVRAM validation; no per-game structural decoder is available yet",
195+
},
196+
}
197+
198+
func validateLegacyRawSave(input saveCreateInput, detection saveSystemDetectionResult, systemSlug string) consoleValidationResult {
199+
profile, ok := legacyRawSaveProfiles[strings.ToLower(strings.TrimSpace(systemSlug))]
200+
if !ok {
201+
return consoleValidationResult{}
202+
}
203+
return validateStrictRawSaveClass(input, detection, profile)
204+
}

0 commit comments

Comments
 (0)