* fixed movie savestates logic, loading post-movie savestates is not allowed in read-only

* optimized movie records operations (compare, insert, clone)
This commit is contained in:
ansstuff 2012-09-01 11:47:09 +00:00
parent 525deb4c65
commit c03fd7d23b
1 changed files with 79 additions and 111 deletions

View File

@ -115,15 +115,12 @@ void MovieData::clearRecordRange(int start, int len)
void MovieData::insertEmpty(int at, int frames)
{
if(at == -1)
if (at == -1)
{
int currcount = records.size();
records.resize(currcount + frames);
clearRecordRange(currcount, frames);
records.resize(records.size() + frames);
} else
{
records.insert(records.begin() + at, frames, MovieRecord());
clearRecordRange(at,frames);
}
}
@ -136,40 +133,33 @@ void MovieData::cloneRegion(int at, int frames)
for(int i = 0; i < frames; i++)
records[i+at].Clone(records[i + at + frames]);
}
// ----------------------------------------------------------------------------
MovieRecord::MovieRecord()
{
joysticks.data[0] = 0;
joysticks.data[1] = 0;
joysticks.data[2] = 0;
joysticks.data[3] = 0;
commands = 0;
zappers[0].b = 0;
zappers[0].bogo = 0;
zappers[0].x = 0;
zappers[0].y = 0;
zappers[0].zaphit = 0;
zappers[1].b = 0;
zappers[1].bogo = 0;
zappers[1].x = 0;
zappers[1].y = 0;
zappers[1].zaphit = 0;
*(uint32*)&joysticks = 0;
memset(zappers, 0, sizeof(zappers));
}
void MovieRecord::clear()
{
commands = 0;
*(uint32*)&joysticks = 0;
memset(zappers,0,sizeof(zappers));
memset(zappers, 0, sizeof(zappers));
}
bool MovieRecord::Compare(MovieRecord& compareRec)
{
//Joysticks, Zappers, and commands
if (this->commands != compareRec.commands)
return false;
if ((*(uint32*)&(this->joysticks)) != (*(uint32*)&(compareRec.joysticks)))
return false;
if (memcmp(this->zappers, compareRec.zappers, sizeof(zappers)))
return false;
/*
if (this->joysticks != compareRec.joysticks)
return false;
@ -190,32 +180,19 @@ bool MovieRecord::Compare(MovieRecord& compareRec)
if (this->zappers[1].zaphit != compareRec.zappers[1].zaphit) return false;
if (this->zappers[1].b != compareRec.zappers[1].b) return false;
if (this->zappers[1].bogo != compareRec.zappers[1].bogo) return false;
*/
return true;
}
void MovieRecord::Clone(MovieRecord& sourceRec)
{
this->joysticks[0] = sourceRec.joysticks[0];
this->joysticks[1] = sourceRec.joysticks[1];
this->joysticks[2] = sourceRec.joysticks[2];
this->joysticks[3] = sourceRec.joysticks[3];
this->zappers[0].x = sourceRec.zappers[0].x;
this->zappers[0].y = sourceRec.zappers[0].y;
this->zappers[0].zaphit = sourceRec.zappers[0].zaphit;
this->zappers[0].b = sourceRec.zappers[0].b;
this->zappers[0].bogo = sourceRec.zappers[0].bogo;
this->zappers[1].x = sourceRec.zappers[1].x;
this->zappers[1].y = sourceRec.zappers[1].y;
this->zappers[1].zaphit = sourceRec.zappers[1].zaphit;
this->zappers[1].b = sourceRec.zappers[1].b;
this->zappers[1].bogo = sourceRec.zappers[1].bogo;
*(uint32*)&joysticks = *(uint32*)(&(sourceRec.joysticks));
memcpy(this->zappers, sourceRec.zappers, sizeof(zappers));
this->commands = sourceRec.commands;
}
const char MovieRecord::mnemonics[8] = {'A','B','S','T','U','D','L','R'};
void MovieRecord::dumpJoy(EMUFILE* os, uint8 joystate)
{
//these are mnemonics for each joystick bit.
@ -1156,30 +1133,28 @@ int FCEUMOV_WriteState(EMUFILE* os)
else return 0;
}
bool CheckTimelines(MovieData& stateMovie, MovieData& currMovie, int& errorFr)
// returns
int CheckTimelines(MovieData& stateMovie, MovieData& currMovie)
{
bool isInTimeline = true;
int length;
// end_frame = min(urrMovie.records.size(), stateMovie.records.size(), currFrameCounter)
int end_frame = currMovie.records.size();
if (end_frame > (int)stateMovie.records.size())
end_frame = stateMovie.records.size();
if (end_frame > currFrameCounter)
end_frame = currFrameCounter;
//First check, make sure we are checking is for a post-movie savestate, we just want to adjust the length for now, we will handle this situation later in another function
if (currFrameCounter <= stateMovie.getNumRecords())
length = currFrameCounter; //Note: currFrameCounter corresponds to the framecounter in the savestate
else if (currFrameCounter > currMovie.getNumRecords()) //Now that we know the length of the records of the savestate we plan to load, let's match the length against the movie
length = currMovie.getNumRecords(); //If length < currMovie records then this is a "future" event from the current movie, againt we will handle this situation later, we just want to find the right number of frames to compare
else
length = stateMovie.getNumRecords();
for (int x = 0; x < length; x++)
int x = 0;
for (; x < end_frame; x++)
{
if (!stateMovie.records[x].Compare(currMovie.records[x]))
{
isInTimeline = false;
errorFr = x;
break;
}
}
return isInTimeline;
if (x < end_frame)
{
return x;
} else
return -1; // no mismatch found
}
@ -1240,11 +1215,12 @@ bool FCEUMOV_ReadState(EMUFILE* is, uint32 size)
* Check that movie and savestate-movie are in same timeline. If not then this is a wrong timeline error.
o on error: a message informing that the savestate doesn't belong to this movie
+ failstate: loadstate attempt canceled, movie can resume as if not attempted if user has backup savestates enabled else stop movie
* Check that savestate-movie is not greater than movie. If not then this is a future event error and is not allowed in read-only
* Check that savestate-movie is not greater than movie. If it's greater then this is a future event error and is not allowed in read-only
o on error: message informing that the savestate is from a frame after the last frame of the movie
+ failstate - loadstate attempt cancelled, movie can resume if user has backup savesattes enabled, else stop movie
* Check that savestate framcount <= savestate movie length. If not this is a post-movie savestate
o on post-movie: See post-movie event section.
* Check that savestate framcount <= savestate movie length. If not this is a post-movie savestate and is not allowed in read-only
o on error: message informing that the savestate is from a frame after the last frame of the savestated movie
+ failstate - loadstate attempt cancelled, movie can resume if user has backup savesattes enabled, else stop movie
* All error checks have passed, state will be loaded
* Movie contained in the savestate will be discarded
* Movie is now in Playback mode
@ -1309,62 +1285,54 @@ bool FCEUMOV_ReadState(EMUFILE* is, uint32 size)
closeRecordingMovie();
if(movie_readonly)
if (movie_readonly)
{
int errorFrame = 0;
bool sameTimeline = CheckTimelines(tempMovieData, currMovieData, errorFrame);
if (sameTimeline)
// currFrameCounter at this point represents the savestate framecount
int frame_of_mismatch = CheckTimelines(tempMovieData, currMovieData);
if (frame_of_mismatch >= 0)
{
//if we made it this far, then the savestate has identical movie data but we want to know now if the state frame count is greater than current movie size and make this error
//currFrameCounter at this point represents the savestate framecount
if(currFrameCounter > (int)currMovieData.records.size())
{
//TODO: turn frame counter to red to get attention
if (! (currFrameCounter > (int)tempMovieData.records.size() ) ) //This seemingly redundant check is necessary in order to not flag a post movie savestate as an error
{
if (!backupSavestates) //If backups are disabled we can just resume normally since we can't restore so stop movie and inform user
{
FCEU_PrintError("Error: Savestate is from a frame (%d) after the final frame in the movie (%d). This is not permitted.\nUnable to restore backup, movie playback stopped.", currFrameCounter, currMovieData.records.size()-1);
FCEUI_StopMovie();
}
else
FCEU_PrintError("Savestate is from a frame (%d) after the final frame in the movie (%d). This is not permitted.", currFrameCounter, currMovieData.records.size()-1);
return false;
}
}
else
{
//Final test, if the savestate frame count is > savestate movie length, this was a post movie savestate
//currFrameCounter is currently savestate frame counter (not savestate movie size
if (currFrameCounter > (int)tempMovieData.records.size())
{
FinishPlayback();
}
else
{
//Finally, this is a savestate file for this movie
movieMode = MOVIEMODE_PLAY;
}
}
}
else
{
//Wrong timeline, do apprioriate logic here
// Wrong timeline, do apprioriate logic here
if (!backupSavestates) //If backups are disabled we can just resume normally since we can't restore so stop movie and inform user
{
FCEU_PrintError("Error: Savestate not in the same timeline as movie!\nFrame %d branches from current timeline\nUnable to restore backup, movie playback stopped.", errorFrame);
FCEU_PrintError("Error: Savestate not in the same timeline as movie!\nFrame %d branches from current timeline\nUnable to restore backup, movie playback stopped.", frame_of_mismatch);
FCEUI_StopMovie();
}
else
FCEU_PrintError("Error: Savestate not in the same timeline as movie!\nFrame %d branches from current timeline", errorFrame);
} else
FCEU_PrintError("Error: Savestate not in the same timeline as movie!\nFrame %d branches from current timeline", frame_of_mismatch);
return false;
} else if (movieMode == MOVIEMODE_FINISHED
&& currFrameCounter > (int)currMovieData.records.size()
&& currMovieData.records.size() == tempMovieData.records.size()
&& frame_of_mismatch < 0)
{
// special case (in MOVIEMODE_FINISHED mode)
// allow loading post-movie savestates that were made after finishing current movie
} else if (currFrameCounter > (int)currMovieData.records.size())
{
// this is future event state, don't allow it
//TODO: turn frame counter to red to get attention
if (!backupSavestates) //If backups are disabled we can just resume normally since we can't restore so stop movie and inform user
{
FCEU_PrintError("Error: Savestate is from a frame (%d) after the final frame in the movie (%d). This is not permitted.\nUnable to restore backup, movie playback stopped.", currFrameCounter, currMovieData.records.size()-1);
FCEUI_StopMovie();
} else
FCEU_PrintError("Savestate is from a frame (%d) after the final frame in the movie (%d). This is not permitted.", currFrameCounter, currMovieData.records.size()-1);
return false;
} else if (currFrameCounter > (int)tempMovieData.records.size())
{
// this is post-movie savestate, don't allow it
//TODO: turn frame counter to red to get attention
if (!backupSavestates) //If backups are disabled we can just resume normally since we can't restore so stop movie and inform user
{
FCEU_PrintError("Error: Savestate is from a frame (%d) after the final frame in the savestated movie (%d). This is not permitted.\nUnable to restore backup, movie playback stopped.", currFrameCounter, tempMovieData.records.size()-1);
FCEUI_StopMovie();
} else
FCEU_PrintError("Savestate is from a frame (%d) after the final frame in the savestated movie (%d). This is not permitted.", currFrameCounter, tempMovieData.records.size()-1);
return false;
} else
{
// Finally, this is a savestate file for this movie
movieMode = MOVIEMODE_PLAY;
}
}
else //Read + write