Changed Serializer/Deserializer API in preparation for event recording.

Pressing 'Ctrl e' now starts/stops event recording (key is temporary and
will probably change).  An state file is then generated containing the
state when recording started, and the eventstream up to the point when
recording stopped.

Next I'll work on loading the event state file and replaying the events.
This one is a little harder, since it has to override EventHandler::poll()
and use events from the eventstream.  Hopefully rudimentary support will
be there by the end of the weekend.


git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@906 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba
This commit is contained in:
stephena 2005-12-09 19:09:49 +00:00
parent a1c490cd21
commit 3b5cd56ca4
11 changed files with 215 additions and 158 deletions

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Array.hxx,v 1.1 2005-12-09 01:16:13 stephena Exp $
// $Id: Array.hxx,v 1.2 2005-12-09 19:09:49 stephena Exp $
//
// Based on code from ScummVM - Scumm Interpreter
// Copyright (C) 2002-2004 The ScummVM project
@ -45,7 +45,7 @@ class Array
Array<T>(const Array<T>& array) : _capacity(0), _size(0), _data(0)
{
_size = array._size;
_capacity = _size + 32;
_capacity = _size + 128;
_data = new T[_capacity];
for(int i = 0; i < _size; i++)
_data[i] = array._data[i];
@ -115,7 +115,7 @@ class Array
if (_data)
delete [] _data;
_size = array._size;
_capacity = _size + 32;
_capacity = _size + 128;
_data = new T[_capacity];
for(int i = 0; i < _size; i++)
_data[i] = array._data[i];
@ -177,7 +177,7 @@ class Array
return;
T *old_data = _data;
_capacity = new_len + 32;
_capacity = new_len + 128;
_data = new T[_capacity];
if (old_data)

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Deserializer.cxx,v 1.5 2005-10-29 18:11:29 stephena Exp $
// $Id: Deserializer.cxx,v 1.6 2005-12-09 19:09:49 stephena Exp $
//============================================================================
#include <iostream>
@ -43,7 +43,7 @@ bool Deserializer::open(const string& fileName)
close();
myStream = new ifstream(fileName.c_str(), ios::in | ios::binary);
return (myStream && myStream->is_open());
return isOpen();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -59,6 +59,12 @@ void Deserializer::close(void)
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Deserializer::isOpen(void)
{
return myStream && myStream->is_open();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
long Deserializer::getLong(void)
{

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Deserializer.hxx,v 1.6 2005-06-16 01:11:27 stephena Exp $
// $Id: Deserializer.hxx,v 1.7 2005-12-09 19:09:49 stephena Exp $
//============================================================================
#ifndef DESERIALIZER_HXX
@ -32,7 +32,7 @@
return.
@author Stephen Anthony
@version $Id: Deserializer.hxx,v 1.6 2005-06-16 01:11:27 stephena Exp $
@version $Id: Deserializer.hxx,v 1.7 2005-12-09 19:09:49 stephena Exp $
*/
class Deserializer
{
@ -65,6 +65,11 @@ class Deserializer
*/
void close(void);
/**
Answers whether the deserializer is currently opened
*/
bool isOpen(void);
/**
Reads a long value from the current input stream.

View File

@ -13,15 +13,16 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Event.cxx,v 1.4 2005-12-09 01:16:13 stephena Exp $
// $Id: Event.cxx,v 1.5 2005-12-09 19:09:49 stephena Exp $
//============================================================================
#include "Event.hxx"
#include "Serializer.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event::Event()
: myNumberOfTypes(Event::LastType),
myEventRecordFlag(true)
myEventRecordFlag(false)
{
// Set all of the events to 0 / false to start with
clear();
@ -30,27 +31,6 @@ Event::Event()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Event::~Event()
{
int events = 0, waits = 0, totalwaits = 0;
cerr << "Event history contains " << myEventHistory.size()/2 << " events\n";
for(unsigned int i = 0; i < myEventHistory.size(); ++i)
{
int tmp = myEventHistory[i];
if(tmp < 0)
{
++waits;
totalwaits += -tmp;
}
else
++events;
cerr << tmp << " ";
}
cerr << endl
<< "events pairs = " << events/2
<< ", frame waits = " << waits
<< ", total frame waits = " << totalwaits
<< endl;
myEventHistory.clear();
}
@ -106,3 +86,15 @@ void Event::nextFrame()
myEventHistory.push_back(-1);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Event::save(Serializer& out)
{
int size = myEventHistory.size();
out.putString("EventStream");
out.putLong(size);
for(int i = 0; i < size; ++i)
out.putLong(myEventHistory[i]);
return true;
}

View File

@ -13,20 +13,21 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Event.hxx,v 1.14 2005-12-09 01:16:13 stephena Exp $
// $Id: Event.hxx,v 1.15 2005-12-09 19:09:49 stephena Exp $
//============================================================================
#ifndef EVENT_HXX
#define EVENT_HXX
class Event;
class Serializer;
#include "Array.hxx"
#include "bspf.hxx"
/**
@author Bradford W. Mott
@version $Id: Event.hxx,v 1.14 2005-12-09 01:16:13 stephena Exp $
@version $Id: Event.hxx,v 1.15 2005-12-09 19:09:49 stephena Exp $
*/
class Event
{
@ -105,11 +106,6 @@ class Event
*/
virtual void clear();
/**
Returns the history for this event
*/
virtual const IntArray& history() { return myEventHistory; }
/**
Start/stop recording events to the event history
@ -122,6 +118,19 @@ class Event
*/
virtual void nextFrame();
/**
Answers if we're in recording mode
*/
virtual bool isRecording() { return myEventRecordFlag; }
/**
Saves the current event history to the given Serializer
@param out The serializer device to save to.
@return The result of the save. True on success, false on failure.
*/
virtual bool save(Serializer& out);
protected:
// Number of event types there are
const Int32 myNumberOfTypes;

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: EventHandler.cxx,v 1.124 2005-12-09 01:16:13 stephena Exp $
// $Id: EventHandler.cxx,v 1.125 2005-12-09 19:09:49 stephena Exp $
//============================================================================
#include <algorithm>
@ -33,6 +33,8 @@
#include "CommandMenu.hxx"
#include "Launcher.hxx"
#include "GuiUtils.hxx"
#include "Deserializer.hxx"
#include "Serializer.hxx"
#include "bspf.hxx"
#ifdef DEVELOPER_SUPPORT
@ -139,6 +141,8 @@ EventHandler::EventHandler(OSystem* osystem)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
EventHandler::~EventHandler()
{
stopRecording();
if(myEvent)
delete myEvent;
@ -524,6 +528,18 @@ void EventHandler::poll(uInt32 time)
myOSystem->createConsole();
break;
// FIXME - these will be removed when a UI is added for event recording
case SDLK_e: // Ctrl-e starts/stops event recording
if(myEvent->isRecording())
stopRecording();
else
startRecording();
break;
case SDLK_l: // Ctrl-l loads a recording
loadRecording();
break;
////////////////////////////////////////////////////////////////////////
case SDLK_END: // Ctrl-End increases Width
myOSystem->console().changeWidth(1);
break;
@ -1682,22 +1698,26 @@ inline bool EventHandler::eventIsAnalog(Event::Type event)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::saveState()
{
// Do a state save using the System
string md5 = myOSystem->console().properties().get("Cartridge.MD5");
ostringstream buf;
buf << myOSystem->stateDir() << BSPF_PATH_SEPARATOR << md5 << ".st" << myLSState;
int result = myOSystem->console().system().saveState(buf.str(), md5);
// Make sure the file can be opened for writing
Serializer out;
if(!out.open(buf.str()))
{
myOSystem->frameBuffer().showMessage("Error saving state file");
return;
}
// Print appropriate message
// Do a state save using the System
buf.str("");
if(result == 1)
if(myOSystem->console().system().saveState(md5, out))
buf << "State " << myLSState << " saved";
else if(result == 2)
buf << "Error saving state " << myLSState;
else if(result == 3)
else
buf << "Invalid state " << myLSState << " file";
out.close();
myOSystem->frameBuffer().showMessage(buf.str());
}
@ -1723,22 +1743,28 @@ void EventHandler::changeState()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::loadState()
{
// Do a state save using the System
string md5 = myOSystem->console().properties().get("Cartridge.MD5");
ostringstream buf;
buf << myOSystem->stateDir() << BSPF_PATH_SEPARATOR << md5 << ".st" << myLSState;
int result = myOSystem->console().system().loadState(buf.str(), md5);
// Print appropriate message
buf.str("");
if(result == 1)
buf << "State " << myLSState << " loaded";
else if(result == 2)
// Make sure the file can be opened for reading
Deserializer in;
if(!in.open(buf.str()))
{
buf.str("");
buf << "Error loading state " << myLSState;
else if(result == 3)
myOSystem->frameBuffer().showMessage(buf.str());
return;
}
// Do a state load using the System
buf.str("");
if(myOSystem->console().system().loadState(md5, in))
buf << "State " << myLSState << " loaded";
else
buf << "Invalid state " << myLSState << " file";
in.close();
myOSystem->frameBuffer().showMessage(buf.str());
}
@ -1806,6 +1832,50 @@ void EventHandler::takeSnapshot()
#endif
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::startRecording()
{
if(myEvent->isRecording())
return;
string eventfile = /*myOSystem->baseDir() + BSPF_PATH_SEPARATOR +*/ "test.inp";
myEventStream.close();
if(!myEventStream.open(eventfile))
{
myOSystem->frameBuffer().showMessage("Error opening eventstream");
return;
}
// And save the current state to it
string md5 = myOSystem->console().properties().get("Cartridge.MD5");
myOSystem->console().system().saveState(md5, myEventStream);
myEvent->record(true);
myOSystem->frameBuffer().showMessage("Recording started");
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::stopRecording()
{
if(!myEvent->isRecording())
return;
// Append the event history to the eventstream
myEvent->save(myEventStream);
// And reset the state
myEvent->record(false);
myOSystem->frameBuffer().showMessage("Recording stopped");
myEventStream.close();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::loadRecording()
{
cerr << "load recording!\n";
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void EventHandler::setPaddleMode(uInt32 num, bool showmessage)
{

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: EventHandler.hxx,v 1.62 2005-12-08 19:01:38 stephena Exp $
// $Id: EventHandler.hxx,v 1.63 2005-12-09 19:09:49 stephena Exp $
//============================================================================
#ifndef EVENTHANDLER_HXX
@ -26,6 +26,7 @@
#include "Array.hxx"
#include "Control.hxx"
#include "StringList.hxx"
#include "Serializer.hxx"
class Console;
class OSystem;
@ -90,7 +91,7 @@ struct Stella_Joystick {
mapping can take place.
@author Stephen Anthony
@version $Id: EventHandler.hxx,v 1.62 2005-12-08 19:01:38 stephena Exp $
@version $Id: EventHandler.hxx,v 1.63 2005-12-09 19:09:49 stephena Exp $
*/
class EventHandler
{
@ -220,12 +221,27 @@ class EventHandler
/**
Save state to explicit state number (debugger uses this)
*/
void saveState(int state);
void saveState(int state);
/**
Load state from explicit state number (debugger uses this)
*/
void loadState(int state);
void loadState(int state);
/**
Start recording event-stream to disk
*/
void startRecording();
/**
Stop recording event-stream
*/
void stopRecording();
/**
Load recorded event-stream into the system
*/
void loadRecording();
/**
Sets the mouse to act as paddle 'num'
@ -403,6 +419,9 @@ class EventHandler
// Indicates the current state of the system (ie, which mode is current)
State myState;
// The serializer to use for saving eventstreams
Serializer myEventStream;
// Indicates current overlay object
DialogContainer* myOverlay;

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Serializer.cxx,v 1.5 2005-06-16 01:11:28 stephena Exp $
// $Id: Serializer.cxx,v 1.6 2005-12-09 19:09:49 stephena Exp $
//============================================================================
#include <iostream>
@ -43,7 +43,7 @@ bool Serializer::open(const string& fileName)
close();
myStream = new ofstream(fileName.c_str(), ios::out | ios::binary);
return (myStream && myStream->is_open());
return isOpen();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -59,6 +59,12 @@ void Serializer::close(void)
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool Serializer::isOpen(void)
{
return myStream && myStream->is_open();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Serializer::putLong(long value)
{

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: Serializer.hxx,v 1.7 2005-06-16 01:11:28 stephena Exp $
// $Id: Serializer.hxx,v 1.8 2005-12-09 19:09:49 stephena Exp $
//============================================================================
#ifndef SERIALIZER_HXX
@ -33,7 +33,7 @@
Boolean values are written using a special pattern.
@author Stephen Anthony
@version $Id: Serializer.hxx,v 1.7 2005-06-16 01:11:28 stephena Exp $
@version $Id: Serializer.hxx,v 1.8 2005-12-09 19:09:49 stephena Exp $
*/
class Serializer
{
@ -66,6 +66,11 @@ class Serializer
*/
void close(void);
/**
Answers whether the serializer is currently opened
*/
bool isOpen(void);
/**
Writes a long value to the current output stream.

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: System.cxx,v 1.13 2005-07-09 23:44:08 urchlay Exp $
// $Id: System.cxx,v 1.14 2005-12-09 19:09:49 stephena Exp $
//============================================================================
#include <assert.h>
@ -54,10 +54,6 @@ System::System(uInt16 n, uInt16 m)
setPageAccess(page, access);
}
// Set up (de)serializer in case we are asked to save/load state
serializer = new Serializer();
deserializer = new Deserializer();
// Bus starts out unlocked (in other words, peek() changes myDataBusState)
myDataBusLocked = false;
}
@ -76,12 +72,6 @@ System::~System()
// Free my page access table
delete[] myPageAccessTable;
// Free the serializer stuff
if(serializer)
delete serializer;
if(deserializer)
delete deserializer;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -135,11 +125,9 @@ void System::attach(TIA* tia)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool System::save(Serializer& out)
{
string name = "System";
try
{
out.putString(name);
out.putString("System");
out.putLong(myCycles);
}
catch(char *msg)
@ -149,7 +137,7 @@ bool System::save(Serializer& out)
}
catch(...)
{
cerr << "Unknown error in save state for " << name << endl;
cerr << "Unknown error in save state for \'System\'" << endl;
return false;
}
@ -159,11 +147,9 @@ bool System::save(Serializer& out)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
bool System::load(Deserializer& in)
{
string name = "System";
try
{
if(in.getString() != name)
if(in.getString() != "System")
return false;
myCycles = (uInt32) in.getLong();
@ -175,7 +161,7 @@ bool System::load(Deserializer& in)
}
catch(...)
{
cerr << "Unknown error in load state for " << name << endl;
cerr << "Unknown error in load state for \'System\'" << endl;
return false;
}
@ -217,91 +203,58 @@ const System::PageAccess& System::getPageAccess(uInt16 page)
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int System::saveState(const string& fileName, const string& md5sum)
bool System::saveState(const string& md5sum, Serializer& out)
{
// Open the file as a new Serializer
if(!serializer->open(fileName))
{
serializer->close();
return 2;
}
if(!out.isOpen())
return false;
// Prepend the state file with the md5sum of this cartridge
// This is the first defensive check for an invalid state file
serializer->putString(md5sum);
out.putString(md5sum);
// First save state for this system
if(!save(*serializer))
{
serializer->close();
return 3;
}
if(!save(out))
return false;
// Next, save state for the CPU
if(!myM6502->save(*serializer))
{
serializer->close();
return 3;
}
if(!myM6502->save(out))
return false;
// Now save the state of each device
for(uInt32 i = 0; i < myNumberOfDevices; ++i)
{
if(!myDevices[i]->save(*serializer))
{
serializer->close();
return 3;
}
}
if(!myDevices[i]->save(out))
return false;
serializer->close();
return 1; // success
return true; // success
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
int System::loadState(const string &fileName, const string& md5sum)
bool System::loadState(const string& md5sum, Deserializer& in)
{
// Open the file as a new Deserializer
if(!deserializer->open(fileName))
{
deserializer->close();
return 2;
}
if(!in.isOpen())
return false;
// Look at the beginning of the state file. It should contain the md5sum
// of the current cartridge. If it doesn't, this state file is invalid.
if(deserializer->getString() != md5sum)
{
deserializer->close();
return 3;
}
if(in.getString() != md5sum)
return false;
// First load state for this system
if(!load(*deserializer))
{
deserializer->close();
return 3;
}
if(!load(in))
return false;
// Next, load state for the CPU
if(!myM6502->load(*deserializer))
{
deserializer->close();
return 3;
}
if(!myM6502->load(in))
return false;
// Now load the state of each device
for(uInt32 i = 0; i < myNumberOfDevices; ++i)
{
if(!myDevices[i]->load(*deserializer))
{
deserializer->close();
return 3;
}
}
if(!myDevices[i]->load(in))
return false;
deserializer->close();
return 1; // success
return true; // success
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -13,7 +13,7 @@
// See the file "license" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//
// $Id: System.hxx,v 1.11 2005-08-24 22:54:30 stephena Exp $
// $Id: System.hxx,v 1.12 2005-12-09 19:09:49 stephena Exp $
//============================================================================
#ifndef SYSTEM_HXX
@ -47,7 +47,7 @@ class Deserializer;
dynamic code for that page of memory.
@author Bradford W. Mott
@version $Id: System.hxx,v 1.11 2005-08-24 22:54:30 stephena Exp $
@version $Id: System.hxx,v 1.12 2005-12-09 19:09:49 stephena Exp $
*/
class System
{
@ -118,25 +118,23 @@ class System
Saves the current state of Stella to the given file. Calls
save on every device and CPU attached to this system.
@param out The serializer device to save to.
@return The result of the save. Error codes as follows:
1 success
2 file could not be opened for read/write
3 invalid state file
@param md5sum MD5 of the current ROM
@param out The serializer device to save to
@return False on any errors, else true
*/
int saveState(const string& fileName, const string& md5sum);
bool saveState(const string& md5sum, Serializer& out);
/**
Loads the current state of Stella from the given file. Calls
load on every device and CPU attached to this system.
@param in The deserializer device to load from.
@return The result of the load. Error codes as follows:
1 success
2 file could not be opened for read/write
3 invalid state file
@param md5sum MD5 of the current ROM
@param in The deserializer device to load from
@return False on any errors, else true
*/
int loadState(const string& fileName, const string& md5sum);
bool loadState(const string& md5sum, Deserializer& in);
public:
/**
@ -359,12 +357,6 @@ class System
// debugger is active.
bool myDataBusLocked;
// The serializer for the system. Used to save state.
Serializer* serializer;
// The deserializer for the system. Used to load state.
Deserializer* deserializer;
private:
// Copy constructor isn't supported by this class so make it private
System(const System&);