adding memorystream. fixed savestates.

This commit is contained in:
zeromus 2008-06-03 04:04:04 +00:00
parent b89ba6a9ca
commit c208a2012d
5 changed files with 289 additions and 31 deletions

View File

@ -4,29 +4,6 @@
#include <iostream>
#include <sstream>
class sane_stringbuf : public std::stringbuf
{
public:
friend class memorystream;
};
class memorystream : public std::basic_iostream<char, std::char_traits<char> >
{
public:
memorystream()
: std::basic_iostream<char, std::char_traits<char> >(&_Stringbuffer)
{ // construct empty character buffer
}
sane_stringbuf _Stringbuffer;
friend class sane_stringbuf;
public:
char* buf() { return _Stringbuffer.pbase(); }
int size() { return tellp(); }
};
extern int fceuindbg;
void ResetGameLoaded(void);

View File

@ -18,8 +18,12 @@
#include "video.h"
#include "movie.h"
#include "utils/memory.h"
#include "utils/memorystream.h"
#include "utils/xstring.h"
using namespace std;
#define MOVIE_VERSION 3 // still at 2 since the format itself is still compatible - to detect which version the movie was made with, check the fceu version stored in the movie header (e.g against FCEU_VERSION_NUMERIC)
extern char FileBase[];
@ -90,6 +94,42 @@ void MovieData::TryDumpIncremental()
}
const char MovieRecord::mnemonics[8] = {'A','B','S','T','U','D','L','R'};
void MovieRecord::dump(std::ostream* os, int index)
{
//todo: if we want frame numbers in the output (which we dont since we couldnt cut and paste in movies)
//but someone would need to change the parser to ignore it
//fputc('|',fp);
//fprintf(fp,"%08d",index);
os->put('|');
//for each joystick
for(int i=0;i<4;i++)
{
//these are mnemonics for each joystick bit.
//since we usually use the regular joypad, these will be more helpful.
//but any character other than ' ' should count as a set bit
//maybe other input types will need to be encoded another way..
for(int bit=7;bit>=0;bit--)
{
uint8 &joystate = joysticks[i];
int bitmask = (1<<bit);
char mnemonic = mnemonics[bit];
//if the bit is set write the mnemonic
if(joystate & bitmask)
os->put(mnemonic);
else //otherwise write a space
os->put(' ');
}
//separate the joysticks
if(i != 3) os->put('|');
}
//each frame is on a new line
os->put('\n');
}
void MovieRecord::dump(FILE* fp, int index)
{
//todo: if we want frame numbers in the output (which we dont since we couldnt cut and paste in movies)
@ -170,8 +210,18 @@ void MovieData::installDictionary(TDictionary& dictionary)
void MovieData::dump(std::ostream *os)
{
*os << "version " << version;
*os << "emuVersion " << emuVersion;
*os << "version " << version << endl;
*os << "emuVersion " << emuVersion << endl;
*os << "recordCount " << recordCount << endl;
*os << "palFlag " << (palFlag?1:0) << endl;
*os << "poweronFlag " << (poweronFlag?1:0) << endl;
*os << "resetFlag " << (resetFlag?1:0) << endl;
*os << "romFilename " << romFilename << endl;
*os << "romChecksum " << BytesToString(romChecksum.data,MD5DATA::size) << endl;
if(savestate.size() != 0)
*os << "savestate " << BytesToString(&savestate[0],savestate.size()) << endl;
for(int i=0;i<(int)records.size();i++)
records[i].dump(os,i);
}
void MovieData::dump(FILE *fp)
@ -193,11 +243,9 @@ void MovieData::dump(FILE *fp)
int MovieData::dumpLen()
{
FILE* tmp = tmpfile();
dump(tmp);
int len = ftell(tmp);
fclose(tmp);
return len;
memorystream ms;
dump(&ms);
return ms.size();
}

View File

@ -28,7 +28,6 @@
//#include <unistd.h> //mbg merge 7/17/06 removed
#include <vector>
#include <sstream>
#include "types.h"
#include "x6502.h"
@ -36,6 +35,7 @@
#include "sound.h"
#include "utils/endian.h"
#include "utils/memory.h"
#include "utils/memorystream.h"
#include "file.h"
#include "fds.h"
#include "state.h"
@ -379,13 +379,19 @@ bool FCEUSS_SaveFP(FILE *st, int compressionLevel)
FCEUPPU_SaveState();
FCEUSND_SaveState();
totalsize=WriteStateChunk(os,1,SFCPU);
ms.sync();
totalsize+=WriteStateChunk(os,2,SFCPUC);
ms.sync();
totalsize+=WriteStateChunk(os,3,FCEUPPU_STATEINFO);
ms.sync();
totalsize+=WriteStateChunk(os,4,FCEUCTRL_STATEINFO);
ms.sync();
totalsize+=WriteStateChunk(os,5,FCEUSND_STATEINFO);
ms.sync();
if(FCEUI_IsMovieActive())
{
totalsize+=WriteStateChunk(os,6,FCEUMOV_STATEINFO);
ms.sync();
uint32 size = FCEUMOV_WriteState((std::ostream*)0);
os->put(7);
write32le(size, os);
@ -400,6 +406,7 @@ bool FCEUSS_SaveFP(FILE *st, int compressionLevel)
write32le(size, os);
os->write((char*)XBackBuf,size);
totalsize += 5 + size;
ms.sync();
}
if(SPreSave) SPreSave();
@ -409,6 +416,13 @@ bool FCEUSS_SaveFP(FILE *st, int compressionLevel)
//save the length of the file
int len = ms.size();
//sanity check: len and totalsize should be the same
if(len != totalsize)
{
FCEUD_PrintError("sanity violation: len != totalsize");
return false;
}
int error = Z_OK;
uint8* cbuf = (uint8*)ms.buf();
uLongf comprlen = -1;

215
src/utils/memorystream.h Normal file
View File

@ -0,0 +1,215 @@
#include <iostream>
#include <vector>
#include <sstream>
class memory_streambuf: public std::streambuf {
private:
friend class memorystream;
//the current buffer
char* buf;
//the current allocated capacity of the buffer
size_t capacity;
//whether the sequence is owned by the stringbuf
bool myBuf;
//the logical length of the buffer
size_t length;
//the current 'write window' starting position within the buffer for writing.
size_t ww;
public:
//the logical length of the buffer
size_t size()
{
sync();
return length;
}
memory_streambuf()
: length(0)
, myBuf(true)
, ww(0)
, buf(new char[capacity = 8])
{
sync();
}
~memory_streambuf()
{
//only cleanup if we own the seq
if(myBuf) delete[] buf;
}
//to avoid copying, rebuilds the provided vector and copies the streambuf contents into it
void toVector(std::vector<char>& out)
{
out.reserve(length);
memcpy(&out[0],buf,length);
}
//maybe the compiler can avoid copying, but maybe not: returns a vector representing the current streambuf
std::vector<char> toVector()
{
return std::vector<char>(buf,buf+length);
}
//constructs a non-expandable streambuf around the provided buffer
memory_streambuf(char* usebuf, int buflength)
: length(buflength)
, myBuf(false)
, buf(usebuf)
, ww(0)
{
sync();
}
//tells the current read or write position
std::streampos tell(std::ios::openmode which)
{
if(which == std::ios::in)
return tellRead();
else if(which == std::ios::out)
return tellWrite();
else return -1;
}
//tells the current read position
std::streampos tellRead()
{
return gptr()-eback();
}
//tells the current write position
std::streampos tellWrite()
{
return pptr()-pbase() + ww;
}
int sync()
{
dosync(-1);
return 0;
}
char* getbuf()
{
sync();
return buf;
}
private:
void dosync(int c)
{
size_t wp = tellWrite();
size_t rp = tellRead();
//if we are supposed to insert a character..
if(c != -1)
{
buf[wp] = c;
wp++;
}
//the length is determined by the highest character that was ever inserted
length = std::max(length,wp);
//the write window advances to begin at the current write insertion point
ww = wp;
//set the new write and read windows
setp(buf+ww, buf + capacity);
setg(buf, buf+rp, buf + length);
}
void expand(size_t upto)
{
if(!myBuf)
throw new std::exception("memory_streambuf is not expandable");
size_t newcapacity;
if(upto == -1)
newcapacity = capacity + capacity/2 + 2;
else
newcapacity = std::max(upto,capacity);
if(newcapacity == capacity) return;
char* newbuf = new char[newcapacity];
memcpy(newbuf,buf,capacity);
delete[] buf;
capacity = newcapacity;
buf = newbuf;
}
protected:
int overflow(int c)
{
expand(-1);
dosync(c);
return 1;
}
std::streambuf::pos_type seekpos(pos_type pos, std::ios::openmode which)
{
//extend if we are seeking the write cursor
if(which & std::ios_base::out)
expand(pos);
sync();
if(which & std::ios_base::in)
setg(buf, buf+pos, buf + length);
if(which & std::ios_base::out)
setp(buf+pos, buf + capacity);
return pos;
}
pos_type seekoff(off_type off, std::ios::seekdir way, std::ios::openmode which)
{
switch(way) {
case std::ios::beg:
return seekpos(off, which);
case std::ios::cur:
return seekpos(tell(which)+off, which);
case std::ios::end:
return seekpos(length+off, which);
default:
return -1;
}
}
};
//an iostream that uses the memory_streambuf to effectively act much like a c# memorystream
//please call sync() after writing data if you want to read it back
class memorystream : public std::basic_iostream<char, std::char_traits<char> >
{
public:
memorystream()
: std::basic_iostream<char, std::char_traits<char> >(&streambuf)
{
}
//the underlying memory_streambuf
memory_streambuf streambuf;
public:
size_t size() { return streambuf.size(); }
char* buf() { return streambuf.getbuf(); }
//flushes all the writing state and ensures the stream is ready for reading
void sync() { streambuf.sync(); }
//rewinds the cursors to offset 0
void rewind() { streambuf.seekpos(0,std::ios::in | std::ios::out); }
};

View File

@ -2155,6 +2155,10 @@
RelativePath="..\src\utils\memory.h"
>
</File>
<File
RelativePath="..\src\utils\memorystream.h"
>
</File>
<File
RelativePath="..\src\utils\unzip.cpp"
>