diff --git a/win32/CD3DCG.cpp b/win32/CD3DCG.cpp index f15de2d1..1d51dc24 100644 --- a/win32/CD3DCG.cpp +++ b/win32/CD3DCG.cpp @@ -683,6 +683,8 @@ void CD3DCG::setShaderVars(int pass) setProgramUniform(pass,"IN.texture_size",&textureSize); setProgramUniform(pass,"IN.output_size",&outputSize); setProgramUniform(pass,"IN.frame_count",&frameCnt); + float frameDirection = GUI.rewinding?-1.0f:1.0f; + setProgramUniform(pass,"IN.frame_direction",&frameDirection); /* ORIG parameter */ diff --git a/win32/CGLCG.cpp b/win32/CGLCG.cpp index 7f0032a8..0d75a999 100644 --- a/win32/CGLCG.cpp +++ b/win32/CGLCG.cpp @@ -667,6 +667,7 @@ void CGLCG::setShaderVars(int pass) setProgram2fv(pass,"IN.texture_size",textureSize); setProgram2fv(pass,"IN.output_size",outputSize); setProgram1f(pass,"IN.frame_count",(float)frameCnt); + setProgram1f(pass,"IN.frame_direction",GUI.rewinding?-1.0f:1.0f); /* ORIG parameter */ diff --git a/win32/InputCustom.cpp b/win32/InputCustom.cpp index d86813ad..39f2cc0c 100644 --- a/win32/InputCustom.cpp +++ b/win32/InputCustom.cpp @@ -688,6 +688,8 @@ int GetNumHotKeysAssignedTo (WORD Key, int modifiers) if(MATCHES_KEY(TurboDown)) count++; if(MATCHES_KEY(ResetGame)) count++; if(MATCHES_KEY(ToggleCheats)) count++; + if(MATCHES_KEY(QuitS9X)) count++; + if(MATCHES_KEY(Rewind)) count++; #undef MATCHES_KEY } diff --git a/win32/StateManager.cpp b/win32/StateManager.cpp new file mode 100644 index 00000000..dcef99f8 --- /dev/null +++ b/win32/StateManager.cpp @@ -0,0 +1,191 @@ +#include "statemanager.h" +#include "snapshot.h" + +/* Snapshot Manager Class that records snapshot data for rewinding + mostly based on SSNES's rewind code by Themaister +*/ + +static inline size_t nearest_pow2_size(size_t v) +{ + size_t orig = v; + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; +#if SIZE_MAX >= 0xffff + v |= v >> 8; +#endif +#if SIZE_MAX >= 0xffffffff + v |= v >> 16; +#endif +#if SIZE_MAX >= 0xffffffffffffffff + v |= v >> 32; +#endif + v++; + + size_t next = v; + size_t prev = v >> 1; + + if ((next - orig) < (orig - prev)) + return next; + else + return prev; +} + +void StateManager::deallocate() { + if(buffer) { + delete [] buffer; + buffer = NULL; + } + if(tmp_state) { + delete [] tmp_state; + tmp_state = NULL; + } + if(in_state) { + delete [] in_state; + in_state = NULL; + } +} + +StateManager::StateManager() +{ + buffer = NULL; + tmp_state = NULL; + in_state = NULL; + init_done = false; +} + +StateManager::~StateManager() { + deallocate(); +} + +bool StateManager::init(size_t buffer_size) { + + init_done = false; + + deallocate(); + + real_state_size = S9xFreezeSize(); + state_size = real_state_size / sizeof(uint32_t); // Works in multiple of 4. + + // We need 4-byte aligned state_size to avoid having to enforce this with unneeded memcpy's! + if(real_state_size % sizeof(uint32_t)) state_size ++; + + if (buffer_size <= real_state_size) // Need a sufficient buffer size. + return false; + + top_ptr = 1; + + + buf_size = nearest_pow2_size(buffer_size) / sizeof(uint64_t); // Works in multiple of 8. + buf_size_mask = buf_size - 1; + + if (!(buffer = new uint64_t[buf_size])) + return false; + if (!(tmp_state = new uint32_t[state_size])) + return false; + if (!(in_state = new uint32_t[state_size])) + return false; + + memset(tmp_state,0,state_size * sizeof(uint32_t)); + memset(in_state,0,state_size * sizeof(uint32_t)); + + init_done = true; + + return true; +} + +int StateManager::pop() +{ + if(!init_done) + return 0; + + if (first_pop) + { + first_pop = false; + return S9xUnfreezeGameMem((uint8 *)tmp_state,real_state_size); + } + + top_ptr = (top_ptr - 1) & buf_size_mask; + + if (top_ptr == bottom_ptr) // Our stack is completely empty... :v + { + top_ptr = (top_ptr + 1) & buf_size_mask; + return 0; + } + + while (buffer[top_ptr]) + { + // Apply the xor patch. + uint32_t addr = buffer[top_ptr] >> 32; + uint32_t xor_ = buffer[top_ptr] & 0xFFFFFFFFU; + tmp_state[addr] ^= xor_; + + top_ptr = (top_ptr - 1) & buf_size_mask; + } + + if (top_ptr == bottom_ptr) // Our stack is completely empty... :v + { + top_ptr = (top_ptr + 1) & buf_size_mask; + } + + return S9xUnfreezeGameMem((uint8 *)tmp_state,real_state_size); +} + +void StateManager::reassign_bottom() +{ + bottom_ptr = (top_ptr + 1) & buf_size_mask; + while (buffer[bottom_ptr]) // Skip ahead until we find the first 0 (boundary for state delta). + bottom_ptr = (bottom_ptr + 1) & buf_size_mask; +} + +void StateManager::generate_delta(const void *data) +{ + bool crossed = false; + const uint32_t *old_state = tmp_state; + const uint32_t *new_state = (const uint32_t*)data; + + buffer[top_ptr++] = 0; // For each separate delta, we have a 0 value sentinel in between. + top_ptr &= buf_size_mask; + + // Check if top_ptr and bottom_ptr crossed each other, which means we need to delete old cruft. + if (top_ptr == bottom_ptr) + crossed = true; + + for (uint64_t i = 0; i < state_size; i++) + { + uint64_t xor_ = old_state[i] ^ new_state[i]; + + // If the data differs (xor != 0), we push that xor on the stack with index and xor. + // This can be reversed by reapplying the xor. + // This, if states don't really differ much, we'll save lots of space :) + // Hopefully this will work really well with save states. + if (xor_) + { + buffer[top_ptr] = (i << 32) | xor_; + top_ptr = (top_ptr + 1) & buf_size_mask; + + if (top_ptr == bottom_ptr) + crossed = true; + } + } + + if (crossed) + reassign_bottom(); +} + +bool StateManager::push() +{ + if(!init_done) + return false; + if(!S9xFreezeGameMem((uint8 *)in_state,real_state_size)) + return false; + generate_delta(in_state); + uint32 *tmp = tmp_state; + tmp_state = in_state; + in_state = tmp; + + first_pop = true; + + return true; +} diff --git a/win32/StateManager.h b/win32/StateManager.h new file mode 100644 index 00000000..65aabb30 --- /dev/null +++ b/win32/StateManager.h @@ -0,0 +1,35 @@ +#ifndef STATEMANAGER_H +#define STATEMANAGER_H + +/* Snapshot Manager Class that records snapshot data for rewinding + mostly based on SSNES's rewind code by Themaister +*/ + +#include "snes9x.h" + +class StateManager { +private: + uint64_t *buffer; + size_t buf_size; + size_t buf_size_mask; + uint32_t *tmp_state; + uint32_t *in_state; + size_t top_ptr; + size_t bottom_ptr; + size_t state_size; + size_t real_state_size; + bool init_done; + bool first_pop; + + void reassign_bottom(); + void generate_delta(const void *data); + void deallocate(); +public: + StateManager(); + ~StateManager(); + bool init(size_t buffer_size); + int pop(); + bool push(); +}; + +#endif // STATEMANAGER_H diff --git a/win32/rsrc/resource.h b/win32/rsrc/resource.h index 90d5997f..04b0b0e8 100644 --- a/win32/rsrc/resource.h +++ b/win32/rsrc/resource.h @@ -81,7 +81,11 @@ #define IDC_HEADER 1067 #define IDC_HIRESAVI 1067 #define IDC_ROMLIST 1068 +#define IDC_REWIND_BUFFER 1068 #define IDC_MEM_TYPE 1069 +#define IDC_REWIND_BUFFER_SPIN 1069 +#define IDC_REWIND_GRANULARITY 1070 +#define IDC_REWIND_GRANULARITY_SPIN 1071 #define IDC_HOSTNAME 1086 #define IDC_PORTNUMBER 1087 #define IDC_CLEARHISTORY 1088 @@ -226,12 +230,17 @@ #define IDC_LABEL_UP4 1183 #define IDC_PORTNUMBLOCK 1184 #define IDC_LABEL_UP5 1184 +#define IDC_LABEL_RBUFFER 1184 #define IDC_CLIENTSETTINGSBLOCK 1185 #define IDC_LABEL_UP6 1185 +#define IDC_LABEL_RBUFFER_TEXT 1185 #define IDC_SERVERSETTINGSBLOCK 1186 #define IDC_LABEL_UP7 1186 +#define IDC_LABEL_RGRANU 1186 #define IDC_LABEL_PORTNUM 1187 #define IDC_LABEL_UP8 1187 +#define IDC_LABEL_RBUFFER_TEXT2 1187 +#define IDC_LABEL_GRANU_TEXT 1187 #define IDC_LABEL_PAUSEINTERVAL 1188 #define IDC_LABEL_UP9 1188 #define IDC_LABEL_PAUSEINTERVAL_TEXT 1189 @@ -485,7 +494,7 @@ #define ID_WINDOW_SIZE_3X 40171 #define ID_WINDOW_SIZE_4X 40172 #define ID_DEBUG_APU_TRACE 40173 -#define ID_EMULATION_BACKGROUNDINPUT 40174 +#define ID_EMULATION_BACKGROUNDINPUT 40174 // Next default values for new objects // diff --git a/win32/rsrc/snes9x.rc b/win32/rsrc/snes9x.rc index e5e1b992..2aa4637f 100644 --- a/win32/rsrc/snes9x.rc +++ b/win32/rsrc/snes9x.rc @@ -96,10 +96,10 @@ CAPTION "APP - About Dialog" FONT 8, "MS Sans Serif", 0, 0, 0x1 BEGIN DEFPUSHBUTTON "OK",IDOK,90,160,50,14 - EDITTEXT IDC_DISCLAIMER,7,7,218,148,ES_MULTILINE | ES_NOHIDESEL | WS_VSCROLL | ES_READONLY,WS_EX_STATICEDGE + EDITTEXT IDC_DISCLAIMER,7,7,218,148,ES_MULTILINE | ES_NOHIDESEL | ES_READONLY | WS_VSCROLL,WS_EX_STATICEDGE END -IDD_EMU_SETTINGS DIALOGEX 0, 0, 319, 154 +IDD_EMU_SETTINGS DIALOGEX 0, 0, 319, 184 STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_CENTER | WS_POPUP | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU CAPTION "APP - Emulator Settings" FONT 8, "MS Sans Serif", 0, 0, 0x0 @@ -112,9 +112,17 @@ BEGIN CONTROL "Spin3",IDC_SPIN_MAX_SKIP,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,298,70,11,13 EDITTEXT IDC_TURBO_SKIP,91,85,49,14,ES_AUTOHSCROLL | ES_NUMBER CONTROL "Spin4",IDC_SPIN_TURBO_SKIP,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,296,94,11,13 - CONTROL "Toggled Turbo Mode",IDC_TOGGLE_TURBO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,91,103,123,12 - DEFPUSHBUTTON "&OK",IDOK,215,136,46,14 - PUSHBUTTON "&Cancel",IDCANCEL,266,135,46,14 + EDITTEXT IDC_REWIND_BUFFER,91,102,49,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "",IDC_REWIND_BUFFER_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,294,114,11,13 + EDITTEXT IDC_REWIND_GRANULARITY,91,120,49,14,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "",IDC_REWIND_GRANULARITY_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,293,131,11,13 + CONTROL "Toggled Turbo Mode",IDC_TOGGLE_TURBO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,91,136,123,12 + CONTROL "Pause When Inactive",IDC_INACTIVE_PAUSE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,91,147,100,12 + CONTROL "Custom ROM Open Dialog",IDC_CUSTOMROMOPEN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,91,158,117,12 + CONTROL "Hi-Res AVI Recording",IDC_HIRESAVI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,91,169,117,12 + DEFPUSHBUTTON "&OK",IDOK,215,168,46,14 + PUSHBUTTON "&Cancel",IDCANCEL,266,168,46,14 + COMBOBOX IDC_DIRCOMBO,7,29,44,30,CBS_DROPDOWNLIST | WS_TABSTOP RTEXT "Directory",IDC_LABEL_FREEZE,53,28,32,14,SS_CENTERIMAGE RTEXT "Auto-Save S-RAM",IDC_LABEL_ASRAM,21,47,64,14,SS_CENTERIMAGE RTEXT "Skip at most",IDC_LABEL_SMAX,40,66,45,14,SS_CENTERIMAGE @@ -122,13 +130,13 @@ BEGIN LTEXT "seconds after last change (0 disables auto-save)",IDC_LABEL_ASRAM_TEXT,146,47,161,14,SS_CENTERIMAGE LTEXT "frames in auto-frame rate mode",IDC_LABEL_SMAX_TEXT,146,66,138,14,SS_CENTERIMAGE LTEXT "frames in Turbo mode",IDC_LABEL_STURBO_TEXT,146,85,92,14,SS_CENTERIMAGE - CONTROL "Pause When Inactive",IDC_INACTIVE_PAUSE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,91,114,100,12 LTEXT "Config file",IDC_STATIC,54,11,34,11 EDITTEXT IDC_CONFIG_NAME_BOX,91,9,49,14,ES_AUTOHSCROLL | ES_READONLY | ES_NUMBER LTEXT "all of Snes9x's settings are stored in this file",IDC_STATIC,147,11,160,11 - COMBOBOX IDC_DIRCOMBO,7,29,44,30,CBS_DROPDOWNLIST | WS_TABSTOP - CONTROL "Custom ROM Open Dialog",IDC_CUSTOMROMOPEN,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,91,125,117,12 - CONTROL "Hi-Res AVI Recording",IDC_HIRESAVI,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,91,136,117,12 + RTEXT "Rewind Buffer",IDC_LABEL_RBUFFER,31,102,51,14,SS_CENTERIMAGE + LTEXT "MB (set to 0 to disable rewind support)",IDC_LABEL_RBUFFER_TEXT,145,102,131,14,SS_CENTERIMAGE + RTEXT "Rewind Granularity",IDC_LABEL_RGRANU,18,120,64,14,SS_CENTERIMAGE + LTEXT "frames",IDC_LABEL_GRANU_TEXT,145,120,131,14,SS_CENTERIMAGE END IDD_OPEN_ROM DIALOGEX 0, 0, 430, 223 @@ -580,7 +588,7 @@ BEGIN BEGIN LEFTMARGIN, 7 RIGHTMARGIN, 312 - BOTTOMMARGIN, 147 + BOTTOMMARGIN, 177 END IDD_OPEN_ROM, DIALOG @@ -842,8 +850,8 @@ BEGIN MENUITEM "&Input Configuration...\tAlt+F7", 40022 MENUITEM "&Customize Hotkeys...\tAlt+F9", ID_OPTIONS_KEYCUSTOM MENUITEM SEPARATOR - MENUITEM "Enable Background Input", ID_EMULATION_BACKGROUNDINPUT - MENUITEM SEPARATOR + MENUITEM "Enable Background Input", ID_EMULATION_BACKGROUNDINPUT + MENUITEM SEPARATOR MENUITEM "Use SNES Joypad(s)", IDM_SNES_JOYPAD MENUITEM "Use SNES Mouse", IDM_MOUSE_TOGGLE MENUITEM "Use Super Scope", IDM_SCOPE_TOGGLE diff --git a/win32/snes9xw.vcproj b/win32/snes9xw.vcproj index 27ea8e7c..3437de14 100644 --- a/win32/snes9xw.vcproj +++ b/win32/snes9xw.vcproj @@ -3022,6 +3022,14 @@ RelativePath=".\render.h" > + + + + diff --git a/win32/wconfig.cpp b/win32/wconfig.cpp index bafa2130..896922dd 100644 --- a/win32/wconfig.cpp +++ b/win32/wconfig.cpp @@ -798,6 +798,7 @@ void WinPostLoad(ConfigFile& conf) for(i=0;i<8;i++) Joypad[i+8].Enabled = Joypad[i].Enabled; if(GUI.MaxRecentGames < 1) GUI.MaxRecentGames = 1; if(GUI.MaxRecentGames > MAX_RECENT_GAMES_LIST_SIZE) GUI.MaxRecentGames = MAX_RECENT_GAMES_LIST_SIZE; + if(GUI.rewindGranularity==0) GUI.rewindGranularity = 1; bool gap = false; for(i=0;i #if (((defined(_MSC_VER) && _MSC_VER >= 1300)) || defined(__MINGW32__)) @@ -563,6 +564,7 @@ struct SCustomKeys CustomKeys = { {'R',CUSTKEY_CTRL_MASK|CUSTKEY_SHIFT_MASK}, // Reset Game {0,0}, // Toggle Cheats {0,0}, + {'R',0}, // Rewind }; @@ -610,7 +612,7 @@ struct OpenMovieParams - +StateManager stateMan; std::vector dm; /*****************************************************************************/ @@ -1231,6 +1233,13 @@ int HandleKeyMessage(WPARAM wParam, LPARAM lParam) { PostMessage(GUI.hWnd,WM_CLOSE,(WPARAM)NULL,(LPARAM)(NULL)); } + if(wParam == CustomKeys.Rewind.key + && modifiers == CustomKeys.Rewind.modifiers) + { + if(!GUI.rewinding) + S9xMessage (S9X_INFO, 0, GUI.rewindBufferSize?WINPROC_REWINDING_TEXT:WINPROC_REWINDING_DISABLED); + GUI.rewinding = true; + } //if(wParam == CustomKeys.BGLHack.key //&& modifiers == CustomKeys.BGLHack.modifiers) //{ @@ -1479,6 +1488,12 @@ LRESULT CALLBACK WinProc( { GUI.superscope_pause = 0; } + if(wParam == CustomKeys.Rewind.key + && modifiers == CustomKeys.Rewind.modifiers) + { + GUI.rewinding = false; + } + } break; @@ -3242,6 +3257,7 @@ int WINAPI WinMain( InitRenderFilters(); GUI.ControlForced = 0xff; + GUI.rewinding = false; S9xSetRecentGames (); @@ -3426,6 +3442,19 @@ int WINAPI WinMain( S9xClearSamples(); } + if(GUI.rewindBufferSize +#ifdef NETPLAY_SUPPORT + &&!Settings.NetPlay +#endif + ) { + if(GUI.rewinding) { + GUI.rewinding = stateMan.pop(); + } else { + if(IPPU.TotalEmulatedFrames % GUI.rewindGranularity == 0) + stateMan.push(); + } + } + S9xMainLoop(); GUI.FrameCount++; } @@ -3887,6 +3916,8 @@ static bool LoadROM(const TCHAR *filename) { else S9xNPServerQueueSendingLoadROMRequest (Memory.ROMName); #endif + if(GUI.rewindBufferSize) + stateMan.init(GUI.rewindBufferSize * 1024 * 1024); } if(GUI.ControllerOption == SNES_SUPERSCOPE) @@ -4909,6 +4940,10 @@ INT_PTR CALLBACK DlgEmulatorProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lPar SendDlgItemMessage(hDlg, IDC_SPIN_MAX_SKIP,UDM_SETPOS,0, Settings.AutoMaxSkipFrames); SendDlgItemMessage(hDlg, IDC_SPIN_TURBO_SKIP, UDM_SETRANGE, 0, MAKELPARAM((short)600, (short)0)); SendDlgItemMessage(hDlg, IDC_SPIN_TURBO_SKIP,UDM_SETPOS,0, Settings.TurboSkipFrames); + SendDlgItemMessage(hDlg, IDC_REWIND_BUFFER_SPIN, UDM_SETRANGE, 0, MAKELPARAM((short)4000, (short)0)); + SendDlgItemMessage(hDlg, IDC_REWIND_BUFFER_SPIN,UDM_SETPOS,0, GUI.rewindBufferSize); + SendDlgItemMessage(hDlg, IDC_REWIND_GRANULARITY_SPIN, UDM_SETRANGE, 0, MAKELPARAM((short)300, (short)1)); + SendDlgItemMessage(hDlg, IDC_REWIND_GRANULARITY_SPIN,UDM_SETPOS,0, GUI.rewindGranularity); CheckDlgButton(hDlg,IDC_TOGGLE_TURBO,GUI.TurboModeToggle ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hDlg,IDC_INACTIVE_PAUSE,GUI.InactivePause ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hDlg,IDC_CUSTOMROMOPEN,GUI.CustomRomOpen ? BST_CHECKED : BST_UNCHECKED); @@ -5001,6 +5036,13 @@ INT_PTR CALLBACK DlgEmulatorProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lPar Settings.TurboSkipFrames=SendDlgItemMessage(hDlg, IDC_SPIN_TURBO_SKIP, UDM_GETPOS, 0,0); Settings.AutoMaxSkipFrames=SendDlgItemMessage(hDlg, IDC_SPIN_MAX_SKIP, UDM_GETPOS, 0,0); Settings.AutoSaveDelay=SendDlgItemMessage(hDlg, IDC_SRAM_SPIN, UDM_GETPOS, 0,0); + GUI.rewindGranularity = SendDlgItemMessage(hDlg, IDC_REWIND_GRANULARITY_SPIN, UDM_GETPOS, 0,0); + if(GUI.rewindGranularity==0) GUI.rewindGranularity = 1; + unsigned int newRewindBufSize = SendDlgItemMessage(hDlg, IDC_REWIND_BUFFER_SPIN, UDM_GETPOS, 0,0); + if(GUI.rewindBufferSize != newRewindBufSize) { + GUI.rewindBufferSize = newRewindBufSize; + stateMan.init(GUI.rewindBufferSize * 1024 * 1024); + } WinSaveConfigFile(); } @@ -7749,7 +7791,7 @@ static void set_hotkeyinfo(HWND hDlg) SendDlgItemMessage(hDlg,IDC_HOTKEY6,WM_USER+44,CustomKeys.ClippingWindows.key,CustomKeys.ClippingWindows.modifiers); SendDlgItemMessage(hDlg,IDC_HOTKEY7,WM_USER+44,CustomKeys.Transparency.key,CustomKeys.Transparency.modifiers); SendDlgItemMessage(hDlg,IDC_HOTKEY8,WM_USER+44,0,0); - SendDlgItemMessage(hDlg,IDC_HOTKEY9,WM_USER+44,0,0); + SendDlgItemMessage(hDlg,IDC_HOTKEY9,WM_USER+44,CustomKeys.Rewind.key,CustomKeys.Rewind.modifiers); SendDlgItemMessage(hDlg,IDC_HOTKEY10,WM_USER+44,CustomKeys.SwitchControllers.key,CustomKeys.SwitchControllers.modifiers); SendDlgItemMessage(hDlg,IDC_HOTKEY11,WM_USER+44,CustomKeys.JoypadSwap.key,CustomKeys.JoypadSwap.modifiers); SendDlgItemMessage(hDlg,IDC_HOTKEY12,WM_USER+44,CustomKeys.ResetGame.key,CustomKeys.ResetGame.modifiers); @@ -7814,7 +7856,7 @@ static void set_hotkeyinfo(HWND hDlg) SetDlgItemText(hDlg,IDC_LABEL_HK6,HOTKEYS_LABEL_2_6); SetDlgItemText(hDlg,IDC_LABEL_HK7,HOTKEYS_LABEL_2_7); SetDlgItemText(hDlg,IDC_LABEL_HK8,INPUTCONFIG_LABEL_UNUSED); - SetDlgItemText(hDlg,IDC_LABEL_HK9,INPUTCONFIG_LABEL_UNUSED); + SetDlgItemText(hDlg,IDC_LABEL_HK9,HOTKEYS_LABEL_2_9); SetDlgItemText(hDlg,IDC_LABEL_HK10,HOTKEYS_LABEL_2_10); SetDlgItemText(hDlg,IDC_LABEL_HK11,HOTKEYS_LABEL_2_11); SetDlgItemText(hDlg,IDC_LABEL_HK12,HOTKEYS_LABEL_2_12); @@ -7979,7 +8021,7 @@ switch(msg) break; case IDC_HOTKEY9: if(index == 0) CustomKeys.ScopePause.key = wParam, CustomKeys.ScopePause.modifiers = modifiers; - //if(index == 1) CustomKeys.GLCube.key = wParam, CustomKeys.GLCube.modifiers = modifiers; + if(index == 1) CustomKeys.Rewind.key = wParam, CustomKeys.Rewind.modifiers = modifiers; if(index == 2) CustomKeys.TurboLeft.key = wParam, CustomKeys.TurboLeft.modifiers = modifiers; if(index == 3) CustomKeys.SelectSave[8].key = wParam, CustomKeys.SelectSave[8].modifiers = modifiers; break; diff --git a/win32/wsnes9x.h b/win32/wsnes9x.h index 4a49f3cd..fb7c81c6 100644 --- a/win32/wsnes9x.h +++ b/win32/wsnes9x.h @@ -339,7 +339,7 @@ struct sGUI { HACCEL Accelerators; bool NeedDepthConvert; bool DepthConverted; - bool BGR; + bool TurboModeToggle; bool InactivePause; bool CustomRomOpen; @@ -395,6 +395,11 @@ struct sGUI { long FrameCount; long LastFrameCount; unsigned long IdleCount; + + // rewinding + bool rewinding; + unsigned int rewindBufferSize; + unsigned int rewindGranularity; }; //TURBO masks @@ -467,6 +472,7 @@ struct SCustomKeys { SCustomKey ResetGame; SCustomKey ToggleCheats; SCustomKey QuitS9X; + SCustomKey Rewind; }; struct SJoypad {