diff --git a/src/drivers/win/res.rc b/src/drivers/win/res.rc index 7e481e9e..026f9208 100644 --- a/src/drivers/win/res.rc +++ b/src/drivers/win/res.rc @@ -336,6 +336,7 @@ BEGIN MENUITEM "&Replay Movie", FCEUX_CONTEXT_REPLAYMOVIE MENUITEM "Record Movie", FCEUX_CONTEXT_RECORDMOVIE MENUITEM SEPARATOR + MENUITEM "Undo savestate", FCEUX_CONTEXT_UNDOSAVESTATE MENUITEM "Undo loadstate", FCEUX_CONTEXT_UNDOLOADSTATE MENUITEM "Rewind to last auto-save", FCEUX_CONTEXT_REWINDTOLASTAUTO MENUITEM "Screenshot", FCEUX_CONTEXT_SCREENSHOT diff --git a/src/drivers/win/resource.h b/src/drivers/win/resource.h index 01da3b4d..53af3f15 100644 --- a/src/drivers/win/resource.h +++ b/src/drivers/win/resource.h @@ -381,7 +381,6 @@ #define MEMW_EDIT03RMADDRESS 1191 #define IDC_DEBUGGER_RESTORESIZE 1191 #define MEMW_EDIT04RMADDRESS 1192 -#define IDC_MOVIE_CANCEL 1192 #define IDC_MOVIE_CLOSE 1192 #define EDIT00_RESULTS 1193 #define IDC_MOVIE_PAUSEAFTERPLAYBACK 1193 @@ -636,6 +635,8 @@ #define FCEUX_CONTEXT_MAKEBACKUP 40320 #define ID_CONFIG_MOVIEOPTIONS 40321 #define MENU_MOVIEOPTIONS 40322 +#define ID_GAME_UNDOSAVESTATE 40323 +#define FCEUX_CONTEXT_UNDOSAVESTATE 40324 #define IDC_DEBUGGER_ICONTRAY 55535 #define MW_ValueLabel2 65423 #define MW_ValueLabel1 65426 @@ -645,7 +646,7 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 125 -#define _APS_NEXT_COMMAND_VALUE 40323 +#define _APS_NEXT_COMMAND_VALUE 40325 #define _APS_NEXT_CONTROL_VALUE 1199 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/src/drivers/win/window.cpp b/src/drivers/win/window.cpp index 66ccce47..92bbe836 100644 --- a/src/drivers/win/window.cpp +++ b/src/drivers/win/window.cpp @@ -93,8 +93,8 @@ char *md5_asciistr(uint8 digest[16]); void ShowNetplayConsole(void); //mbg merge 7/17/06 YECH had to add void MapInput(void); extern BOOL CALLBACK ReplayMetadataDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); //Metadata dialog - extern bool CheckFileExists(const char* filename); //Receives a filename (fullpath) and checks to see if that file exists +extern void SwapSaveState(); //AutoFire----------------------------------------------- void SetAutoFirePattern(int onframes, int offframes); @@ -1301,6 +1301,11 @@ UpdateContextMenuItems(hfceuxcontextsub, whichContext); CreateDialog(fceu_hInstance, "IDD_REPLAY_METADATA", hWnd, ReplayMetadataDialogProc); break; + //Undo Savestate + case FCEUX_CONTEXT_UNDOSAVESTATE: + SwapSaveState(); + break; + //Undo Loadstate case FCEUX_CONTEXT_UNDOLOADSTATE: if (CheckBackupSaveStateExist()) diff --git a/src/movie.cpp b/src/movie.cpp index 30f7c065..eafb742e 100644 --- a/src/movie.cpp +++ b/src/movie.cpp @@ -1301,10 +1301,10 @@ bool CheckFileExists(const char* filename) { //This function simply checks to see if the given filename exists string checkFilename; - + if (filename) checkFilename = filename; - + FCEUI_printf("Checking file %s\n",checkFilename.c_str()); //Check if this filename exists fstream test; test.open(checkFilename.c_str(),fstream::in); @@ -1312,11 +1312,11 @@ bool CheckFileExists(const char* filename) if (test.fail()) { test.close(); - return false; + return false; } else { test.close(); - return true; + return true; } } \ No newline at end of file diff --git a/src/state.cpp b/src/state.cpp index e2a954c4..abf80226 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -59,6 +59,9 @@ static int StateShow; //tells the save system innards that we're loading the old format bool FCEU_state_loading_old_format; +char lastSavestateMade[2048]; //Stores the last savestate made (needed for UndoSavestate) +bool redoSS = false; //This will be true if UndoSaveState is run, will turn false when a new savestate is made + #define SFMDATA_SIZE (64) static SFORMAT SFMDATA[SFMDATA_SIZE]; static int SFEXINDEX; @@ -425,17 +428,37 @@ void FCEUSS_Save(const char *fname) return; } - if(fname) + if(fname) //If filename is given use it. + { + //backup existing savestate first + if (CheckFileExists(fname)) + { + CreateBackupSaveState(fname); //Make a backup of previous savestate before overwriting it + strcpy(lastSavestateMade,fname); //Remember what the last savestate filename was (for undoing later) + redoSS = true; //Backup was created so redo is possible + } + else + redoSS = false; //No backup was needed, so lastSavestateMade does not contain a file that has a backup + st =FCEUD_UTF8_fstream(fname, "wb"); - else + } + else //Else, generate one { //FCEU_PrintError("daCurrentState=%d",CurrentState); fn = strdup(FCEU_MakeFName(FCEUMKF_STATE,CurrentState,0).c_str()); - st = FCEUD_UTF8_fstream(fn,"wb"); //backup existing savestate first - if (CheckFileExists(fn)) CreateBackupSaveState(fn); - + if (CheckFileExists(fn)) + { + CreateBackupSaveState(fn); //Make a backup of previous savestate before overwriting it + strcpy(lastSavestateMade,fn); //Remember what the last savestate filename was (for undoing later) + FCEUI_printf("Last save made: %s\n",lastSavestateMade); + redoSS = true; //Backup was created so redo is possible + } + else + redoSS = false; //No backup was needed, so lastSavestateMade does not contain a file that has a backup + + st = FCEUD_UTF8_fstream(fn,"wb"); free(fn); } @@ -848,6 +871,90 @@ void FCEU_DrawSaveStates(uint8 *XBuf) StateShow--; } +//************************************************************************* +//Savestate backup functions +//(Used when making savestates) +//************************************************************************* + +string GenerateBackupSaveStateFn(const char *fname) +{ + //This backup is for the backup "slot" for any savestate made. Example: smb.fc0 becomes smb-bak.fc0 + string filename; + filename = fname; //Convert fname to a string object + int x = filename.find_last_of("."); //Find file extension + filename.insert(x,"-bak"); //add "-bak" before the dot. + FCEUI_printf("Generating filename of %s\n",filename.c_str()); + return filename; +} + + +void CreateBackupSaveState(const char *fname) +{ + string filename = GenerateBackupSaveStateFn(fname); + std::fstream* st = 0; + st = FCEUD_UTF8_fstream(filename.c_str(),"wb"); + + if(st == NULL) + { + FCEU_DispMessage("State %d save error.",filename.c_str()); + return; + } + + if(FCEUMOV_Mode(MOVIEMODE_INACTIVE)) + FCEUSS_SaveMS(st,-1); + else + FCEUSS_SaveMS(st,0); + + delete st; +} +//--------------------------------------------------------------------------------------------------------------------------------------------------- +//Logic - undo available until a new savestate is made,game is closed, or fcuex is closed + +//What we need is a concept of if you are undoing or redoing as well +//Undo should know there is a bak file before making a decision. the main platform code should know this before calling it. +//Once it is undone, it should know this and call it a redo if called again on the same file. It should remember as long as it knows about undo. +//The undo redo is meaningless information for this function as it simply swaps. +//perhaps a combo of flag(s) and a function to handle flag situations is called for? +//This function would then simply swap the files, which means it is both undo and redo + //And extra function needs to be in charge of flags to keep up with which one is being done, but that is purely cosmetic + +void SwapSaveState() +{ + if (!lastSavestateMade) return; //If there is no last savestate, can't undo + string backup = GenerateBackupSaveStateFn(lastSavestateMade); //Get filename of backup state + if (!CheckFileExists(backup.c_str())) + { + FCEUI_printf("%s exists",backup.c_str()); + return; //If no backup, can't undo + } + + //-------------------------------------------------------------------------------------------- + //So both exists, now swap the last savestate and its backup + //-------------------------------------------------------------------------------------------- + string temp; //Create a temp string object + temp = backup; //Put backup filename in temp + temp.append("x"); //Add x + + FILE* backupf = fopen(backup.c_str(),"r"); //Open backup file + FILE* currentf = fopen(lastSavestateMade,"w"); //create temp file + + rename(backup.c_str(),temp.c_str()); //rename backup file to temp file + rename(lastSavestateMade,backup.c_str()); //rename current as backup + rename(temp.c_str(),lastSavestateMade); //rename backup as current + + fclose(backupf); //Cleanup, close backup file + fclose(currentf); //Cleanup, close current file + FCEUI_DispMessage("%s restored",backup.c_str()); + FCEUI_printf("%s restored\n",backup.c_str()); + //TODO: deal with error handling if any of these files does/doesn't exist unexpectedly +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------ +//************************************************************************* +//Loadstate backup functions +//(Used when Loading savestates) +//************************************************************************* + string GetBackupFileName() { //This backup savestate is a special one specifically made whenever a loadstate occurs so that the user's place in a movie/game is never lost @@ -863,30 +970,6 @@ string GetBackupFileName() return filename; } -void CreateBackupSaveState(const char *fname) -{ - string filename; - filename = fname; //Convert fname to a string object - int x = filename.find_last_of("."); //Find file extension - filename.insert(x-1,"-bak"); //add "-bak" before the dot. Ex: smb.fc0 becomes smb-bak.fc0 - - std::fstream* st = 0; - st = FCEUD_UTF8_fstream(filename.c_str(),"wb"); - - if(st == NULL) - { - FCEU_DispMessage("State %d save error.",CurrentState); - return; - } - - if(FCEUMOV_Mode(MOVIEMODE_INACTIVE)) - FCEUSS_SaveMS(st,-1); - else - FCEUSS_SaveMS(st,0); - - delete st; -} - void BackupLoadState() { string filename = GetBackupFileName(); diff --git a/src/state.h b/src/state.h index 2f9ad274..734c2743 100644 --- a/src/state.h +++ b/src/state.h @@ -63,4 +63,5 @@ void FCEU_DrawSaveStates(uint8 *XBuf); void CreateBackupSaveState(const char *fname); //backsup a savestate before overwriting it with a new one void BackupLoadState(); //Makes a backup savestate before any loadstate -void LoadBackup(); //Loads the backupsavestate \ No newline at end of file +void LoadBackup(); //Loads the backupsavestate +void SwapSaveState(); //Swaps a savestate with its backup state \ No newline at end of file