~ubuntu-branches/debian/squeeze/stella/squeeze

« back to all changes in this revision

Viewing changes to src/cheat/CheatManager.cxx

  • Committer: Bazaar Package Importer
  • Author(s): Mario Iseli
  • Date: 2006-04-08 18:38:25 UTC
  • mfrom: (1.1.2 upstream) (2.1.1 etch)
  • Revision ID: james.westby@ubuntu.com-20060408183825-vu1jk57rk929derx
* New Maintainer (Closes: #361345)
* New upstream release (Closes: #349725)
* Build-Depend now on libslang2-dev (Closes: #325577)
* Complete rebuild of debian/, upgraded to policy-standards
  3.6.2 and compat-level 5.
* Removed stellarc since stella only reads ~/.stellarc and even
  works without a first config.
* New debian/watch file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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-2005 by Bradford W. Mott and the Stella team
 
12
//
 
13
// See the file "license" for information on usage and redistribution of
 
14
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
 
15
//
 
16
// $Id: CheatManager.cxx,v 1.10 2006/03/22 15:12:02 stephena Exp $
 
17
//============================================================================
 
18
 
 
19
#include <sstream>
 
20
 
 
21
#include "OSystem.hxx"
 
22
#include "Cheat.hxx"
 
23
#include "CheetahCheat.hxx"
 
24
#include "BankRomCheat.hxx"
 
25
#include "RamCheat.hxx"
 
26
 
 
27
#include "CheatManager.hxx"
 
28
 
 
29
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
30
CheatManager::CheatManager(OSystem* osystem)
 
31
  : myOSystem(osystem),
 
32
    myCurrentCheat(""),
 
33
    myListIsDirty(false)
 
34
{
 
35
}
 
36
 
 
37
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
38
CheatManager::~CheatManager()
 
39
{
 
40
  saveCheatDatabase();
 
41
  myCheatMap.clear();
 
42
  clear();
 
43
}
 
44
 
 
45
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
46
const Cheat* CheatManager::add(const string& name, const string& code,
 
47
                               bool enable, int idx)
 
48
{
 
49
  Cheat* cheat = (Cheat*) createCheat(name, code);
 
50
  if(!cheat)
 
51
    return NULL;
 
52
 
 
53
  // Delete duplicate entries
 
54
  for(unsigned int i = 0; i < myCheatList.size(); i++)
 
55
  {
 
56
    if(myCheatList[i]->code() == code)
 
57
    {
 
58
      myCheatList.remove_at(i);
 
59
      break;
 
60
    }
 
61
  }
 
62
 
 
63
  // Add the cheat to the main cheat list
 
64
  if(idx == -1)
 
65
    myCheatList.push_back(cheat);
 
66
  else
 
67
    myCheatList.insert_at(idx, cheat);
 
68
 
 
69
  // And enable/disable it (the cheat knows how to enable or disable itself)
 
70
  if(enable)
 
71
    cheat->enable();
 
72
  else
 
73
    cheat->disable();
 
74
 
 
75
  return cheat;
 
76
}
 
77
 
 
78
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
79
void CheatManager::remove(int idx)
 
80
{
 
81
  if((unsigned int)idx >= myCheatList.size())
 
82
    return;
 
83
 
 
84
  Cheat* c = myCheatList[idx];
 
85
 
 
86
  // First remove it from the per-frame list
 
87
  addPerFrame(c, false);
 
88
 
 
89
  // Then remove it from the cheatlist entirely
 
90
  myCheatList.remove_at(idx);
 
91
  delete c;
 
92
}
 
93
 
 
94
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
95
void CheatManager::addPerFrame(Cheat* cheat, bool enable)
 
96
{
 
97
  if(!cheat)
 
98
    return;
 
99
 
 
100
  // Make sure there are no duplicates
 
101
  bool found = false;
 
102
  unsigned int i;
 
103
  for(i = 0; i < myPerFrameList.size(); i++)
 
104
  {
 
105
    if(myPerFrameList[i]->code() == cheat->code())
 
106
    {
 
107
      found = true;
 
108
      break;
 
109
    }
 
110
  }
 
111
 
 
112
  if(enable)
 
113
  {
 
114
    if(!found)
 
115
      myPerFrameList.push_back(cheat);
 
116
  }
 
117
  else
 
118
  {
 
119
    if(found)
 
120
      myPerFrameList.remove_at(i);
 
121
  }
 
122
}
 
123
 
 
124
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
125
void CheatManager::addOneShot(const string& name, const string& code)
 
126
{
 
127
  Cheat* cheat = (Cheat*) createCheat(name, code);
 
128
  if(!cheat)
 
129
    return;
 
130
 
 
131
  // Evaluate this cheat once, and then immediately delete it
 
132
  cheat->evaluate();
 
133
  delete cheat;
 
134
}
 
135
 
 
136
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
137
const Cheat* CheatManager::createCheat(const string& name, const string& code)
 
138
{
 
139
  if(!isValidCode(code))
 
140
    return NULL;
 
141
 
 
142
  // Create new cheat based on string length
 
143
  switch(code.size())
 
144
  {
 
145
    case 4:
 
146
      return new RamCheat(myOSystem, name, code);
 
147
      break;
 
148
 
 
149
    case 6:
 
150
      return new CheetahCheat(myOSystem, name, code);
 
151
      break;
 
152
 
 
153
    case 8:
 
154
      return new BankRomCheat(myOSystem, name, code);
 
155
      break;
 
156
 
 
157
    default:
 
158
      return NULL;
 
159
  }
 
160
}
 
161
 
 
162
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
163
void CheatManager::parse(const string& cheats)
 
164
{
 
165
  StringList s;
 
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;
 
169
 
 
170
  // Split string by comma, getting each cheat
 
171
  while(string::npos != pos || string::npos != lastPos)
 
172
  {
 
173
    // Get the next cheat
 
174
    cheat = cheats.substr(lastPos, pos - lastPos);
 
175
 
 
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)
 
180
    {
 
181
      s.push_back(cheat.substr(lastColonPos, colonPos - lastColonPos));
 
182
      lastColonPos = cheat.find_first_not_of(":", colonPos);
 
183
      colonPos     = cheat.find_first_of(":", lastColonPos);
 
184
    }
 
185
 
 
186
    // Account for variable number of items specified for cheat
 
187
    switch(s.size())
 
188
    {
 
189
      case 1:
 
190
        name = s[0];
 
191
        code = name;
 
192
        add(name, code, true);
 
193
        break;
 
194
 
 
195
      case 2:
 
196
        name = s[0];
 
197
        code = s[1];
 
198
        add(name, code, true);
 
199
        break;
 
200
 
 
201
      case 3:
 
202
        name = s[0];
 
203
        code = s[1];
 
204
        add(name, code, s[2] == "1");
 
205
        break;
 
206
    }
 
207
    s.clear();
 
208
 
 
209
    lastPos = cheats.find_first_not_of(",", pos);
 
210
    pos     = cheats.find_first_of(",", lastPos);
 
211
  }
 
212
}
 
213
 
 
214
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
215
void CheatManager::enable(const string& code, bool enable)
 
216
{
 
217
  for(unsigned int i = 0; i < myCheatList.size(); i++)
 
218
  {
 
219
    if(myCheatList[i]->code() == code)
 
220
    {
 
221
      if(enable)
 
222
        myCheatList[i]->enable();
 
223
      else
 
224
        myCheatList[i]->disable();
 
225
      break;
 
226
    }
 
227
  }
 
228
}
 
229
 
 
230
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
231
void CheatManager::loadCheatDatabase()
 
232
{
 
233
  string cheatfile = myOSystem->baseDir() + BSPF_PATH_SEPARATOR + "stella.cht";
 
234
  ifstream in(cheatfile.c_str(), ios::in);
 
235
  if(!in)
 
236
    return;
 
237
 
 
238
  string line, md5, cheat;
 
239
  string::size_type one, two, three, four;
 
240
 
 
241
  // Loop reading cheats
 
242
  while(getline(in, line))
 
243
  {
 
244
    if(line.length() == 0)
 
245
      continue;
 
246
 
 
247
    one = line.find("\"", 0);
 
248
    two = line.find("\"", one + 1);
 
249
    three = line.find("\"", two + 1);
 
250
    four = line.find("\"", three + 1);
 
251
 
 
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))
 
255
      break;
 
256
 
 
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);
 
260
 
 
261
    myCheatMap.insert(make_pair(md5, cheat));
 
262
  }
 
263
 
 
264
  in.close();
 
265
  myListIsDirty = false;
 
266
}
 
267
 
 
268
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
269
void CheatManager::saveCheatDatabase()
 
270
{
 
271
  if(!myListIsDirty)
 
272
    return;
 
273
 
 
274
  string cheatfile = myOSystem->baseDir() + BSPF_PATH_SEPARATOR + "stella.cht";
 
275
  ofstream out(cheatfile.c_str(), ios::out);
 
276
  if(!out)
 
277
    return;
 
278
 
 
279
  CheatCodeMap::iterator iter;
 
280
  for(iter = myCheatMap.begin(); iter != myCheatMap.end(); ++iter)
 
281
    out << "\"" << iter->first << "\" "
 
282
        << "\"" << iter->second << "\""
 
283
        << endl;
 
284
 
 
285
  out.close();
 
286
}
 
287
 
 
288
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
289
void CheatManager::loadCheats(const string& md5sum)
 
290
{
 
291
  clear();
 
292
  myCurrentCheat = "";
 
293
 
 
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");
 
297
  if(cheats != "")
 
298
    myOSystem->settings().setString("cheat", "");
 
299
 
 
300
  CheatCodeMap::iterator iter = myCheatMap.find(md5sum);
 
301
  if(iter == myCheatMap.end() && cheats == "")
 
302
    return;
 
303
 
 
304
  // Remember the cheats for this ROM
 
305
  myCurrentCheat = iter->second;
 
306
 
 
307
  // Parse the cheat list, constructing cheats and adding them to the manager
 
308
  parse(iter->second + cheats);
 
309
}
 
310
 
 
311
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
312
void CheatManager::saveCheats(const string& md5sum)
 
313
{
 
314
  ostringstream cheats;
 
315
  for(unsigned int i = 0; i < myCheatList.size(); i++)
 
316
  {
 
317
    cheats << myCheatList[i]->name() << ":"
 
318
           << myCheatList[i]->code() << ":"
 
319
           << myCheatList[i]->enabled();
 
320
    if(i+1 < myCheatList.size())
 
321
      cheats << ",";
 
322
  }
 
323
 
 
324
  bool changed = cheats.str() != myCurrentCheat;
 
325
 
 
326
  // Only update the list if absolutely necessary
 
327
  if(changed)
 
328
  {
 
329
    CheatCodeMap::iterator iter = myCheatMap.find(md5sum);
 
330
 
 
331
    // Erase old entry and add a new one only if it's changed
 
332
    if(iter != myCheatMap.end())
 
333
      myCheatMap.erase(iter);
 
334
 
 
335
    // Add new entry only if there are any cheats defined
 
336
    if(cheats.str() != "")
 
337
      myCheatMap.insert(make_pair(md5sum, cheats.str()));
 
338
  }
 
339
 
 
340
  // Update the dirty flag
 
341
  myListIsDirty = myListIsDirty || changed;
 
342
}
 
343
 
 
344
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
345
void CheatManager::clear()
 
346
{
 
347
  // Don't delete the items from per-frame list, since it will be done in
 
348
  // the following loop
 
349
  myPerFrameList.clear();
 
350
 
 
351
  for(unsigned int i = 0; i < myCheatList.size(); i++)
 
352
    delete myCheatList[i];
 
353
  myCheatList.clear();
 
354
}
 
355
 
 
356
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 
357
bool CheatManager::isValidCode(const string& code)
 
358
{
 
359
  for(unsigned int i = 0; i < code.size(); i++)
 
360
    if(!isxdigit(code[i]))
 
361
      return false;
 
362
 
 
363
  int length = code.length();
 
364
  return (length == 4 || length == 6 || length == 8);
 
365
}