adding memorystream. fixed savestates.
This commit is contained in:
parent
b89ba6a9ca
commit
c208a2012d
23
src/fceu.h
23
src/fceu.h
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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); }
|
||||
};
|
|
@ -2155,6 +2155,10 @@
|
|||
RelativePath="..\src\utils\memory.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\utils\memorystream.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\src\utils\unzip.cpp"
|
||||
>
|
||||
|
|
Loading…
Reference in New Issue