StateManager.cxx 10.7 KB
Newer Older
1
2
//============================================================================
//
3
4
5
//   SSSS    tt          lll  lll
//  SS  SS   tt           ll   ll
//  SS     tttttt  eeee   ll   ll   aaaa
6
7
8
9
10
//   SSSS    tt   ee  ee  ll   ll      aa
//      SS   tt   eeeeee  ll   ll   aaaaa  --  "An Atari 2600 VCS Emulator"
//  SS  SS   tt   ee      ll   ll  aa  aa
//   SSSS     ttt  eeeee llll llll  aaaaa
//
11
// Copyright (c) 1995-2018 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
16
17
18
19
20
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================

#include "OSystem.hxx"
#include "Settings.hxx"
#include "Console.hxx"
21
#include "Cart.hxx"
22
23
#include "Control.hxx"
#include "Switches.hxx"
24
#include "System.hxx"
25
#include "Serializable.hxx"
26
#include "RewindManager.hxx"
27
28
29

#include "StateManager.hxx"

30
#define STATE_HEADER "06000000state"
31
// #define MOVIE_HEADER "03030000movie"
32

33
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
34
StateManager::StateManager(OSystem& osystem)
35
  : myOSystem(osystem),
36
    myCurrentSlot(0),
37
    myActiveMode(Mode::Off)
38
{
39
  myRewindManager = make_unique<RewindManager>(myOSystem, *this);
40
41
42
  reset();
}

43
44
45
46
47
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
StateManager::~StateManager()
{
}

48
#if 0
49
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
50
void StateManager::toggleRecordMode()
51
52
53
54
55
{
  if(myActiveMode != kMovieRecordMode)  // Turn on movie record mode
  {
    myActiveMode = kOffMode;

56
    string moviefile = /*myOSystem.baseDir() +*/ "test.inp";
57
58
59
60
61
62
    if(myMovieWriter.isOpen())
      myMovieWriter.close();
    if(!myMovieWriter.open(moviefile))
      return false;

    // Prepend the ROM md5 so this state file only works with that ROM
63
    myMovieWriter.putString(myOSystem.console().properties().get(Cartridge_MD5));
64

65
    if(!myOSystem.console().save(myMovieWriter))
66
67
68
69
70
71
72
      return false;

    // Save controller types for this ROM
    // We need to check this, since some controllers save more state than
    // normal, and those states files wouldn't be compatible with normal
    // controllers.
    myMovieWriter.putString(
73
      myOSystem.console().controller(Controller::Left).name());
74
    myMovieWriter.putString(
75
      myOSystem.console().controller(Controller::Right).name());
76
77
78
79
80
81
82
83
84
85
86
87

    // If we get this far, we're really in movie record mode
    myActiveMode = kMovieRecordMode;
  }
  else  // Turn off movie record mode
  {
    myActiveMode = kOffMode;
    myMovieWriter.close();
    return false;
  }

  return myActiveMode == kMovieRecordMode;
88
89
////////////////////////////////////////////////////////
// FIXME - For now, I'm going to use this to activate movie playback
90
91
92
93
94
95
96
  // Close the writer, since we're about to re-open in read mode
  myMovieWriter.close();

  if(myActiveMode != kMoviePlaybackMode)  // Turn on movie playback mode
  {
    myActiveMode = kOffMode;

97
    string moviefile = /*myOSystem.baseDir() + */ "test.inp";
98
99
100
101
102
103
104
    if(myMovieReader.isOpen())
      myMovieReader.close();
    if(!myMovieReader.open(moviefile))
      return false;

    // Check the ROM md5
    if(myMovieReader.getString() !=
105
       myOSystem.console().properties().get(Cartridge_MD5))
106
107
      return false;

108
    if(!myOSystem.console().load(myMovieReader))
109
110
111
112
113
114
      return false;

    // Check controller types
    const string& left  = myMovieReader.getString();
    const string& right = myMovieReader.getString();

115
116
    if(left != myOSystem.console().controller(Controller::Left).name() ||
       right != myOSystem.console().controller(Controller::Right).name())
117
118
119
120
121
122
123
124
125
126
127
      return false;

    // If we get this far, we're really in movie record mode
    myActiveMode = kMoviePlaybackMode;
  }
  else  // Turn off movie playback mode
  {
    myActiveMode = kOffMode;
    myMovieReader.close();
    return false;
  }
128
}
129
#endif
130
131

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
thrust26's avatar
thrust26 committed
132
void StateManager::toggleTimeMachine()
133
{
134
135
  bool devSettings = myOSystem.settings().getBool("dev.settings");

thrust26's avatar
thrust26 committed
136
137
138
  myActiveMode = myActiveMode == Mode::TimeMachine ? Mode::Off : Mode::TimeMachine;
  if(myActiveMode == Mode::TimeMachine)
    myOSystem.frameBuffer().showMessage("Time Machine enabled");
139
  else
thrust26's avatar
thrust26 committed
140
141
    myOSystem.frameBuffer().showMessage("Time Machine disabled");
  myOSystem.settings().setValue(devSettings ? "dev.timemachine" : "plr.timemachine", myActiveMode == Mode::TimeMachine);
142
143
}

144
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
145
bool StateManager::addExtraState(const string& message)
146
{
147
148
149
150
151
152
  if(myActiveMode == Mode::TimeMachine)
  {
    RewindManager& r = myOSystem.state().rewindManager();
    return r.addState(message);
  }
  return false;
153
154
155
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
156
bool StateManager::rewindStates(uInt32 numStates)
157
158
{
  RewindManager& r = myOSystem.state().rewindManager();
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
  return r.rewindStates(numStates);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool StateManager::unwindStates(uInt32 numStates)
{
  RewindManager& r = myOSystem.state().rewindManager();
  return r.unwindStates(numStates);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool StateManager::windStates(uInt32 numStates, bool unwind)
{
  RewindManager& r = myOSystem.state().rewindManager();
  return r.windStates(numStates, unwind);
174
175
}

176
177
178
179
180
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StateManager::update()
{
  switch(myActiveMode)
  {
thrust26's avatar
thrust26 committed
181
182
    case Mode::TimeMachine:
      myRewindManager->addState("Time Machine", true);
183
184
185
186
      break;

#if 0
    case Mode::MovieRecord:
187
188
189
      myOSystem.console().controller(Controller::Left).save(myMovieWriter);
      myOSystem.console().controller(Controller::Right).save(myMovieWriter);
      myOSystem.console().switches().save(myMovieWriter);
190
191
      break;

192
    case Mode::MoviePlayback:
193
194
195
      myOSystem.console().controller(Controller::Left).load(myMovieReader);
      myOSystem.console().controller(Controller::Right).load(myMovieReader);
      myOSystem.console().switches().load(myMovieReader);
196
      break;
197
#endif
198
199
200
201
202
    default:
      break;
  }
}

203
204
205
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StateManager::loadState(int slot)
{
206
  if(myOSystem.hasConsole())
207
208
209
210
  {
    if(slot < 0) slot = myCurrentSlot;

    ostringstream buf;
211
212
    buf << myOSystem.stateDir()
        << myOSystem.console().properties().get(Cartridge_Name)
213
        << ".st" << slot;
214

215
216
    // Make sure the file can be opened in read-only mode
    Serializer in(buf.str(), true);
217
    if(!in)
218
219
    {
      buf.str("");
220
      buf << "Can't open/load from state file " << slot;
221
      myOSystem.frameBuffer().showMessage(buf.str());
222
223
224
      return;
    }

225
226
    // First test if we have a valid header
    // If so, do a complete state load using the Console
227
    buf.str("");
228
    try
229
    {
230
231
      if(in.getString() != STATE_HEADER)
        buf << "Incompatible state " << slot << " file";
232
233
234
235
236
237
238
      else
      {
        if(myOSystem.console().load(in))
          buf << "State " << slot << " loaded";
        else
          buf << "Invalid data in state " << slot << " file";
      }
239
    }
240
241
    catch(...)
    {
242
      buf << "Invalid data in state " << slot << " file";
243
    }
244

245
    myOSystem.frameBuffer().showMessage(buf.str());
246
247
248
249
250
251
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StateManager::saveState(int slot)
{
252
  if(myOSystem.hasConsole())
253
254
255
256
  {
    if(slot < 0) slot = myCurrentSlot;

    ostringstream buf;
257
258
    buf << myOSystem.stateDir()
        << myOSystem.console().properties().get(Cartridge_Name)
259
        << ".st" << slot;
260
261

    // Make sure the file can be opened for writing
262
    Serializer out(buf.str());
263
    if(!out)
264
    {
265
266
      buf.str("");
      buf << "Can't open/save to state file " << slot;
267
      myOSystem.frameBuffer().showMessage(buf.str());
268
269
270
      return;
    }

271
272
273
274
275
276
277
278
279
280
281
282
    try
    {
      // Add header so that if the state format changes in the future,
      // we'll know right away, without having to parse the rest of the file
      out.putString(STATE_HEADER);
    }
    catch(...)
    {
      buf << "Error saving state " << slot;
      myOSystem.frameBuffer().showMessage(buf.str());
      return;
    }
283
284

    // Do a complete state save using the Console
285
    buf.str("");
286
    if(myOSystem.console().save(out))
287
288
    {
      buf << "State " << slot << " saved";
289
      if(myOSystem.settings().getBool("autoslot"))
290
291
292
293
294
295
296
297
      {
        myCurrentSlot = (slot + 1) % 10;
        buf << ", switching to slot " << slot;
      }
    }
    else
      buf << "Error saving state " << slot;

298
    myOSystem.frameBuffer().showMessage(buf.str());
299
300
301
302
303
304
305
306
307
308
309
  }
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StateManager::changeState()
{
  myCurrentSlot = (myCurrentSlot + 1) % 10;

  // Print appropriate message
  ostringstream buf;
  buf << "Changed to slot " << myCurrentSlot;
310
  myOSystem.frameBuffer().showMessage(buf.str());
311
312
}

313
314
315
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool StateManager::loadState(Serializer& in)
{
316
  try
317
  {
318
    if(myOSystem.hasConsole())
319
    {
320
      // Make sure the file can be opened for reading
321
      if(in)
322
      {
323
        // First test if we have a valid header
324
325
326
327
        // If so, do a complete state load using the Console
        return in.getString() == STATE_HEADER &&
               myOSystem.console().load(in);
      }
328
329
    }
  }
330
331
332
333
  catch(...)
  {
    cerr << "ERROR: StateManager::loadState(Serializer&)" << endl;
  }
334
335
336
337
338
339
  return false;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool StateManager::saveState(Serializer& out)
{
340
  try
341
  {
342
    if(myOSystem.hasConsole())
343
    {
344
      // Make sure the file can be opened for writing
345
      if(out)
346
347
348
349
      {
        // Add header so that if the state format changes in the future,
        // we'll know right away, without having to parse the rest of the file
        out.putString(STATE_HEADER);
350

351
        // Do a complete state save using the Console
352
        if(myOSystem.console().save(out))
353
354
          return true;
      }
355
356
    }
  }
357
  catch(...)
358
  {
359
    cerr << "ERROR: StateManager::saveState(Serializer&)" << endl;
360
  }
361
362
363
  return false;
}

364
365
366
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StateManager::reset()
{
367
  myRewindManager->clear();
thrust26's avatar
thrust26 committed
368
  myActiveMode = myOSystem.settings().getBool(
thrust26's avatar
thrust26 committed
369
    myOSystem.settings().getBool("dev.settings") ? "dev.timemachine" : "plr.timemachine") ? Mode::TimeMachine : Mode::Off;
370

371
#if 0
372
  myCurrentSlot = 0;
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387

  switch(myActiveMode)
  {
    case kMovieRecordMode:
      myMovieWriter.close();
      break;

    case kMoviePlaybackMode:
      myMovieReader.close();
      break;

    default:
      break;
  }
  myActiveMode = kOffMode;
388
#endif
389
}