Save States:
1. Added a header, including the Game ID (old states are obsolete unless the gameid is appended to the beginning) so states from different games can't be cross-loaded 2. Added loading/saving from/to file 3. Added loading of last saved state (F11) 4. Added "Undo State": Load the backed up last overwritten state (if you press save instead of load) (F12) 5. State code cleanup git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@3560 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
2ea850f5a0
commit
7ea2bc5da9
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue