add binary movie mode, which is used when saving movies into savestates

This commit is contained in:
zeromus 2008-06-20 22:15:30 +00:00
parent d159696288
commit 8009546872
8 changed files with 211 additions and 74 deletions

View File

@ -311,7 +311,7 @@ static void Export()
if(GetSaveFileName(&ofn))
{
fstream* osRecordingMovie = FCEUD_UTF8_fstream(ofn.lpstrFile, "wb");
currMovieData.dump(osRecordingMovie);
currMovieData.dump(osRecordingMovie,false);
delete osRecordingMovie;
osRecordingMovie = 0;
}

View File

@ -127,18 +127,6 @@ void MovieRecord::parseJoy(std::istream* is, uint8& joystate)
joystate <<= 1;
joystate |= ((buf[i]!=' ')?1:0);
}
//older, slower(?) way:
//joystate = 0;
//for(int bit=7;bit>=0;bit--)
//{
// int c = is->get();
// if(c == -1)
// return;
// if(c != ' ')
// joystate |= (1<<bit);
//}
}
void MovieRecord::parse(MovieData* md, std::istream* is)
@ -166,9 +154,6 @@ void MovieRecord::parse(MovieData* md, std::istream* is)
parseJoy(is, joysticks[port]);
else if(md->ports[port] == SI_ZAPPER)
{
int x,y,b,bogo;
uint64 zaphit;
//*is >> x >> y >> b >> bogo >> zaphit;
zappers[port].x = uint32DecFromIstream(is);
zappers[port].y = uint32DecFromIstream(is);
zappers[port].b = uint32DecFromIstream(is);
@ -186,6 +171,67 @@ void MovieRecord::parse(MovieData* md, std::istream* is)
//should be left at a newline
}
bool MovieRecord::parseBinary(MovieData* md, std::istream* is)
{
commands = (uint8)is->get();
//check for eof
if(is->gcount() != 1) return false;
if(md->fourscore)
{
is->read((char*)&joysticks,4);
}
else
{
for(int port=0;port<2;port++)
{
if(md->ports[port] == SI_GAMEPAD)
joysticks[port] = (uint8)is->get();
else if(md->ports[port] == SI_ZAPPER)
{
zappers[port].x = (uint8)is->get();
zappers[port].y = (uint8)is->get();
zappers[port].b = (uint8)is->get();
zappers[port].bogo = (uint8)is->get();
zappers[port].zaphit = uint64DecFromIstream(is);
}
}
}
return true;
}
void MovieRecord::dumpBinary(MovieData* md, std::ostream* os, int index)
{
os->put(commands);
if(md->fourscore)
{
os->put(joysticks[0]);
os->put(joysticks[1]);
os->put(joysticks[2]);
os->put(joysticks[3]);
}
else
{
for(int port=0;port<2;port++)
{
if(md->ports[port] == SI_GAMEPAD)
os->put(joysticks[port]);
else if(md->ports[port] == SI_ZAPPER)
{
os->put(zappers[port].x);
os->put(zappers[port].y);
os->put(zappers[port].b);
os->put(zappers[port].bogo);
write64le(zappers[port].zaphit, os);
}
}
}
}
void MovieRecord::dump(MovieData* md, 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)
@ -239,6 +285,7 @@ MovieData::MovieData()
, palFlag(false)
, poweronFlag(false)
, resetFlag(false)
, binaryFlag(false)
, recordCount(1)
, greenZoneCount(0)
{
@ -279,6 +326,8 @@ void MovieData::installValue(std::string& key, std::string& val)
installInt(val,ports[1]);
else if(key == "port2")
installInt(val,ports[2]);
else if(key == "binary")
installBool(val,binaryFlag);
else if(key == "savestate")
{
int len = HexStringToBytesLength(val);
@ -290,7 +339,7 @@ void MovieData::installValue(std::string& key, std::string& val)
}
}
int MovieData::dump(std::ostream *os)
int MovieData::dump(std::ostream *os, bool binary)
{
int start = os->tellp();
*os << "version " << version << endl;
@ -306,11 +355,23 @@ int MovieData::dump(std::ostream *os)
*os << "port0 " << ports[0] << endl;
*os << "port1 " << ports[1] << endl;
*os << "port2 " << ports[2] << endl;
if(binary)
*os << "binary 1" << 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(this,os,i);
if(binary)
{
//put one | to start the binary dump
os->put('|');
for(int i=0;i<(int)records.size();i++)
records[i].dumpBinary(this,os,i);
}
else
for(int i=0;i<(int)records.size();i++)
records[i].dump(this,os,i);
int end = os->tellp();
return end-start;
}
@ -348,6 +409,43 @@ bool FCEUMOV_Mode(int modemask)
return FCEUMOV_Mode((EMOVIEMODE)modemask);
}
static void LoadFM2_binarychunk(MovieData& movieData, std::istream* fp, int size)
{
int recordsize = 1; //1 for the command
if(movieData.fourscore)
recordsize += 4; //4 joysticks
else
{
for(int i=0;i<2;i++)
{
switch(movieData.ports[i])
{
case SI_GAMEPAD: recordsize++; break;
case SI_ZAPPER: recordsize+=10; break;
}
}
}
//find out how much remains in the file
int curr = fp->tellg();
fp->seekg(0,std::ios::end);
int end = fp->tellg();
int flen = end-curr;
fp->seekg(curr,std::ios::beg);
//the amount todo is the min of the limiting size we received and the remaining contents of the file
int todo = std::min(size, flen);
int numRecords = todo/recordsize;
movieData.records.resize(numRecords);
for(int i=0;i<numRecords;i++)
{
movieData.records[i].parseBinary(&movieData,fp);
}
}
//yuck... another custom text parser.
static void LoadFM2(MovieData& movieData, std::istream* fp, int size, bool stopAfterHeader)
{
@ -367,6 +465,11 @@ static void LoadFM2(MovieData& movieData, std::istream* fp, int size, bool stopA
iswhitespace = (c==' '||c=='\t');
isrecchar = (c=='|');
isnewline = (c==10||c==13);
if(isrecchar && movieData.binaryFlag && !stopAfterHeader)
{
LoadFM2_binarychunk(movieData, fp, size);
return;
}
switch(state)
{
case NEWLINE:
@ -383,12 +486,12 @@ static void LoadFM2(MovieData& movieData, std::istream* fp, int size, bool stopA
{
dorecord:
if (stopAfterHeader) return;
MovieRecord record;
int currcount = movieData.records.size();
movieData.records.resize(currcount+1);
int preparse = fp->tellg();
record.parse(&movieData, fp);
movieData.records[currcount].parse(&movieData, fp);
int postparse = fp->tellg();
size -= (postparse-preparse);
movieData.records.push_back(record);
state = NEWLINE;
break;
}
@ -660,7 +763,7 @@ void FCEUI_SaveMovie(char *fname, uint8 flags)
}
//we are going to go ahead and dump the header. from now on we will only be appending frames
currMovieData.dump(osRecordingMovie);
currMovieData.dump(osRecordingMovie, false);
//todo - think about this
//ResetInputTypes();
@ -762,7 +865,7 @@ void FCEU_DrawMovies(uint8 *XBuf)
char counterbuf[32] = {0};
if(movieMode == MOVIEMODE_PLAY)
sprintf(counterbuf,"%d/%d",currFrameCounter,currMovieData.records.size());
else if(movieMode == MOVIEMODE_RECORD)
else if(movieMode == MOVIEMODE_RECORD)
sprintf(counterbuf,"%d",currMovieData.records.size());
if(counterbuf[0])
@ -774,7 +877,7 @@ int FCEUMOV_WriteState(std::ostream* os)
{
//we are supposed to dump the movie data into the savestate
if(movieMode == MOVIEMODE_RECORD || movieMode == MOVIEMODE_PLAY)
return currMovieData.dump(os);
return currMovieData.dump(os, true);
else return 0;
}
@ -845,7 +948,7 @@ bool FCEUMOV_ReadState(std::istream* is, uint32 size)
currMovieData.recordCount++;
openRecordingMovie(curMovieFilename);
currMovieData.dump(osRecordingMovie);
currMovieData.dump(osRecordingMovie, false);
movieMode = MOVIEMODE_RECORD;
}
}

View File

@ -93,7 +93,9 @@ public:
std::vector<char> savestate;
void parse(MovieData* md, std::istream* is);
bool parseBinary(MovieData* md, std::istream* is);
void dump(MovieData* md, std::ostream* os, int index);
void dumpBinary(MovieData* md, std::ostream* os, int index);
void parseJoy(std::istream* is, uint8& joystate);
void dumpJoy(std::ostream* os, uint8 joystate);
@ -123,6 +125,9 @@ public:
int recordCount;
FCEU_Guid guid;
//was the frame data stored in binary?
bool binaryFlag;
//which ports are defined for the movie
int ports[3];
//whether fourscore is enabled
@ -164,7 +169,7 @@ public:
void truncateAt(int frame);
void installValue(std::string& key, std::string& val);
int dump(std::ostream* os);
int dump(std::ostream* os, bool binary);
void clearRecordRange(int start, int len);
static bool loadSavestateFrom(std::vector<char>* buf);

View File

@ -256,46 +256,49 @@ void NetplayUpdate(uint8 *joyp)
break;
case FCEUNPCMD_SAVESTATE:
{
char *fn;
FILE *fp;
//mbg todo netplay
//char *fn;
//FILE *fp;
/* Send the cheats first, then the save state, since
there might be a frame or two in between the two sendfile
commands on the server side.
*/
fn = strdup(FCEU_MakeFName(FCEUMKF_CHEAT,0,0).c_str());
//if(!
FCEUNET_SendFile(FCEUNPCMD_LOADCHEATS,fn);
////Send the cheats first, then the save state, since
////there might be a frame or two in between the two sendfile
////commands on the server side.
// {
// free(fn);
// return;
// }
free(fn);
if(!FCEUnetplay) return;
//fn = strdup(FCEU_MakeFName(FCEUMKF_CHEAT,0,0).c_str());
fn = strdup(FCEU_MakeFName(FCEUMKF_NPTEMP,0,0).c_str());
fp = fopen(fn, "wb");
if(FCEUSS_SaveFP(fp,Z_BEST_COMPRESSION))
{
fclose(fp);
if(!FCEUNET_SendFile(FCEUNPCMD_LOADSTATE, fn))
{
unlink(fn);
free(fn);
return;
}
unlink(fn);
free(fn);
}
else
{
fclose(fp);
FCEUD_PrintError("File error. (K)ill, (M)aim, (D)estroy? Now!");
unlink(fn);
free(fn);
return;
}
////why??????
////if(!
// FCEUNET_SendFile(FCEUNPCMD_LOADCHEATS,fn);
//// {
//// free(fn);
//// return;
//// }
//free(fn);
//if(!FCEUnetplay) return;
//fn = strdup(FCEU_MakeFName(FCEUMKF_NPTEMP,0,0).c_str());
//fp = fopen(fn, "wb");
//if(FCEUSS_SaveFP(fp,Z_BEST_COMPRESSION))
//{
// fclose(fp);
// if(!FCEUNET_SendFile(FCEUNPCMD_LOADSTATE, fn))
// {
// unlink(fn);
// free(fn);
// return;
// }
// unlink(fn);
// free(fn);
//}
//else
//{
// fclose(fp);
// FCEUD_PrintError("File error. (K)ill, (M)aim, (D)estroy? Now!");
// unlink(fn);
// free(fn);
// return;
//}
}
break;

View File

@ -314,13 +314,6 @@ static bool ReadStateChunks(std::istream* is, int32 totalsize)
int CurrentState=1;
extern int geniestage;
bool FCEUSS_SaveFP(FILE* fp, int compressionLevel)
{
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)
{
@ -688,7 +681,8 @@ void FCEUI_LoadState(char *fname)
if(FCEUSS_Load(fname))
{
if(FCEUnetplay)
//mbg todo netplay
/*if(FCEUnetplay)
{
char *fn = strdup(FCEU_MakeFName(FCEUMKF_NPTEMP, 0, 0).c_str());
FILE *fp;
@ -709,7 +703,7 @@ void FCEUI_LoadState(char *fname)
}
free(fn);
}
}*/
}
else
{

View File

@ -31,7 +31,6 @@ bool FCEUSS_Load(char *);
//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(std::istream* is, ENUM_SSLOADPARAMS params);

View File

@ -78,6 +78,22 @@ int write32le(uint32 b, std::ostream* os)
return 4;
}
int write64le(uint64 b, std::ostream* os)
{
uint8 s[8];
s[0]=b;
s[1]=b>>8;
s[2]=b>>16;
s[3]=b>>24;
s[4]=b>>32;
s[5]=b>>40;
s[6]=b>>48;
s[7]=b>>56;
os->write((char*)&s,8);
return 4;
}
///reads a little endian 32bit value from the specified file
int read32le(uint32 *Bufo, FILE *fp)
{
@ -92,6 +108,21 @@ int read32le(uint32 *Bufo, FILE *fp)
return 1;
}
///reads a little endian 64bit value from the specified file
int read64le(uint64 *Bufo, std::istream *is)
{
uint64 buf;
if(is->read((char*)&buf,8).gcount() != 8)
return 0;
#ifdef LSB_FIRST
*Bufo=buf;
#else
*Bufo = FCEU_de64lsb(&buf)
#endif
return 1;
}
int read32le(uint32 *Bufo, std::istream *is)
{
uint32 buf;

View File

@ -8,6 +8,8 @@
int write16le(uint16 b, FILE *fp);
int write32le(uint32 b, FILE *fp);
int write32le(uint32 b, std::ostream* os);
int write64le(uint64 b, std::ostream* os);
int read64le(uint64 *Bufo, std::istream *is);
int read32le(uint32 *Bufo, std::istream *is);
int read32le(uint32 *Bufo, FILE *fp);
void FlipByteOrder(uint8 *src, uint32 count);