Fixes the bug that extra frames could be saved in the movie file, when rerecording from middle of the movie.

Also flushes the movie file more often.
This commit is contained in:
aquanull 2018-04-04 19:32:29 +08:00
parent b08267b84a
commit 693607f66b
2 changed files with 113 additions and 56 deletions

View File

@ -478,7 +478,7 @@ void MovieData::installValue(std::string& key, std::string& val)
} }
} }
int MovieData::dump(EMUFILE *os, bool binary) int MovieData::dump(EMUFILE *os, bool binary, bool seekToCurrFramePos)
{ {
int start = os->ftell(); int start = os->ftell();
os->fprintf("version %d\n", version); os->fprintf("version %d\n", version);
@ -516,19 +516,30 @@ int MovieData::dump(EMUFILE *os, bool binary)
if (this->loadFrameCount >= 0) if (this->loadFrameCount >= 0)
os->fprintf("length %d\n" , this->loadFrameCount); os->fprintf("length %d\n" , this->loadFrameCount);
int currFramePos = -1;
if(binary) if(binary)
{ {
//put one | to start the binary dump //put one | to start the binary dump
os->fputc('|'); os->fputc('|');
for(int i=0;i<(int)records.size();i++) for (int i = 0; i < (int)records.size(); i++)
{
if (seekToCurrFramePos && currFrameCounter == i)
currFramePos = os->ftell();
records[i].dumpBinary(this, os, i); records[i].dumpBinary(this, os, i);
}
} else } else
{ {
for(int i=0;i<(int)records.size();i++) for (int i = 0; i < (int)records.size(); i++)
{
if (seekToCurrFramePos && currFrameCounter == i)
currFramePos = os->ftell();
records[i].dump(this, os, i); records[i].dump(this, os, i);
}
} }
int end = os->ftell(); int end = os->ftell();
if (currFramePos >= 0)
os->fseek(currFramePos, SEEK_SET);
return end-start; return end-start;
} }
@ -735,53 +746,83 @@ bool LoadFM2(MovieData& movieData, EMUFILE* fp, int size, bool stopAfterHeader)
return true; return true;
} }
/// Stop movie playback. static EMUFILE *openRecordingMovie(const char* fname)
static void StopPlayback()
{ {
FCEU_DispMessageOnMovie("Movie playback stopped."); if (osRecordingMovie)
movieMode = MOVIEMODE_INACTIVE; delete osRecordingMovie;
}
// Stop movie playback without closing the movie. osRecordingMovie = FCEUD_UTF8_fstream(fname, "wb");
static void FinishPlayback() if (!osRecordingMovie || osRecordingMovie->fail()) {
{ FCEU_PrintError("Error opening movie output file: %s", fname);
extern int closeFinishedMovie; return NULL;
if (closeFinishedMovie)
StopPlayback();
else
{
FCEU_DispMessage("Movie finished playing.",0);
movieMode = MOVIEMODE_FINISHED;
} }
strcpy(curMovieFilename, fname);
return osRecordingMovie;
} }
static void closeRecordingMovie() static void closeRecordingMovie()
{ {
if(osRecordingMovie) if (osRecordingMovie)
{ {
delete osRecordingMovie; delete osRecordingMovie;
osRecordingMovie = 0; osRecordingMovie = 0;
} }
} }
// Callers shall set the approriate movieMode before calling this
static void RedumpWholeMovieFile(bool justToggledRecording = false)
{
bool recording = (movieMode == MOVIEMODE_RECORD);
assert((NULL != osRecordingMovie) == (recording != justToggledRecording) && "osRecordingMovie should be consistent with movie mode!");
if (NULL == openRecordingMovie(curMovieFilename))
return;
currMovieData.dump(osRecordingMovie, false/*currMovieData.binaryFlag*/, recording);
if (recording)
osRecordingMovie->fflush();
else
closeRecordingMovie();
}
/// Stop movie playback.
static void StopPlayback()
{
assert(movieMode != MOVIEMODE_RECORD && NULL == osRecordingMovie);
movieMode = MOVIEMODE_INACTIVE;
FCEU_DispMessageOnMovie("Movie playback stopped.");
}
// Stop movie playback without closing the movie.
static void FinishPlayback()
{
assert(movieMode != MOVIEMODE_RECORD);
extern int closeFinishedMovie;
if (closeFinishedMovie)
StopPlayback();
else
{
movieMode = MOVIEMODE_FINISHED;
FCEU_DispMessage("Movie finished playing.",0);
}
}
/// Stop movie recording /// Stop movie recording
static void StopRecording() static void StopRecording()
{ {
FCEU_DispMessage("Movie recording stopped.",0); assert(movieMode == MOVIEMODE_RECORD);
movieMode = MOVIEMODE_INACTIVE;
closeRecordingMovie(); movieMode = MOVIEMODE_INACTIVE;
RedumpWholeMovieFile(true);
FCEU_DispMessage("Movie recording stopped.",0);
} }
void FCEUI_StopMovie() static void OnMovieClosed()
{ {
if(suppressMovieStop) assert(movieMode == MOVIEMODE_INACTIVE);
return;
if(movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_FINISHED)
StopPlayback();
else if(movieMode == MOVIEMODE_RECORD)
StopRecording();
curMovieFilename[0] = 0; //No longer a current movie filename curMovieFilename[0] = 0; //No longer a current movie filename
freshMovie = false; //No longer a fresh movie loaded freshMovie = false; //No longer a fresh movie loaded
@ -794,6 +835,19 @@ void FCEUI_StopMovie()
bool bogorf; bool bogorf;
void FCEUI_StopMovie()
{
if (suppressMovieStop)
return;
if (movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_FINISHED)
StopPlayback();
else if (movieMode == MOVIEMODE_RECORD)
StopRecording();
OnMovieClosed();
}
void poweron(bool shouldDisableBatteryLoading) void poweron(bool shouldDisableBatteryLoading)
{ {
//// make a for-movie-recording power-on clear the game's save data, too //// make a for-movie-recording power-on clear the game's save data, too
@ -1021,21 +1075,6 @@ bool FCEUI_LoadMovie(const char *fname, bool _read_only, int _pauseframe)
return true; return true;
} }
static void openRecordingMovie(const char* fname)
{
osRecordingMovie = FCEUD_UTF8_fstream(fname, "wb");
if(!osRecordingMovie || osRecordingMovie->fail()) {
FCEU_PrintError("Error opening movie output file: %s",fname);
return;
}
strcpy(curMovieFilename, fname);
#ifdef WIN32
//Add to the recent movie menu
AddRecentMovieFile(fname);
#endif
}
//begin recording a new movie //begin recording a new movie
//TODO - BUG - the record-from-another-savestate doesnt work. //TODO - BUG - the record-from-another-savestate doesnt work.
@ -1048,11 +1087,14 @@ void FCEUI_SaveMovie(const char *fname, EMOVIE_FLAG flags, std::wstring author)
FCEUI_StopMovie(); FCEUI_StopMovie();
openRecordingMovie(fname); if (NULL == openRecordingMovie(fname))
if(!osRecordingMovie || osRecordingMovie->fail())
return; return;
#ifdef WIN32
//Add to the recent movie menu
AddRecentMovieFile(fname);
#endif
currFrameCounter = 0; currFrameCounter = 0;
LagCounterReset(); LagCounterReset();
FCEUMOV_CreateCleanMovie(); FCEUMOV_CreateCleanMovie();
@ -1224,6 +1266,9 @@ void FCEUMOV_AddCommand(int cmd)
void FCEU_DrawMovies(uint8 *XBuf) void FCEU_DrawMovies(uint8 *XBuf)
{ {
// not the best place, but just working
assert((NULL != osRecordingMovie) == (movieMode == MOVIEMODE_RECORD));
if(frame_display) if(frame_display)
{ {
char counterbuf[32] = {0}; char counterbuf[32] = {0};
@ -1420,9 +1465,15 @@ bool FCEUMOV_ReadState(EMUFILE* is, uint32 size)
#endif #endif
} }
closeRecordingMovie();
if (movie_readonly) if (movie_readonly)
{ {
if (movieMode == MOVIEMODE_RECORD)
{
movieMode = MOVIEMODE_PLAY;
RedumpWholeMovieFile(true);
closeRecordingMovie();
}
// currFrameCounter at this point represents the savestate framecount // currFrameCounter at this point represents the savestate framecount
int frame_of_mismatch = CheckTimelines(tempMovieData, currMovieData); int frame_of_mismatch = CheckTimelines(tempMovieData, currMovieData);
if (frame_of_mismatch >= 0) if (frame_of_mismatch >= 0)
@ -1461,13 +1512,16 @@ bool FCEUMOV_ReadState(EMUFILE* is, uint32 size)
} else } else
{ {
//Read+Write mode //Read+Write mode
closeRecordingMovie();
if (currFrameCounter > (int)tempMovieData.records.size()) if (currFrameCounter > (int)tempMovieData.records.size())
{ {
//This is a post movie savestate, handle it differently //This is a post movie savestate, handle it differently
//Replace movie contents but then switch to movie finished mode //Replace movie contents but then switch to movie finished mode
currMovieData = tempMovieData; currMovieData = tempMovieData;
openRecordingMovie(curMovieFilename); movieMode = MOVIEMODE_PLAY;
currMovieData.dump(osRecordingMovie, false/*currMovieData.binaryFlag*/); FCEUMOV_IncrementRerecordCount();
RedumpWholeMovieFile();
FinishPlayback(); FinishPlayback();
} else } else
{ {
@ -1477,11 +1531,9 @@ bool FCEUMOV_ReadState(EMUFILE* is, uint32 size)
tempMovieData.truncateAt(currFrameCounter); tempMovieData.truncateAt(currFrameCounter);
currMovieData = tempMovieData; currMovieData = tempMovieData;
FCEUMOV_IncrementRerecordCount();
openRecordingMovie(curMovieFilename);
currMovieData.dump(osRecordingMovie, false/*currMovieData.binaryFlag*/);
movieMode = MOVIEMODE_RECORD; movieMode = MOVIEMODE_RECORD;
FCEUMOV_IncrementRerecordCount();
RedumpWholeMovieFile(true);
} }
} }
} }
@ -1616,6 +1668,11 @@ void FCEUI_MoviePlayFromBeginning(void)
#endif #endif
} else if (movieMode != MOVIEMODE_INACTIVE) } else if (movieMode != MOVIEMODE_INACTIVE)
{ {
if (movieMode == MOVIEMODE_RECORD)
{
movieMode = MOVIEMODE_PLAY;
RedumpWholeMovieFile(true);
}
if (currMovieData.savestate.empty()) if (currMovieData.savestate.empty())
{ {
movie_readonly = true; movie_readonly = true;

View File

@ -234,7 +234,7 @@ public:
void truncateAt(int frame); void truncateAt(int frame);
void installValue(std::string& key, std::string& val); void installValue(std::string& key, std::string& val);
int dump(EMUFILE* os, bool binary); int dump(EMUFILE* os, bool binary, bool seekToCurrFramePos = false);
void clearRecordRange(int start, int len); void clearRecordRange(int start, int len);
void eraseRecords(int at, int frames = 1); void eraseRecords(int at, int frames = 1);