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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
24Properties::Properties()
25{
26 setDefaults();
27}
28
29// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
30Properties::Properties(const Properties& properties)
31{
32 copy(properties);
33}
34
35// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
36void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
79istream& 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
114ostream& 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
143string 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
171void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
193bool 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
203bool Properties::operator!=(const Properties& properties) const
204{
205 return !(*this == properties);
206}
207
208// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
209Properties& 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
222void Properties::setDefault(PropType key, const string& value)
223{
224 ourDefaultProperties[static_cast<uInt8>(key)] = value;
225}
226
227// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
228void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
236void 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
263void Properties::setDefaults()
264{
265 for(uInt8 i = 0; i < static_cast<uInt8>(PropType::NumTypes); ++i)
266 myProperties[i] = ourDefaultProperties[i];
267}
268
269// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
270PropType 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
281void Properties::printHeader()
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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
308string 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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
334const 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