bool mov_loadstate(EMUFILE* fp, int size) { load_successful = false; u32 cookie; if(read32le(&cookie,fp) != 1) return false; if(cookie == kNOMO) { if(movieMode == MOVIEMODE_RECORD || movieMode == MOVIEMODE_PLAY) FinishPlayback(); return true; } else if(cookie != kMOVI) return false; size -= 4; if (!movie_readonly && autoMovieBackup && freshMovie) //If auto-backup is on, movie has not been altered this session and the movie is in read+write mode { FCEUI_MakeBackupMovie(false); //Backup the movie before the contents get altered, but do not display messages } //a little rule: cant load states in read+write mode with a movie from an archive. //so we are going to switch it to readonly mode in that case // if(!movie_readonly // //*&& FCEU_isFileInArchive(curMovieFilename)*/ // ) { // FCEU_PrintError("Cannot loadstate in Read+Write with movie from archive. Movie is now Read-Only."); // movie_readonly = true; // } MovieData tempMovieData = MovieData(); //int curr = fp->ftell(); if(!LoadFM2(tempMovieData, fp, size, false)) { // is->seekg((uint32)curr+size); /* extern bool FCEU_state_loading_old_format; if(FCEU_state_loading_old_format) { if(movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_RECORD) { FCEUI_StopMovie(); FCEU_PrintError("You have tried to use an old savestate while playing a movie. This is unsupported (since the old savestate has old-format movie data in it which can't be converted on the fly)"); } }*/ return false; } //---------------- //complex TAS logic for loadstate //fully conforms to the savestate logic documented in the Laws of TAS //http://tasvideos.org/LawsOfTAS/OnSavestates.html //---------------- /* Playback or Recording + Read-only * Check that GUID of movie and savestate-movie must match or else error o on error: a message informing that the savestate doesn't belong to this movie. This is a GUID mismatch error. Give user a choice to load it anyway. + failstate: if use declines, loadstate attempt canceled, movie can resume as if not attempted if user has backup savstates enabled else stop movie * 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 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. * All error checks have passed, state will be loaded * Movie contained in the savestate will be discarded * Movie is now in Playback mode Playback, Recording + Read+write * Check that GUID of movie and savestate-movie must match or else error o on error: a message informing that the savestate doesn't belong to this movie. This is a GUID mismatch error. Give user a choice to load it anyway. + failstate: if use declines, loadstate attempt canceled, movie can resume as if not attempted (stop movie if resume fails)canceled, movie can resume if backup savestates enabled else stopmovie * Check that savestate framcount <= savestate movie length. If not this is a post-movie savestate o on post-movie: See post-movie event section. * savestate passed all error checks and will now be loaded in its entirety and replace movie (note: there will be no truncation yet) * current framecount will be set to savestate framecount * on the next frame of input, movie will be truncated to framecount o (note: savestate-movie can be a future event of the movie timeline, or a completely new timeline and it is still allowed) * Rerecord count of movie will be incremented * Movie is now in record mode Post-movie savestate event * Whan a savestate is loaded and is determined that the savestate-movie length is less than the savestate framecount then it is a post-movie savestate. These occur when a savestate was made during Movie Finished mode. * If read+write, the entire savestate movie will be loaded and replace current movie. * If read-only, the savestate movie will be discarded * Mode will be switched to Move Finished * Savestate will be loaded * Current framecount changes to savestate framecount * User will have control of input as if Movie inactive mode */ if(movieMode != MOVIEMODE_INACTIVE) { //handle moviefile mismatch if(tempMovieData.guid != currMovieData.guid) { //mbg 8/18/08 - this code can be used to turn the error message into an OK/CANCEL #if defined(WIN32) && !defined(WXPORT) std::string msg = "There is a mismatch between savestate's movie and current movie.\ncurrent: " + currMovieData.guid.toString() + "\nsavestate: " + tempMovieData.guid.toString() + "\n\nThis means that you have loaded a savestate belonging to a different movie than the one you are playing now.\n\nContinue loading this savestate anyway?"; int result = MessageBox(MainWindow->getHWnd(),msg.c_str(),"Error loading savestate",MB_OKCANCEL); if(result == IDCANCEL) return false; #else FCEU_PrintError("Mismatch between savestate's movie and current movie.\ncurrent: %s\nsavestate: %s\n",currMovieData.guid.toString().c_str(),tempMovieData.guid.toString().c_str()); return false; #endif } closeRecordingMovie(); if(!movie_readonly) { currMovieData = tempMovieData; currMovieData.rerecordCount = currRerecordCount; } if(currFrameCounter > (int)currMovieData.records.size()) { // if the frame counter is longer than our current movie, // switch to "finished" mode. // this is a mode that behaves like "inactive" // except it permits switching to play/record by loading an earlier savestate. // (and we continue to store the finished movie in savestates made while finished) osd->setLineColor(255,0,0); // let's make the text red too to hopefully catch the user's attention a bit. FinishPlayback(); osd->setLineColor(255,255,255); //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(movie_readonly) { //------------------------------------------------------------- //this code would reload the movie from disk. allegedly it is helpful to hexers, but //it is way too slow with dsm format. so it is only here as a reminder, and in case someone //wants to play with it //------------------------------------------------------------- //{ // fstream fs (curMovieFilename); // if(!LoadFM2(tempMovieData, &fs, INT_MAX, false)) // { // FCEU_PrintError("Failed to reload DSM after loading savestate"); // } // fs.close(); // currMovieData = tempMovieData; //} //------------------------------------------------------------- movieMode = MOVIEMODE_PLAY; } else { // #ifdef _S9XLUA_H // if(!FCEU_LuaRerecordCountSkip()) currRerecordCount++; // #endif currMovieData.rerecordCount = currRerecordCount; currMovieData.truncateAt(currFrameCounter); openRecordingMovie(curMovieFilename); if(!osRecordingMovie) { osd->setLineColor(255, 0, 0); osd->addLine("Can't save movie file!"); } //printf("DUMPING MOVIE: %d FRAMES\n",currMovieData.records.size()); currMovieData.dump(osRecordingMovie, false); movieMode = MOVIEMODE_RECORD; } } load_successful = true; freshMovie = false; return true; }
//the main interaction point between the emulator and the movie system. //either dumps the current joystick state or loads one state from the movie void FCEUMOV_AddInputState() { #if defined(WIN32) && !defined(DINGUX) if (movieMode == MOVIEMODE_TASEDITOR) { // if movie length is less or equal to currFrame, pad it with empty frames if (((int)currMovieData.records.size() - 1) < (currFrameCounter + 1)) currMovieData.insertEmpty(-1, (currFrameCounter + 1) - ((int)currMovieData.records.size() - 1)); MovieRecord* mr = &currMovieData.records[currFrameCounter]; if (isTaseditorRecording()) { // record commands and buttons mr->commands |= _currCommand; joyports[0].log(mr); joyports[1].log(mr); recordInputByTaseditor(); } // replay buttons joyports[0].load(mr); joyports[1].load(mr); // replay commands if (mr->command_power()) PowerNES(); if (mr->command_reset()) ResetNES(); if (mr->command_fds_insert()) FCEU_FDSInsert(); if (mr->command_fds_select()) FCEU_FDSSelect(); if (mr->command_vs_insertcoin()) FCEU_VSUniCoin(); _currCommand = 0; } else #endif if (movieMode == MOVIEMODE_PLAY) { //stop when we run out of frames if (currFrameCounter >= (int)currMovieData.records.size()) { FinishPlayback(); //tell all drivers to poll input and set up their logical states for(int port=0;port<2;port++) joyports[port].driver->Update(port,joyports[port].ptr,joyports[port].attrib); portFC.driver->Update(portFC.ptr,portFC.attrib); } else { MovieRecord* mr = &currMovieData.records[currFrameCounter]; //reset and power cycle if necessary if(mr->command_power()) PowerNES(); if(mr->command_reset()) ResetNES(); if(mr->command_fds_insert()) FCEU_FDSInsert(); if(mr->command_fds_select()) FCEU_FDSSelect(); if (mr->command_vs_insertcoin()) FCEU_VSUniCoin(); joyports[0].load(mr); joyports[1].load(mr); } //if we are on the last frame, then pause the emulator if the player requested it if (currFrameCounter == currMovieData.records.size()-1) { if(FCEUD_PauseAfterPlayback()) { FCEUI_ToggleEmulationPause(); } } //pause the movie at a specified frame if (FCEUMOV_ShouldPause() && FCEUI_EmulationPaused()==0) { FCEUI_ToggleEmulationPause(); FCEU_DispMessage("Paused at specified movie frame",0); } } else if (movieMode == MOVIEMODE_RECORD) { MovieRecord mr; joyports[0].log(&mr); joyports[1].log(&mr); mr.commands = _currCommand; _currCommand = 0; //Adelikat: in normal mode, this is done at the time of loading a savestate in read+write mode //If the user chooses it can be delayed to here if (fullSaveStateLoads && (currFrameCounter < (int)currMovieData.records.size())) currMovieData.truncateAt(currFrameCounter); mr.dump(&currMovieData, osRecordingMovie,currMovieData.records.size()); // to disk currMovieData.records.push_back(mr); } currFrameCounter++; extern uint8 joy[4]; memcpy(&cur_input_display,joy,4); }
bool mov_loadstate(std::istream* is, int size)//std::istream* is { load_successful = false; int cookie; if(read32lemov(&cookie,is) != 1) return false; if(cookie == kNOMO) return true; else if(cookie != kMOVI) return false; size -= 4; if (!movie_readonly && autoMovieBackup && freshMovie) //If auto-backup is on, movie has not been altered this session and the movie is in read+write mode MakeBackupMovie(false); //Backup the movie before the contents get altered, but do not display messages MovieData tempMovieData = MovieData(); std::ios::pos_type curr = is->tellg(); if(!LoadMC2(tempMovieData, is, size, false)) return false; //---------------- //complex TAS logic for loadstate //fully conforms to the savestate logic documented in the Laws of TAS //http://tasvideos.org/LawsOfTAS/OnSavestates.html //---------------- /* Playback or Recording + Read-only * Check that GUID of movie and savestate-movie must match or else error o on error: a message informing that the savestate doesn't belong to this movie. This is a GUID mismatch error. Give user a choice to load it anyway. + failstate: if use declines, loadstate attempt canceled, movie can resume as if not attempted if user has backup savstates enabled else stop movie * 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 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. * All error checks have passed, state will be loaded * Movie contained in the savestate will be discarded * Movie is now in Playback mode Playback, Recording + Read+write * Check that GUID of movie and savestate-movie must match or else error o on error: a message informing that the savestate doesn't belong to this movie. This is a GUID mismatch error. Give user a choice to load it anyway. + failstate: if use declines, loadstate attempt canceled, movie can resume as if not attempted (stop movie if resume fails)canceled, movie can resume if backup savestates enabled else stopmovie * Check that savestate framcount <= savestate movie length. If not this is a post-movie savestate o on post-movie: See post-movie event section. * savestate passed all error checks and will now be loaded in its entirety and replace movie (note: there will be no truncation yet) * current framecount will be set to savestate framecount * on the next frame of input, movie will be truncated to framecount o (note: savestate-movie can be a future event of the movie timeline, or a completely new timeline and it is still allowed) * Rerecord count of movie will be incremented * Movie is now in record mode Post-movie savestate event * Whan a savestate is loaded and is determined that the savestate-movie length is less than the savestate framecount then it is a post-movie savestate. These occur when a savestate was made during Movie Finished mode. * If read+write, the entire savestate movie will be loaded and replace current movie. * If read-only, the savestate movie will be discarded * Mode will be switched to Move Finished * Savestate will be loaded * Current framecount changes to savestate framecount * User will have control of input as if Movie inactive mode */ if(movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_RECORD) { //handle moviefile mismatch /* if(tempMovieData.guid != currMovieData.guid) { //mbg 8/18/08 - this code can be used to turn the error message into an OK/CANCEL #ifdef WIN32 //std::string msg = "There is a mismatch between savestate's movie and current movie.\ncurrent: " + currMovieData.guid.toString() + "\nsavestate: " + tempMovieData.guid.toString() + "\n\nThis means that you have loaded a savestate belonging to a different movie than the one you are playing now.\n\nContinue loading this savestate anyway?"; //extern HWND pwindow; //int result = MessageBox(pwindow,msg.c_str(),"Error loading savestate",MB_OKCANCEL); //if(result == IDCANCEL) // return false; #else DisplayMessage("Mismatch between savestate's movie and current movie.\ncurrent: %s\nsavestate: %s\n",currMovieData.guid.toString().c_str(),tempMovieData.guid.toString().c_str()); return false; #endif }*/ closeRecordingMovie(); if(movie_readonly) { //------------------------------------------------------------- //this code would reload the movie from disk. allegedly it is helpful to hexers, but //it is way too slow with dsm format. so it is only here as a reminder, and in case someone //wants to play with it //------------------------------------------------------------- //{ // fstream fs (curMovieFilename); // if(!LoadMC2(tempMovieData, &fs, INT_MAX, false)) // { // MDFN_PrintError("Failed to reload DSM after loading savestate"); // } // fs.close(); // currMovieData = tempMovieData; //} //------------------------------------------------------------- //if the frame counter is longer than our current movie, then error if(currFrameCounter > (int)currMovieData.records.size()) { MDFN_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; } movieMode = MOVIEMODE_PLAY; } else { //truncate before we copy, just to save some time tempMovieData.truncateAt(currFrameCounter); currMovieData = tempMovieData; currRerecordCount++; currMovieData.rerecordCount = currRerecordCount; openRecordingMovie(curMovieFilename); currMovieData.dump(osRecordingMovie, false); movieMode = MOVIEMODE_RECORD; } } load_successful = true; freshMovie = false; //// Maximus: Show the last input combination entered from the //// movie within the state //if(current!=0) // <- mz: only if playing or recording a movie // memcpy(&cur_input_display, joop, 4); return true; }
bool FCEUMOV_ReadState(EMUFILE* is, uint32 size) { load_successful = false; if (!movie_readonly) { if (currMovieData.loadFrameCount >= 0) { #if defined(WIN32) && !defined(DINGUX) int result = MessageBox(hAppWnd, "This movie is a TAS Editor project file.\nIt can be modified in TAS Editor only.\n\nOpen it in TAS Editor now?", "Movie Replay", MB_YESNO); if (result == IDYES) mustEngageTaseditor = true; #else FCEUI_printf("This movie is a TAS Editor project file! It can be modified in TAS Editor only.\nMovie is now Read-Only.\n"); #endif movie_readonly = true; } if (FCEU_isFileInArchive(curMovieFilename)) { //a little rule: cant load states in read+write mode with a movie from an archive. //so we are going to switch it to readonly mode in that case FCEU_PrintError("Cannot loadstate in Read+Write with movie from archive. Movie is now Read-Only."); movie_readonly = true; } } MovieData tempMovieData = MovieData(); std::ios::pos_type curr = is->ftell(); if(!LoadFM2(tempMovieData, is, size, false)) { is->fseek((uint32)curr+size,SEEK_SET); extern bool FCEU_state_loading_old_format; if(FCEU_state_loading_old_format) { if(movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_RECORD || movieMode == MOVIEMODE_FINISHED) { //FCEUI_StopMovie(); //No reason to stop the movie, nothing destructive has happened yet. FCEU_PrintError("You have tried to use an old savestate while playing a movie. This is unsupported (since the old savestate has old-format movie data in it which can't be converted on the fly)"); } } return false; } //---------------- //complex TAS logic for loadstate //fully conforms to the savestate logic documented in the Laws of TAS //http://tasvideos.org/LawsOfTAS/OnSavestates.html //---------------- /* Playback or Recording + Read-only * Check that GUID of movie and savestate-movie must match or else error o on error: a message informing that the savestate doesn't belong to this movie. This is a GUID mismatch error. Give user a choice to load it anyway. + failstate: if use declines, loadstate attempt canceled, movie can resume as if not attempted if user has backup savstates enabled else stop movie * 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 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 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 Playback, Recording + Read+write * Check that GUID of movie and savestate-movie must match or else error o on error: a message informing that the savestate doesn't belong to this movie. This is a GUID mismatch error. Give user a choice to load it anyway. + failstate: if use declines, loadstate attempt canceled, movie can resume as if not attempted (stop movie if resume fails)canceled, movie can resume if backup savestates enabled else stopmovie * Check that savestate framcount <= savestate movie length. If not this is a post-movie savestate o on post-movie: See post-movie event section. * savestate passed all error checks and will now be loaded in its entirety and replace movie (note: there will be no truncation yet) * current framecount will be set to savestate framecount * on the next frame of input, movie will be truncated to framecount o (note: savestate-movie can be a future event of the movie timeline, or a completely new timeline and it is still allowed) * Rerecord count of movie will be incremented * Movie is now in record mode Post-movie savestate event * Whan a savestate is loaded and is determined that the savestate-movie length is less than the savestate framecount then it is a post-movie savestate. These occur when a savestate was made during Movie Finished mode. * If read+write, the entire savestate movie will be loaded and replace current movie. * If read-only, the savestate movie will be discarded * Mode will be switched to Move Finished * Savestate will be loaded * Current framecount changes to savestate framecount * User will have control of input as if Movie inactive mode */ if(movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_RECORD || movieMode == MOVIEMODE_FINISHED) { //handle moviefile mismatch if(tempMovieData.guid != currMovieData.guid) { //mbg 8/18/08 - this code can be used to turn the error message into an OK/CANCEL #if defined(WIN32) && !defined(DINGUX) std::string msg = "There is a mismatch between savestate's movie and current movie.\ncurrent: " + currMovieData.guid.toString() + "\nsavestate: " + tempMovieData.guid.toString() + "\n\nThis means that you have loaded a savestate belonging to a different movie than the one you are playing now.\n\nContinue loading this savestate anyway?"; extern HWND pwindow; int result = MessageBox(pwindow,msg.c_str(),"Error loading savestate",MB_OKCANCEL); if(result == IDCANCEL) { if (!backupSavestates) //If backups are disabled we can just resume normally since we can't restore so stop movie and inform user { FCEU_PrintError("Unable to restore backup, movie playback stopped."); FCEUI_StopMovie(); } return false; } #else if (!backupSavestates) //If backups are disabled we can just resume normally since we can't restore so stop movie and inform user { FCEU_PrintError("Mismatch between savestate's movie and current movie.\ncurrent: %s\nsavestate: %s\nUnable to restore backup, movie playback stopped.\n",currMovieData.guid.toString().c_str(),tempMovieData.guid.toString().c_str()); FCEUI_StopMovie(); } else FCEU_PrintError("Mismatch between savestate's movie and current movie.\ncurrent: %s\nsavestate: %s\n",currMovieData.guid.toString().c_str(),tempMovieData.guid.toString().c_str()); return false; #endif } closeRecordingMovie(); if (movie_readonly) { // currFrameCounter at this point represents the savestate framecount int frame_of_mismatch = CheckTimelines(tempMovieData, currMovieData); if (frame_of_mismatch >= 0) { // 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.", frame_of_mismatch); FCEUI_StopMovie(); } 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()) { // 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 mode if (currFrameCounter > (int)tempMovieData.records.size()) { //This is a post movie savestate, handle it differently //Replace movie contents but then switch to movie finished mode currMovieData = tempMovieData; openRecordingMovie(curMovieFilename); currMovieData.dump(osRecordingMovie, false/*currMovieData.binaryFlag*/); FinishPlayback(); } else { //truncate before we copy, just to save some time, unless the user selects a full copy option if (!fullSaveStateLoads) //we can only assume this here since we have checked that the frame counter is not greater than the movie data tempMovieData.truncateAt(currFrameCounter); currMovieData = tempMovieData; FCEUMOV_IncrementRerecordCount(); openRecordingMovie(curMovieFilename); currMovieData.dump(osRecordingMovie, false/*currMovieData.binaryFlag*/); movieMode = MOVIEMODE_RECORD; } } } load_successful = true; return true; }
static bool FCEUMOV_ReadState(std::istream* is, uint32 size) { load_successful = false; //a little rule: cant load states in read+write mode with a movie from an archive. //so we are going to switch it to readonly mode in that case if(!movie_readonly //*&& FCEU_isFileInArchive(curMovieFilename)*/ ) { FCEU_PrintError("Cannot loadstate in Read+Write with movie from archive. Movie is now Read-Only."); movie_readonly = true; } MovieData tempMovieData = MovieData(); std::ios::pos_type curr = is->tellg(); if(!LoadFM2(tempMovieData, is, size, false)) { /*is->seekg((uint32)curr+size); extern bool FCEU_state_loading_old_format; if(FCEU_state_loading_old_format) { if(movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_RECORD) { FCEUI_StopMovie(); FCEU_PrintError("You have tried to use an old savestate while playing a movie. This is unsupported (since the old savestate has old-format movie data in it which can't be converted on the fly)"); } }*/ return false; } //complex TAS logic for when a savestate is loaded: //---------------- //if we are playing or recording and toggled read-only: // then, the movie we are playing must match the guid of the one stored in the savestate or else error. // the savestate is assumed to be in the same timeline as the current movie. // if the current movie is not long enough to get to the savestate's frame#, then it is an error. // the movie contained in the savestate will be discarded. // the emulator will be put into play mode. //if we are playing or recording and toggled read+write // then, the movie we are playing must match the guid of the one stored in the savestate or else error. // the movie contained in the savestate will be loaded into memory // the frames in the movie after the savestate frame will be discarded // the in-memory movie will have its rerecord count incremented // the in-memory movie will be dumped to disk as fcm. // the emulator will be put into record mode. //if we are doing neither: // then, we must discard this movie and just load the savestate if(movieMode == MOVIEMODE_PLAY || movieMode == MOVIEMODE_RECORD) { //handle moviefile mismatch if(tempMovieData.guid != currMovieData.guid) { //mbg 8/18/08 - this code can be used to turn the error message into an OK/CANCEL #ifdef WIN32 //std::string msg = "There is a mismatch between savestate's movie and current movie.\ncurrent: " + currMovieData.guid.toString() + "\nsavestate: " + tempMovieData.guid.toString() + "\n\nThis means that you have loaded a savestate belonging to a different movie than the one you are playing now.\n\nContinue loading this savestate anyway?"; //extern HWND pwindow; //int result = MessageBox(pwindow,msg.c_str(),"Error loading savestate",MB_OKCANCEL); //if(result == IDCANCEL) // return false; #else FCEU_PrintError("Mismatch between savestate's movie and current movie.\ncurrent: %s\nsavestate: %s\n",currMovieData.guid.toString().c_str(),tempMovieData.guid.toString().c_str()); return false; #endif } closeRecordingMovie(); if(movie_readonly) { //if the frame counter is longer than our current movie, then error if(currFrameCounter > (int)currMovieData.records.size()) { 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; } movieMode = MOVIEMODE_PLAY; } else { //truncate before we copy, just to save some time tempMovieData.truncateAt(currFrameCounter); currMovieData = tempMovieData; #ifdef _S9XLUA_H if(!FCEU_LuaRerecordCountSkip()) currRerecordCount++; #endif currMovieData.rerecordCount = currRerecordCount; openRecordingMovie(curMovieFilename); currMovieData.dump(osRecordingMovie, false); movieMode = MOVIEMODE_RECORD; } } load_successful = true; //// Maximus: Show the last input combination entered from the //// movie within the state //if(current!=0) // <- mz: only if playing or recording a movie // memcpy(&cur_input_display, joop, 4); return true; }