diff --git a/Source/Core/Core/Src/Core.cpp b/Source/Core/Core/Src/Core.cpp index 71e071667b..d82d190209 100644 --- a/Source/Core/Core/Src/Core.cpp +++ b/Source/Core/Core/Src/Core.cpp @@ -863,6 +863,14 @@ void Callback_KeyPress(int key, bool shift, bool control) State_Load(slot_number); } } + + // 0x7a == VK_F11 + if (key == 0x7a) + State_LoadLastSaved(); + + // 0x7a == VK_F12 + if (key == 0x7b) + State_LoadAs(FULL_STATESAVES_DIR "lastState.sav"); } // Callback_WiimoteLog diff --git a/Source/Core/Core/Src/State.cpp b/Source/Core/Core/Src/State.cpp index 5bac051599..2c8f635b8c 100644 --- a/Source/Core/Core/Src/State.cpp +++ b/Source/Core/Core/Src/State.cpp @@ -52,12 +52,13 @@ static HEAP_ALLOC(wrkmem,LZO1X_1_MEM_COMPRESS); static int ev_Save; static int ev_Load; -static std::string cur_filename; +static std::string cur_filename, lastFilename; static bool const bCompressed = true; -enum { - version = 1 +enum +{ + version = 1, }; void DoState(PointerWrap &p) @@ -84,22 +85,31 @@ THREAD_RETURN CompressAndDumpState(void *pArgs) u8 *buffer = saveArg->buffer; size_t sz = saveArg->size; lzo_uint out_len = 0; + state_header header; + std::string filename = cur_filename; delete saveArg; - FILE *f = fopen(cur_filename.c_str(), "wb"); + // Moving to last overwritten save-state + if(File::Exists(cur_filename.c_str())) { + if(File::Exists(FULL_STATESAVES_DIR "lastState.sav")) + File::Delete(FULL_STATESAVES_DIR "lastState.sav"); + + if(!File::Rename(cur_filename.c_str(), FULL_STATESAVES_DIR "lastState.sav")) + Core::DisplayMessage("Failed to move previous state to state undo backup", 1000); + } + + FILE *f = fopen(filename.c_str(), "wb"); if(f == NULL) { Core::DisplayMessage("Could not save state", 2000); return 0; } - if (bCompressed) { - fwrite(&sz, sizeof(int), 1, f); - } else { - int zero = 0; - fwrite(&zero, sizeof(int), 1, f); - } + // Setting up the header + memcpy(header.gameID, Core::GetStartupParameter().GetUniqueID().c_str(), 6); + header.sz = bCompressed ? sz : 0; + fwrite(&header, sizeof(state_header), 1, f); if (bCompressed) { if (lzo_init() != LZO_E_OK) @@ -134,7 +144,7 @@ THREAD_RETURN CompressAndDumpState(void *pArgs) delete [] buffer; Core::DisplayMessage(StringFromFormat("Saved State to %s", - cur_filename.c_str()).c_str(), 2000); + filename.c_str()).c_str(), 2000); return 0; } @@ -176,22 +186,43 @@ void LoadStateCallback(u64 userdata, int cyclesLate) bool bCompressedState; + // If saving state, wait for it to finish + if(saveThread) + { + delete saveThread; + saveThread = NULL; + } + FILE *f = fopen(cur_filename.c_str(), "rb"); if (!f) { Core::DisplayMessage("State not found", 2000); return; } - jit.ClearCache(); - u8 *buffer = NULL; + state_header header; + size_t sz; - int sz; - fread(&sz, sizeof(int), 1, f); + fread(&header, sizeof(state_header), 1, f); + + if(memcmp(Core::GetStartupParameter().GetUniqueID().c_str(), header.gameID, 6)) + { + char gameID[7] = {0}; + memcpy(gameID, header.gameID, 6); + Core::DisplayMessage(StringFromFormat("State belongs to a different game (ID %s)", + gameID), 2000); + fclose(f); + + return; + } + + sz = header.sz; bCompressedState = (sz != 0); if (bCompressedState) { + Core::DisplayMessage("Decompressing State...", 500); + if (lzo_init() != LZO_E_OK) PanicAlert("Internal LZO Error - lzo_init() failed"); else { @@ -225,6 +256,8 @@ void LoadStateCallback(u64 userdata, int cyclesLate) fclose(f); + jit.ClearCache(); + u8 *ptr = buffer; PointerWrap p(&ptr, PointerWrap::MODE_READ); DoState(p); @@ -253,14 +286,33 @@ std::string MakeStateFilename(int state_number) return StringFromFormat(FULL_STATESAVES_DIR "%s.s%02i", Core::GetStartupParameter().GetUniqueID().c_str(), state_number); } +void State_SaveAs(const std::string &filename) +{ + cur_filename = filename; + lastFilename = filename; + CoreTiming::ScheduleEvent_Threadsafe(0, ev_Save); +} + void State_Save(int slot) { - cur_filename = MakeStateFilename(slot); - CoreTiming::ScheduleEvent_Threadsafe(0, ev_Save); + State_SaveAs(MakeStateFilename(slot)); +} + +void State_LoadAs(const std::string &filename) +{ + cur_filename = filename; + CoreTiming::ScheduleEvent_Threadsafe(0, ev_Load); } void State_Load(int slot) { - cur_filename = MakeStateFilename(slot); - CoreTiming::ScheduleEvent_Threadsafe(0, ev_Load); + State_LoadAs(MakeStateFilename(slot)); +} + +void State_LoadLastSaved() +{ + if(lastFilename.empty()) + Core::DisplayMessage("There is no last saved state", 2000); + else + State_LoadAs(lastFilename); } diff --git a/Source/Core/Core/Src/State.h b/Source/Core/Core/Src/State.h index 19313b2c85..b22df727d8 100644 --- a/Source/Core/Core/Src/State.h +++ b/Source/Core/Core/Src/State.h @@ -28,10 +28,22 @@ void State_Shutdown(); void State_Save(int slot); void State_Load(int slot); +void State_SaveAs(const std::string &filename); +void State_LoadAs(const std::string &filename); + +void State_LoadLastSaved(); + typedef struct { u8 *buffer; size_t size; } saveStruct; + +typedef struct +{ + u8 gameID[6]; + size_t sz; +} state_header; + #endif diff --git a/Source/Core/DolphinWX/Src/Frame.cpp b/Source/Core/DolphinWX/Src/Frame.cpp index 050d98c50f..438622b4d8 100644 --- a/Source/Core/DolphinWX/Src/Frame.cpp +++ b/Source/Core/DolphinWX/Src/Frame.cpp @@ -285,6 +285,11 @@ EVT_MENU(IDM_LISTPAL, CFrame::GameListChanged) EVT_MENU(IDM_LISTUSA, CFrame::GameListChanged) EVT_MENU(IDM_PURGECACHE, CFrame::GameListChanged) +EVT_MENU(IDM_LOADLASTSTATE, CFrame::OnLoadLastState) +EVT_MENU(IDM_UNDOSTATE, CFrame::OnUndoState) +EVT_MENU(IDM_LOADSTATEFILE, CFrame::OnLoadStateFromFile) +EVT_MENU(IDM_SAVESTATEFILE, CFrame::OnSaveStateToFile) + EVT_MENU_RANGE(IDM_LOADSLOT1, IDM_LOADSLOT10, CFrame::OnLoadState) EVT_MENU_RANGE(IDM_SAVESLOT1, IDM_SAVESLOT10, CFrame::OnSaveState) EVT_MENU_RANGE(IDM_DRIVE1, IDM_DRIVE24, CFrame::OnBootDrive) diff --git a/Source/Core/DolphinWX/Src/Frame.h b/Source/Core/DolphinWX/Src/Frame.h index 1bacd0b7c4..08d0c96198 100644 --- a/Source/Core/DolphinWX/Src/Frame.h +++ b/Source/Core/DolphinWX/Src/Frame.h @@ -185,6 +185,10 @@ class CFrame : public wxFrame void OnClose(wxCloseEvent &event); void OnLoadState(wxCommandEvent& event); void OnSaveState(wxCommandEvent& event); + void OnLoadStateFromFile(wxCommandEvent& event); + void OnSaveStateToFile(wxCommandEvent& event); + void OnLoadLastState(wxCommandEvent& event); + void OnUndoState(wxCommandEvent& event); void OnConfigMain(wxCommandEvent& event); // Options void OnPluginGFX(wxCommandEvent& event); diff --git a/Source/Core/DolphinWX/Src/FrameTools.cpp b/Source/Core/DolphinWX/Src/FrameTools.cpp index d7cda0e5dd..da76aed608 100644 --- a/Source/Core/DolphinWX/Src/FrameTools.cpp +++ b/Source/Core/DolphinWX/Src/FrameTools.cpp @@ -139,11 +139,20 @@ void CFrame::CreateMenu() emulationMenu->AppendSeparator(); wxMenu *saveMenu = new wxMenu; wxMenu *loadMenu = new wxMenu; - m_pSubMenuLoad = emulationMenu->AppendSubMenu(saveMenu, _T("&Load State")); - m_pSubMenuSave = emulationMenu->AppendSubMenu(loadMenu, _T("Sa&ve State")); + m_pSubMenuLoad = emulationMenu->AppendSubMenu(loadMenu, _T("&Load State")); + m_pSubMenuSave = emulationMenu->AppendSubMenu(saveMenu, _T("Sa&ve State")); + + saveMenu->Append(IDM_SAVESTATEFILE, _T("Save State...")); + saveMenu->AppendSeparator(); + + loadMenu->Append(IDM_LOADSTATEFILE, _T("Load State...")); + loadMenu->Append(IDM_LOADLASTSTATE, _T("Last Saved State\tF11")); + loadMenu->Append(IDM_UNDOSTATE, _T("Last Overwritten State\tF12")); + loadMenu->AppendSeparator(); + for (int i = 1; i < 10; i++) { - saveMenu->Append(IDM_LOADSLOT1 + i - 1, wxString::Format(_T("Slot %i\tF%i"), i, i)); - loadMenu->Append(IDM_SAVESLOT1 + i - 1, wxString::Format(_T("Slot %i\tShift+F%i"), i, i)); + loadMenu->Append(IDM_LOADSLOT1 + i - 1, wxString::Format(_T("Slot %i\tF%i"), i, i)); + saveMenu->Append(IDM_SAVESLOT1 + i - 1, wxString::Format(_T("Slot %i\tShift+F%i"), i, i)); } menuBar->Append(emulationMenu, _T("&Emulation")); @@ -717,6 +726,52 @@ void CFrame::OnToggleSkipIdle(wxCommandEvent& WXUNUSED (event)) SConfig::GetInstance().SaveSettings(); } +void CFrame::OnLoadStateFromFile(wxCommandEvent& WXUNUSED (event)) +{ + wxString path = wxFileSelector( + _T("Select the state to load"), + wxEmptyString, wxEmptyString, wxEmptyString, + wxString::Format + ( + _T("All Save States (sav, s##)|*.sav;*.s??|All files (%s)|%s"), + wxFileSelectorDefaultWildcardStr, + wxFileSelectorDefaultWildcardStr + ), + wxFD_OPEN | wxFD_PREVIEW | wxFD_FILE_MUST_EXIST, + this); + + if(path) + State_LoadAs(path.ToAscii()); +} + +void CFrame::OnSaveStateToFile(wxCommandEvent& WXUNUSED (event)) +{ + wxString path = wxFileSelector( + _T("Select the state to save"), + wxEmptyString, wxEmptyString, wxEmptyString, + wxString::Format + ( + _T("All Save States (sav, s##)|*.sav;*.s??|All files (%s)|%s"), + wxFileSelectorDefaultWildcardStr, + wxFileSelectorDefaultWildcardStr + ), + wxFD_SAVE, + this); + + if(path) + State_SaveAs(path.ToAscii()); +} + +void CFrame::OnLoadLastState(wxCommandEvent& WXUNUSED (event)) +{ + State_LoadLastSaved(); +} + +void CFrame::OnUndoState(wxCommandEvent& WXUNUSED (event)) +{ + State_LoadAs(FULL_STATESAVES_DIR "lastState.sav"); +} + void CFrame::OnLoadState(wxCommandEvent& event) { int id = event.GetId(); @@ -724,13 +779,6 @@ void CFrame::OnLoadState(wxCommandEvent& event) State_Load(slot); } -void CFrame::OnResize(wxSizeEvent& event) -{ - FitInside(); - DoMoveIcons(); // In FrameWiimote.cpp - event.Skip(); -} - void CFrame::OnSaveState(wxCommandEvent& event) { int id = event.GetId(); @@ -738,6 +786,12 @@ void CFrame::OnSaveState(wxCommandEvent& event) State_Save(slot); } +void CFrame::OnResize(wxSizeEvent& event) +{ + FitInside(); + DoMoveIcons(); // In FrameWiimote.cpp + event.Skip(); +} // Enable and disable the toolbar void CFrame::OnToggleToolbar(wxCommandEvent& event) diff --git a/Source/Core/DolphinWX/Src/Globals.h b/Source/Core/DolphinWX/Src/Globals.h index ab84a08bef..7dae1f508d 100644 --- a/Source/Core/DolphinWX/Src/Globals.h +++ b/Source/Core/DolphinWX/Src/Globals.h @@ -31,6 +31,10 @@ enum { IDM_LOADSTATE = 200, // File menu IDM_SAVESTATE, + IDM_LOADLASTSTATE, + IDM_UNDOSTATE, + IDM_LOADSTATEFILE, + IDM_SAVESTATEFILE, IDM_SAVESLOT1, IDM_SAVESLOT2, IDM_SAVESLOT3,