more savestate things work better
This commit is contained in:
parent
c013dd96e6
commit
7a9721e48a
|
@ -3,11 +3,13 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
#include "types.h"
|
||||
#include "git.h"
|
||||
|
||||
FILE *FCEUD_UTF8fopen(const char *fn, const char *mode);
|
||||
std::fstream* FCEUD_UTF8_fstream(const char *n, const char *m);
|
||||
|
||||
|
||||
//mbg 7/23/06
|
||||
|
|
|
@ -1235,6 +1235,30 @@ static void FCEUD_MakePathDirs(const char *fname)
|
|||
} while(1);
|
||||
}
|
||||
|
||||
std::fstream* FCEUD_UTF8_fstream(const char *n, const char *m)
|
||||
{
|
||||
if(strchr(m, 'w') || strchr(m, '+'))
|
||||
{
|
||||
FCEUD_MakePathDirs(n);
|
||||
}
|
||||
|
||||
std::ios_base::open_mode mode = std::ios_base::binary;
|
||||
if(!strcmp(m,"r"))
|
||||
mode |= std::ios_base::in;
|
||||
else if(!strcmp(m,"w"))
|
||||
mode |= std::ios_base::out | std::ios_base::trunc;
|
||||
else if(!strcmp(m,"a"))
|
||||
mode |= std::ios_base::out | std::ios_base::app;
|
||||
else if(!strcmp(m,"r+"))
|
||||
mode |= std::ios_base::in | std::ios_base::out;
|
||||
else if(!strcmp(m,"w+"))
|
||||
mode |= std::ios_base::in | std::ios_base::out | std::ios_base::trunc;
|
||||
else if(!strcmp(m,"a+"))
|
||||
mode |= std::ios_base::in | std::ios_base::out | std::ios_base::app;
|
||||
|
||||
return new std::fstream(n,mode);
|
||||
}
|
||||
|
||||
FILE *FCEUD_UTF8fopen(const char *n, const char *m)
|
||||
{
|
||||
if(strchr(m, 'w') || strchr(m, '+'))
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
|
@ -86,7 +87,7 @@ void MovieData::TryDumpIncremental()
|
|||
{
|
||||
if(currFrameCounter < (int)currMovieData.records.size())
|
||||
{
|
||||
MovieData::dumpSavestateTo(&currMovieData.records[currFrameCounter].savestate);
|
||||
MovieData::dumpSavestateTo(&currMovieData.records[currFrameCounter].savestate,Z_NO_COMPRESSION);
|
||||
currMovieData.greenZoneCount++;
|
||||
}
|
||||
}
|
||||
|
@ -417,7 +418,7 @@ static void ResetInputTypes()
|
|||
#endif
|
||||
}
|
||||
|
||||
bool MovieData::loadSavestateFrom(std::vector<uint8>* buf)
|
||||
bool MovieData::loadSavestateFrom(std::vector<char>* buf)
|
||||
{
|
||||
//dump the savestate to disk
|
||||
FILE* fp = tmpfile();
|
||||
|
@ -430,19 +431,10 @@ bool MovieData::loadSavestateFrom(std::vector<uint8>* buf)
|
|||
return success;
|
||||
}
|
||||
|
||||
void MovieData::dumpSavestateTo(std::vector<uint8>* buf)
|
||||
void MovieData::dumpSavestateTo(std::vector<char>* buf, int compressionLevel)
|
||||
{
|
||||
//dump a savestate to a tempfile..
|
||||
FILE* tmp = tmpfile();
|
||||
FCEUSS_SaveFP(tmp,-1);
|
||||
|
||||
//reloading the savestate into the data structure
|
||||
fseek(tmp,0,SEEK_END);
|
||||
int len = (int)ftell(tmp);
|
||||
fseek(tmp,0,SEEK_SET);
|
||||
buf->resize(len);
|
||||
fread(&(*buf)[0],1,len,tmp);
|
||||
fclose(tmp);
|
||||
memorystream ms(buf);
|
||||
FCEUSS_SaveMS(&ms,compressionLevel);
|
||||
}
|
||||
|
||||
//begin playing an existing movie
|
||||
|
@ -577,7 +569,7 @@ void FCEUI_SaveMovie(char *fname, uint8 flags)
|
|||
}
|
||||
else
|
||||
{
|
||||
MovieData::dumpSavestateTo(&currMovieData.savestate);
|
||||
MovieData::dumpSavestateTo(&currMovieData.savestate,Z_BEST_COMPRESSION);
|
||||
}
|
||||
|
||||
//we are going to go ahead and dump the header. from now on we will only be appending frames
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
}
|
||||
|
||||
//a waste of memory in lots of cases.. maybe make it a pointer later?
|
||||
std::vector<uint8> savestate;
|
||||
std::vector<char> savestate;
|
||||
|
||||
void dump(FILE* fp, int index);
|
||||
void dump(std::ostream* os, int index);
|
||||
|
@ -82,7 +82,7 @@ public:
|
|||
bool resetFlag;
|
||||
MD5DATA romChecksum;
|
||||
std::string romFilename;
|
||||
std::vector<uint8> savestate;
|
||||
std::vector<char> savestate;
|
||||
std::vector<MovieRecord> records;
|
||||
//this is the RERECORD COUNT. please rename variable.
|
||||
int recordCount;
|
||||
|
@ -129,8 +129,8 @@ public:
|
|||
int dumpLen();
|
||||
void clearRecordRange(int start, int len);
|
||||
|
||||
static bool loadSavestateFrom(std::vector<uint8>* buf);
|
||||
static void dumpSavestateTo(std::vector<uint8>* buf);
|
||||
static bool loadSavestateFrom(std::vector<char>* buf);
|
||||
static void dumpSavestateTo(std::vector<char>* buf, int compressionLevel);
|
||||
void TryDumpIncremental();
|
||||
};
|
||||
|
||||
|
|
|
@ -276,7 +276,7 @@ void NetplayUpdate(uint8 *joyp)
|
|||
|
||||
fn = FCEU_MakeFName(FCEUMKF_NPTEMP,0,0);
|
||||
fp = fopen(fn, "wb");
|
||||
if(FCEUSS_SaveFP(fp,-1))
|
||||
if(FCEUSS_SaveFP(fp,Z_BEST_COMPRESSION))
|
||||
{
|
||||
fclose(fp);
|
||||
if(!FCEUNET_SendFile(FCEUNPCMD_LOADSTATE, fn))
|
||||
|
|
|
@ -368,9 +368,19 @@ static int ReadStateChunks(FILE *st, int32 totalsize)
|
|||
int CurrentState=1;
|
||||
extern int geniestage;
|
||||
|
||||
bool FCEUSS_SaveFP(FILE *st, int compressionLevel)
|
||||
bool FCEUSS_SaveFP(FILE* fp, int compressionLevel)
|
||||
{
|
||||
//a temporary buffer. we're going to put the savestate in here and then compress it
|
||||
memorystream ms;
|
||||
bool ret = FCEUSS_SaveMS(&ms,compressionLevel);
|
||||
fwrite(ms.buf(),1,ms.size(),fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool FCEUSS_SaveMS(std::ostream* outstream, int compressionLevel)
|
||||
{
|
||||
//a temp memory stream. we'll dump some data here and then compress
|
||||
//TODO - support dumping directly without compressing to save a buffer copy
|
||||
|
||||
memorystream ms;
|
||||
std::ostream* os = (std::ostream*)&ms;
|
||||
|
||||
|
@ -426,7 +436,7 @@ bool FCEUSS_SaveFP(FILE *st, int compressionLevel)
|
|||
int error = Z_OK;
|
||||
uint8* cbuf = (uint8*)ms.buf();
|
||||
uLongf comprlen = -1;
|
||||
//if(compressionLevel != Z_NO_COMPRESSION)
|
||||
if(compressionLevel != Z_NO_COMPRESSION)
|
||||
{
|
||||
//worst case compression.
|
||||
//zlib says "0.1% larger than sourceLen plus 12 bytes"
|
||||
|
@ -442,8 +452,8 @@ bool FCEUSS_SaveFP(FILE *st, int compressionLevel)
|
|||
FCEU_en32lsb(header+12, comprlen);
|
||||
|
||||
//dump it to the destination file
|
||||
fwrite(header,1,16,st);
|
||||
fwrite(cbuf,1,comprlen,st);
|
||||
outstream->write((char*)header,16);
|
||||
outstream->write((char*)cbuf,comprlen);
|
||||
|
||||
if(cbuf != (uint8*)ms.buf()) delete[] cbuf;
|
||||
return error == Z_OK;
|
||||
|
|
|
@ -29,7 +29,11 @@ enum ENUM_SSLOADPARAMS
|
|||
|
||||
void FCEUSS_Save(char *);
|
||||
int FCEUSS_Load(char *);
|
||||
bool FCEUSS_SaveFP(FILE *, int compressionLevel); //zlib values: 0 (none) through 9 (max) or -1 (default)
|
||||
|
||||
//zlib values: 0 (none) through 9 (max) or -1 (default)
|
||||
bool FCEUSS_SaveMS(std::ostream* outstream, int compressionLevel);
|
||||
bool FCEUSS_SaveFP(FILE* fp, int compressionLevel);
|
||||
|
||||
bool FCEUSS_LoadFP(FILE *, ENUM_SSLOADPARAMS);
|
||||
|
||||
extern int CurrentState;
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
template<typename T>
|
||||
class memory_streambuf: public std::streambuf {
|
||||
private:
|
||||
|
||||
friend class memorystream;
|
||||
|
||||
//the current buffer
|
||||
char* buf;
|
||||
T* buf;
|
||||
|
||||
//the current allocated capacity of the buffer
|
||||
size_t capacity;
|
||||
|
@ -21,6 +22,9 @@ private:
|
|||
|
||||
//the current 'write window' starting position within the buffer for writing.
|
||||
size_t ww;
|
||||
|
||||
//a vector that we have been told to use
|
||||
std::vector<T>* usevec;
|
||||
|
||||
|
||||
public:
|
||||
|
@ -36,11 +40,39 @@ public:
|
|||
: length(0)
|
||||
, myBuf(true)
|
||||
, ww(0)
|
||||
, buf(new char[capacity = 8])
|
||||
, buf(new T[capacity = 128])
|
||||
, usevec(0)
|
||||
{
|
||||
sync();
|
||||
}
|
||||
|
||||
//constructs a non-expandable streambuf around the provided buffer
|
||||
memory_streambuf(T* usebuf, int buflength)
|
||||
: length(buflength)
|
||||
, myBuf(false)
|
||||
, buf(usebuf)
|
||||
, ww(0)
|
||||
, usevec(0)
|
||||
{
|
||||
sync();
|
||||
}
|
||||
|
||||
//constructs an expandable streambuf around the provided buffer
|
||||
memory_streambuf(std::vector<T>* _usevec)
|
||||
: usevec(_usevec)
|
||||
, length(_usevec->size())
|
||||
, capacity(_usevec->size())
|
||||
, myBuf(false)
|
||||
, ww(0)
|
||||
{
|
||||
if(length>0)
|
||||
buf = &(*_usevec)[0];
|
||||
else buf = 0;
|
||||
|
||||
sync();
|
||||
}
|
||||
|
||||
|
||||
~memory_streambuf()
|
||||
{
|
||||
//only cleanup if we own the seq
|
||||
|
@ -48,26 +80,16 @@ public:
|
|||
}
|
||||
|
||||
//to avoid copying, rebuilds the provided vector and copies the streambuf contents into it
|
||||
void toVector(std::vector<char>& out)
|
||||
void toVector(std::vector<T>& out)
|
||||
{
|
||||
out.reserve(length);
|
||||
out.resize(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()
|
||||
std::vector<T> 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();
|
||||
return std::vector<T>(buf,buf+length);
|
||||
}
|
||||
|
||||
//tells the current read or write position
|
||||
|
@ -98,7 +120,7 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
char* getbuf()
|
||||
T* getbuf()
|
||||
{
|
||||
sync();
|
||||
return buf;
|
||||
|
@ -131,7 +153,7 @@ private:
|
|||
|
||||
void expand(size_t upto)
|
||||
{
|
||||
if(!myBuf)
|
||||
if(!myBuf && !usevec)
|
||||
throw new std::exception("memory_streambuf is not expandable");
|
||||
|
||||
size_t newcapacity;
|
||||
|
@ -139,14 +161,25 @@ private:
|
|||
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;
|
||||
//if we are supposed to use the vector, then do it now
|
||||
if(usevec)
|
||||
{
|
||||
usevec->resize(newcapacity);
|
||||
capacity = usevec->size();
|
||||
buf = &(*usevec)[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
//otherwise, manage our own buffer
|
||||
T* newbuf = new T[newcapacity];
|
||||
memcpy(newbuf,buf,capacity);
|
||||
delete[] buf;
|
||||
capacity = newcapacity;
|
||||
buf = newbuf;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
@ -197,11 +230,20 @@ class memorystream : public std::basic_iostream<char, std::char_traits<char> >
|
|||
public:
|
||||
memorystream()
|
||||
: std::basic_iostream<char, std::char_traits<char> >(&streambuf)
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
memorystream(char* usebuf, int buflength)
|
||||
: streambuf(usebuf, buflength)
|
||||
, std::basic_iostream<char, std::char_traits<char> >(&streambuf)
|
||||
{}
|
||||
|
||||
memorystream(std::vector<char>* usevec)
|
||||
: streambuf(usevec)
|
||||
, std::basic_iostream<char, std::char_traits<char> >(&streambuf)
|
||||
{}
|
||||
|
||||
//the underlying memory_streambuf
|
||||
memory_streambuf streambuf;
|
||||
memory_streambuf<char> streambuf;
|
||||
|
||||
|
||||
public:
|
||||
|
@ -212,4 +254,4 @@ public:
|
|||
void sync() { streambuf.sync(); }
|
||||
//rewinds the cursors to offset 0
|
||||
void rewind() { streambuf.seekpos(0,std::ios::in | std::ios::out); }
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue