1 | //============================================================================ |
2 | // |
3 | // SSSS tt lll lll |
4 | // SS SS tt ll ll |
5 | // SS tttttt eeee ll ll aaaa |
6 | // SSSS tt ee ee ll ll aa |
7 | // SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" |
8 | // SS SS tt ee ll ll aa aa |
9 | // SSSS ttt eeeee llll llll aaaaa |
10 | // |
11 | // Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony |
12 | // and the Stella Team |
13 | // |
14 | // See the file "License.txt" for information on usage and redistribution of |
15 | // this file, and for a DISCLAIMER OF ALL WARRANTIES. |
16 | //============================================================================ |
17 | |
18 | #include <sstream> |
19 | |
20 | #include "bspf.hxx" |
21 | #include "Props.hxx" |
22 | |
23 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
24 | Properties::Properties() |
25 | { |
26 | setDefaults(); |
27 | } |
28 | |
29 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
30 | Properties::Properties(const Properties& properties) |
31 | { |
32 | copy(properties); |
33 | } |
34 | |
35 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
36 | void Properties::set(PropType key, const string& value) |
37 | { |
38 | uInt8 pos = static_cast<uInt8>(key); |
39 | if(pos < static_cast<uInt8>(PropType::NumTypes)) |
40 | { |
41 | myProperties[pos] = value; |
42 | if(BSPF::equalsIgnoreCase(myProperties[pos], "AUTO-DETECT" )) |
43 | myProperties[pos] = "AUTO" ; |
44 | |
45 | switch(key) |
46 | { |
47 | case PropType::Cart_Sound: |
48 | case PropType::Cart_Type: |
49 | case PropType::Console_LeftDiff: |
50 | case PropType::Console_RightDiff: |
51 | case PropType::Console_TVType: |
52 | case PropType::Console_SwapPorts: |
53 | case PropType::Controller_Left: |
54 | case PropType::Controller_Right: |
55 | case PropType::Controller_SwapPaddles: |
56 | case PropType::Controller_MouseAxis: |
57 | case PropType::Display_Format: |
58 | case PropType::Display_Phosphor: |
59 | { |
60 | BSPF::toUpperCase(myProperties[pos]); |
61 | break; |
62 | } |
63 | |
64 | case PropType::Display_PPBlend: |
65 | { |
66 | int blend = atoi(myProperties[pos].c_str()); |
67 | if(blend < 0 || blend > 100) |
68 | myProperties[pos] = ourDefaultProperties[pos]; |
69 | break; |
70 | } |
71 | |
72 | default: |
73 | break; |
74 | } |
75 | } |
76 | } |
77 | |
78 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
79 | istream& operator>>(istream& is, Properties& p) |
80 | { |
81 | p.setDefaults(); |
82 | |
83 | // Loop reading properties |
84 | string key, value; |
85 | for(;;) |
86 | { |
87 | // Get the key associated with this property |
88 | key = p.readQuotedString(is); |
89 | |
90 | // Make sure the stream is still okay |
91 | if(!is) |
92 | return is; |
93 | |
94 | // A null key signifies the end of the property list |
95 | if(key == "" ) |
96 | break; |
97 | |
98 | // Get the value associated with this property |
99 | value = p.readQuotedString(is); |
100 | |
101 | // Make sure the stream is still okay |
102 | if(!is) |
103 | return is; |
104 | |
105 | // Set the property |
106 | PropType type = Properties::getPropType(key); |
107 | p.set(type, value); |
108 | } |
109 | |
110 | return is; |
111 | } |
112 | |
113 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
114 | ostream& operator<<(ostream& os, const Properties& p) |
115 | { |
116 | // Write out each of the key and value pairs |
117 | bool changed = false; |
118 | for(uInt8 i = 0; i < static_cast<uInt8>(PropType::NumTypes); ++i) |
119 | { |
120 | // Try to save some space by only saving the items that differ from default |
121 | if(p.myProperties[i] != Properties::ourDefaultProperties[i]) |
122 | { |
123 | p.writeQuotedString(os, Properties::ourPropertyNames[i]); |
124 | os.put(' '); |
125 | p.writeQuotedString(os, p.myProperties[i]); |
126 | os.put('\n'); |
127 | changed = true; |
128 | } |
129 | } |
130 | |
131 | if(changed) |
132 | { |
133 | // Put a trailing null string so we know when to stop reading |
134 | p.writeQuotedString(os, "" ); |
135 | os.put('\n'); |
136 | os.put('\n'); |
137 | } |
138 | |
139 | return os; |
140 | } |
141 | |
142 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
143 | string Properties::readQuotedString(istream& in) |
144 | { |
145 | // Read characters until we see a quote |
146 | char c; |
147 | while(in.get(c)) |
148 | if(c == '"') |
149 | break; |
150 | |
151 | // Read characters until we see the close quote |
152 | string s; |
153 | while(in.get(c)) |
154 | { |
155 | if((c == '\\') && (in.peek() == '"')) |
156 | in.get(c); |
157 | else if((c == '\\') && (in.peek() == '\\')) |
158 | in.get(c); |
159 | else if(c == '"') |
160 | break; |
161 | else if(c == '\r') |
162 | continue; |
163 | |
164 | s += c; |
165 | } |
166 | |
167 | return s; |
168 | } |
169 | |
170 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
171 | void Properties::writeQuotedString(ostream& out, const string& s) |
172 | { |
173 | out.put('"'); |
174 | for(uInt32 i = 0; i < s.length(); ++i) |
175 | { |
176 | if(s[i] == '\\') |
177 | { |
178 | out.put('\\'); |
179 | out.put('\\'); |
180 | } |
181 | else if(s[i] == '\"') |
182 | { |
183 | out.put('\\'); |
184 | out.put('"'); |
185 | } |
186 | else |
187 | out.put(s[i]); |
188 | } |
189 | out.put('"'); |
190 | } |
191 | |
192 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
193 | bool Properties::operator==(const Properties& properties) const |
194 | { |
195 | for(uInt8 i = 0; i < static_cast<uInt8>(PropType::NumTypes); ++i) |
196 | if(myProperties[i] != properties.myProperties[i]) |
197 | return false; |
198 | |
199 | return true; |
200 | } |
201 | |
202 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
203 | bool Properties::operator!=(const Properties& properties) const |
204 | { |
205 | return !(*this == properties); |
206 | } |
207 | |
208 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
209 | Properties& Properties::operator=(const Properties& properties) |
210 | { |
211 | // Do the assignment only if this isn't a self assignment |
212 | if(this != &properties) |
213 | { |
214 | // Now, make myself a copy of the given object |
215 | copy(properties); |
216 | } |
217 | |
218 | return *this; |
219 | } |
220 | |
221 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
222 | void Properties::setDefault(PropType key, const string& value) |
223 | { |
224 | ourDefaultProperties[static_cast<uInt8>(key)] = value; |
225 | } |
226 | |
227 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
228 | void Properties::copy(const Properties& properties) |
229 | { |
230 | // Now, copy each property from properties |
231 | for(uInt8 i = 0; i < static_cast<uInt8>(PropType::NumTypes); ++i) |
232 | myProperties[i] = properties.myProperties[i]; |
233 | } |
234 | |
235 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
236 | void Properties::print() const |
237 | { |
238 | cout << get(PropType::Cart_MD5) << "|" |
239 | << get(PropType::Cart_Name) << "|" |
240 | << get(PropType::Cart_Manufacturer) << "|" |
241 | << get(PropType::Cart_ModelNo) << "|" |
242 | << get(PropType::Cart_Note) << "|" |
243 | << get(PropType::Cart_Rarity) << "|" |
244 | << get(PropType::Cart_Sound) << "|" |
245 | << get(PropType::Cart_StartBank) << "|" |
246 | << get(PropType::Cart_Type) << "|" |
247 | << get(PropType::Console_LeftDiff) << "|" |
248 | << get(PropType::Console_RightDiff) << "|" |
249 | << get(PropType::Console_TVType) << "|" |
250 | << get(PropType::Console_SwapPorts) << "|" |
251 | << get(PropType::Controller_Left) << "|" |
252 | << get(PropType::Controller_Right) << "|" |
253 | << get(PropType::Controller_SwapPaddles) << "|" |
254 | << get(PropType::Controller_MouseAxis) << "|" |
255 | << get(PropType::Display_Format) << "|" |
256 | << get(PropType::Display_YStart) << "|" |
257 | << get(PropType::Display_Phosphor) << "|" |
258 | << get(PropType::Display_PPBlend) |
259 | << endl; |
260 | } |
261 | |
262 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
263 | void Properties::setDefaults() |
264 | { |
265 | for(uInt8 i = 0; i < static_cast<uInt8>(PropType::NumTypes); ++i) |
266 | myProperties[i] = ourDefaultProperties[i]; |
267 | } |
268 | |
269 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
270 | PropType Properties::getPropType(const string& name) |
271 | { |
272 | for(uInt8 i = 0; i < static_cast<uInt8>(PropType::NumTypes); ++i) |
273 | if(ourPropertyNames[i] == name) |
274 | return PropType(i); |
275 | |
276 | // Otherwise, indicate that the item wasn't found |
277 | return PropType::NumTypes; |
278 | } |
279 | |
280 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
281 | void Properties::() |
282 | { |
283 | cout << "Cart_MD5|" |
284 | << "Cart_Name|" |
285 | << "Cart_Manufacturer|" |
286 | << "Cart_ModelNo|" |
287 | << "Cart_Note|" |
288 | << "Cart_Rarity|" |
289 | << "Cart_Sound|" |
290 | << "Cart_StartBank|" |
291 | << "Cart_Type|" |
292 | << "Console_LeftDiff|" |
293 | << "Console_RightDiff|" |
294 | << "Console_TVType|" |
295 | << "Console_SwapPorts|" |
296 | << "Controller_Left|" |
297 | << "Controller_Right|" |
298 | << "Controller_SwapPaddles|" |
299 | << "Controller_MouseAxis|" |
300 | << "Display_Format|" |
301 | << "Display_YStart|" |
302 | << "Display_Phosphor|" |
303 | << "Display_PPBlend" |
304 | << endl; |
305 | } |
306 | |
307 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
308 | string Properties::ourDefaultProperties[static_cast<uInt8>(PropType::NumTypes)] = |
309 | { |
310 | "" , // Cart.MD5 |
311 | "" , // Cart.Manufacturer |
312 | "" , // Cart.ModelNo |
313 | "" , // Cart.Name |
314 | "" , // Cart.Note |
315 | "" , // Cart.Rarity |
316 | "MONO" , // Cart.Sound |
317 | "" , // Cart.StartBank |
318 | "AUTO" , // Cart.Type |
319 | "B" , // Console.LeftDiff |
320 | "B" , // Console.RightDiff |
321 | "COLOR" , // Console.TVType |
322 | "NO" , // Console.SwapPorts |
323 | "AUTO" , // Controller.Left |
324 | "AUTO" , // Controller.Right |
325 | "NO" , // Controller.SwapPaddles |
326 | "AUTO" , // Controller.MouseAxis |
327 | "AUTO" , // Display.Format |
328 | "0" , // Display.YStart |
329 | "NO" , // Display.Phosphor |
330 | "0" // Display.PPBlend |
331 | }; |
332 | |
333 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
334 | const char* const Properties::ourPropertyNames[static_cast<uInt8>(PropType::NumTypes)] = |
335 | { |
336 | "Cart.MD5" , |
337 | "Cart.Manufacturer" , |
338 | "Cart.ModelNo" , |
339 | "Cart.Name" , |
340 | "Cart.Note" , |
341 | "Cart.Rarity" , |
342 | "Cart.Sound" , |
343 | "Cart.StartBank" , |
344 | "Cart.Type" , |
345 | "Console.LeftDiff" , |
346 | "Console.RightDiff" , |
347 | "Console.TVType" , |
348 | "Console.SwapPorts" , |
349 | "Controller.Left" , |
350 | "Controller.Right" , |
351 | "Controller.SwapPaddles" , |
352 | "Controller.MouseAxis" , |
353 | "Display.Format" , |
354 | "Display.YStart" , |
355 | "Display.Phosphor" , |
356 | "Display.PPBlend" |
357 | }; |
358 | |