diff --git a/src/movie.cpp b/src/movie.cpp index 09a6aa60..fb22fd58 100644 --- a/src/movie.cpp +++ b/src/movie.cpp @@ -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