| 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 | |