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 <iostream>
|
||||||
#include <sstream>
|
#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;
|
extern int fceuindbg;
|
||||||
void ResetGameLoaded(void);
|
void ResetGameLoaded(void);
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,12 @@
|
||||||
#include "video.h"
|
#include "video.h"
|
||||||
#include "movie.h"
|
#include "movie.h"
|
||||||
#include "utils/memory.h"
|
#include "utils/memory.h"
|
||||||
|
#include "utils/memorystream.h"
|
||||||
#include "utils/xstring.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)
|
#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[];
|
extern char FileBase[];
|
||||||
|
@ -90,6 +94,42 @@ void MovieData::TryDumpIncremental()
|
||||||
}
|
}
|
||||||
|
|
||||||
const char MovieRecord::mnemonics[8] = {'A','B','S','T','U','D','L','R'};
|
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)
|
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)
|
//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)
|
void MovieData::dump(std::ostream *os)
|
||||||
{
|
{
|
||||||
*os << "version " << version;
|
*os << "version " << version << endl;
|
||||||
*os << "emuVersion " << emuVersion;
|
*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)
|
void MovieData::dump(FILE *fp)
|
||||||
|
@ -193,11 +243,9 @@ void MovieData::dump(FILE *fp)
|
||||||
|
|
||||||
int MovieData::dumpLen()
|
int MovieData::dumpLen()
|
||||||
{
|
{
|
||||||
FILE* tmp = tmpfile();
|
memorystream ms;
|
||||||
dump(tmp);
|
dump(&ms);
|
||||||
int len = ftell(tmp);
|
return ms.size();
|
||||||
fclose(tmp);
|
|
||||||
return len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@
|
||||||
//#include <unistd.h> //mbg merge 7/17/06 removed
|
//#include <unistd.h> //mbg merge 7/17/06 removed
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "x6502.h"
|
#include "x6502.h"
|
||||||
|
@ -36,6 +35,7 @@
|
||||||
#include "sound.h"
|
#include "sound.h"
|
||||||
#include "utils/endian.h"
|
#include "utils/endian.h"
|
||||||
#include "utils/memory.h"
|
#include "utils/memory.h"
|
||||||
|
#include "utils/memorystream.h"
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
#include "fds.h"
|
#include "fds.h"
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
|
@ -379,13 +379,19 @@ bool FCEUSS_SaveFP(FILE *st, int compressionLevel)
|
||||||
FCEUPPU_SaveState();
|
FCEUPPU_SaveState();
|
||||||
FCEUSND_SaveState();
|
FCEUSND_SaveState();
|
||||||
totalsize=WriteStateChunk(os,1,SFCPU);
|
totalsize=WriteStateChunk(os,1,SFCPU);
|
||||||
|
ms.sync();
|
||||||
totalsize+=WriteStateChunk(os,2,SFCPUC);
|
totalsize+=WriteStateChunk(os,2,SFCPUC);
|
||||||
|
ms.sync();
|
||||||
totalsize+=WriteStateChunk(os,3,FCEUPPU_STATEINFO);
|
totalsize+=WriteStateChunk(os,3,FCEUPPU_STATEINFO);
|
||||||
|
ms.sync();
|
||||||
totalsize+=WriteStateChunk(os,4,FCEUCTRL_STATEINFO);
|
totalsize+=WriteStateChunk(os,4,FCEUCTRL_STATEINFO);
|
||||||
|
ms.sync();
|
||||||
totalsize+=WriteStateChunk(os,5,FCEUSND_STATEINFO);
|
totalsize+=WriteStateChunk(os,5,FCEUSND_STATEINFO);
|
||||||
|
ms.sync();
|
||||||
if(FCEUI_IsMovieActive())
|
if(FCEUI_IsMovieActive())
|
||||||
{
|
{
|
||||||
totalsize+=WriteStateChunk(os,6,FCEUMOV_STATEINFO);
|
totalsize+=WriteStateChunk(os,6,FCEUMOV_STATEINFO);
|
||||||
|
ms.sync();
|
||||||
uint32 size = FCEUMOV_WriteState((std::ostream*)0);
|
uint32 size = FCEUMOV_WriteState((std::ostream*)0);
|
||||||
os->put(7);
|
os->put(7);
|
||||||
write32le(size, os);
|
write32le(size, os);
|
||||||
|
@ -400,6 +406,7 @@ bool FCEUSS_SaveFP(FILE *st, int compressionLevel)
|
||||||
write32le(size, os);
|
write32le(size, os);
|
||||||
os->write((char*)XBackBuf,size);
|
os->write((char*)XBackBuf,size);
|
||||||
totalsize += 5 + size;
|
totalsize += 5 + size;
|
||||||
|
ms.sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(SPreSave) SPreSave();
|
if(SPreSave) SPreSave();
|
||||||
|
@ -409,6 +416,13 @@ bool FCEUSS_SaveFP(FILE *st, int compressionLevel)
|
||||||
//save the length of the file
|
//save the length of the file
|
||||||
int len = ms.size();
|
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;
|
int error = Z_OK;
|
||||||
uint8* cbuf = (uint8*)ms.buf();
|
uint8* cbuf = (uint8*)ms.buf();
|
||||||
uLongf comprlen = -1;
|
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"
|
RelativePath="..\src\utils\memory.h"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File
|
||||||
|
RelativePath="..\src\utils\memorystream.h"
|
||||||
|
>
|
||||||
|
</File>
|
||||||
<File
|
<File
|
||||||
RelativePath="..\src\utils\unzip.cpp"
|
RelativePath="..\src\utils\unzip.cpp"
|
||||||
>
|
>
|
||||||
|
|
Loading…
Reference in New Issue