1
//============================================================================
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
11
// Copyright (c) 1995-2005 by Bradford W. Mott and the Stella team
13
// See the file "license" for information on usage and redistribution of
14
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16
// $Id: CheatManager.cxx,v 1.10 2006/03/22 15:12:02 stephena Exp $
17
//============================================================================
21
#include "OSystem.hxx"
23
#include "CheetahCheat.hxx"
24
#include "BankRomCheat.hxx"
25
#include "RamCheat.hxx"
27
#include "CheatManager.hxx"
29
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
30
CheatManager::CheatManager(OSystem* osystem)
37
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
38
CheatManager::~CheatManager()
45
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
46
const Cheat* CheatManager::add(const string& name, const string& code,
49
Cheat* cheat = (Cheat*) createCheat(name, code);
53
// Delete duplicate entries
54
for(unsigned int i = 0; i < myCheatList.size(); i++)
56
if(myCheatList[i]->code() == code)
58
myCheatList.remove_at(i);
63
// Add the cheat to the main cheat list
65
myCheatList.push_back(cheat);
67
myCheatList.insert_at(idx, cheat);
69
// And enable/disable it (the cheat knows how to enable or disable itself)
78
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
79
void CheatManager::remove(int idx)
81
if((unsigned int)idx >= myCheatList.size())
84
Cheat* c = myCheatList[idx];
86
// First remove it from the per-frame list
87
addPerFrame(c, false);
89
// Then remove it from the cheatlist entirely
90
myCheatList.remove_at(idx);
94
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
95
void CheatManager::addPerFrame(Cheat* cheat, bool enable)
100
// Make sure there are no duplicates
103
for(i = 0; i < myPerFrameList.size(); i++)
105
if(myPerFrameList[i]->code() == cheat->code())
115
myPerFrameList.push_back(cheat);
120
myPerFrameList.remove_at(i);
124
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
125
void CheatManager::addOneShot(const string& name, const string& code)
127
Cheat* cheat = (Cheat*) createCheat(name, code);
131
// Evaluate this cheat once, and then immediately delete it
136
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
137
const Cheat* CheatManager::createCheat(const string& name, const string& code)
139
if(!isValidCode(code))
142
// Create new cheat based on string length
146
return new RamCheat(myOSystem, name, code);
150
return new CheetahCheat(myOSystem, name, code);
154
return new BankRomCheat(myOSystem, name, code);
162
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
163
void CheatManager::parse(const string& cheats)
166
string::size_type lastPos = cheats.find_first_not_of(",", 0);
167
string::size_type pos = cheats.find_first_of(",", lastPos);
168
string cheat, name, code;
170
// Split string by comma, getting each cheat
171
while(string::npos != pos || string::npos != lastPos)
173
// Get the next cheat
174
cheat = cheats.substr(lastPos, pos - lastPos);
176
// Split cheat by colon, separating each part
177
string::size_type lastColonPos = cheat.find_first_not_of(":", 0);
178
string::size_type colonPos = cheat.find_first_of(":", lastColonPos);
179
while(string::npos != colonPos || string::npos != lastColonPos)
181
s.push_back(cheat.substr(lastColonPos, colonPos - lastColonPos));
182
lastColonPos = cheat.find_first_not_of(":", colonPos);
183
colonPos = cheat.find_first_of(":", lastColonPos);
186
// Account for variable number of items specified for cheat
192
add(name, code, true);
198
add(name, code, true);
204
add(name, code, s[2] == "1");
209
lastPos = cheats.find_first_not_of(",", pos);
210
pos = cheats.find_first_of(",", lastPos);
214
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
215
void CheatManager::enable(const string& code, bool enable)
217
for(unsigned int i = 0; i < myCheatList.size(); i++)
219
if(myCheatList[i]->code() == code)
222
myCheatList[i]->enable();
224
myCheatList[i]->disable();
230
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
231
void CheatManager::loadCheatDatabase()
233
string cheatfile = myOSystem->baseDir() + BSPF_PATH_SEPARATOR + "stella.cht";
234
ifstream in(cheatfile.c_str(), ios::in);
238
string line, md5, cheat;
239
string::size_type one, two, three, four;
241
// Loop reading cheats
242
while(getline(in, line))
244
if(line.length() == 0)
247
one = line.find("\"", 0);
248
two = line.find("\"", one + 1);
249
three = line.find("\"", two + 1);
250
four = line.find("\"", three + 1);
252
// Invalid line if it doesn't contain 4 quotes
253
if((one == string::npos) || (two == string::npos) ||
254
(three == string::npos) || (four == string::npos))
257
// Otherwise get the ms5sum and associated cheats
258
md5 = line.substr(one + 1, two - one - 1);
259
cheat = line.substr(three + 1, four - three - 1);
261
myCheatMap.insert(make_pair(md5, cheat));
265
myListIsDirty = false;
268
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
269
void CheatManager::saveCheatDatabase()
274
string cheatfile = myOSystem->baseDir() + BSPF_PATH_SEPARATOR + "stella.cht";
275
ofstream out(cheatfile.c_str(), ios::out);
279
CheatCodeMap::iterator iter;
280
for(iter = myCheatMap.begin(); iter != myCheatMap.end(); ++iter)
281
out << "\"" << iter->first << "\" "
282
<< "\"" << iter->second << "\""
288
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
289
void CheatManager::loadCheats(const string& md5sum)
294
// Set up any cheatcodes that was on the command line
295
// (and remove the key from the settings, so they won't get set again)
296
string cheats = myOSystem->settings().getString("cheat");
298
myOSystem->settings().setString("cheat", "");
300
CheatCodeMap::iterator iter = myCheatMap.find(md5sum);
301
if(iter == myCheatMap.end() && cheats == "")
304
// Remember the cheats for this ROM
305
myCurrentCheat = iter->second;
307
// Parse the cheat list, constructing cheats and adding them to the manager
308
parse(iter->second + cheats);
311
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
312
void CheatManager::saveCheats(const string& md5sum)
314
ostringstream cheats;
315
for(unsigned int i = 0; i < myCheatList.size(); i++)
317
cheats << myCheatList[i]->name() << ":"
318
<< myCheatList[i]->code() << ":"
319
<< myCheatList[i]->enabled();
320
if(i+1 < myCheatList.size())
324
bool changed = cheats.str() != myCurrentCheat;
326
// Only update the list if absolutely necessary
329
CheatCodeMap::iterator iter = myCheatMap.find(md5sum);
331
// Erase old entry and add a new one only if it's changed
332
if(iter != myCheatMap.end())
333
myCheatMap.erase(iter);
335
// Add new entry only if there are any cheats defined
336
if(cheats.str() != "")
337
myCheatMap.insert(make_pair(md5sum, cheats.str()));
340
// Update the dirty flag
341
myListIsDirty = myListIsDirty || changed;
344
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
345
void CheatManager::clear()
347
// Don't delete the items from per-frame list, since it will be done in
348
// the following loop
349
myPerFrameList.clear();
351
for(unsigned int i = 0; i < myCheatList.size(); i++)
352
delete myCheatList[i];
356
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
357
bool CheatManager::isValidCode(const string& code)
359
for(unsigned int i = 0; i < code.size(); i++)
360
if(!isxdigit(code[i]))
363
int length = code.length();
364
return (length == 4 || length == 6 || length == 8);