mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-01-10 16:19:28 +01:00
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);
|
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
|
// Callback_WiimoteLog
|
||||||
|
@ -52,12 +52,13 @@ static HEAP_ALLOC(wrkmem,LZO1X_1_MEM_COMPRESS);
|
|||||||
static int ev_Save;
|
static int ev_Save;
|
||||||
static int ev_Load;
|
static int ev_Load;
|
||||||
|
|
||||||
static std::string cur_filename;
|
static std::string cur_filename, lastFilename;
|
||||||
|
|
||||||
static bool const bCompressed = true;
|
static bool const bCompressed = true;
|
||||||
|
|
||||||
enum {
|
enum
|
||||||
version = 1
|
{
|
||||||
|
version = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
void DoState(PointerWrap &p)
|
void DoState(PointerWrap &p)
|
||||||
@ -84,22 +85,31 @@ THREAD_RETURN CompressAndDumpState(void *pArgs)
|
|||||||
u8 *buffer = saveArg->buffer;
|
u8 *buffer = saveArg->buffer;
|
||||||
size_t sz = saveArg->size;
|
size_t sz = saveArg->size;
|
||||||
lzo_uint out_len = 0;
|
lzo_uint out_len = 0;
|
||||||
|
state_header header;
|
||||||
|
std::string filename = cur_filename;
|
||||||
|
|
||||||
delete saveArg;
|
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) {
|
if(f == NULL) {
|
||||||
Core::DisplayMessage("Could not save state", 2000);
|
Core::DisplayMessage("Could not save state", 2000);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bCompressed) {
|
// Setting up the header
|
||||||
fwrite(&sz, sizeof(int), 1, f);
|
memcpy(header.gameID, Core::GetStartupParameter().GetUniqueID().c_str(), 6);
|
||||||
} else {
|
header.sz = bCompressed ? sz : 0;
|
||||||
int zero = 0;
|
|
||||||
fwrite(&zero, sizeof(int), 1, f);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fwrite(&header, sizeof(state_header), 1, f);
|
||||||
|
|
||||||
if (bCompressed) {
|
if (bCompressed) {
|
||||||
if (lzo_init() != LZO_E_OK)
|
if (lzo_init() != LZO_E_OK)
|
||||||
@ -134,7 +144,7 @@ THREAD_RETURN CompressAndDumpState(void *pArgs)
|
|||||||
delete [] buffer;
|
delete [] buffer;
|
||||||
|
|
||||||
Core::DisplayMessage(StringFromFormat("Saved State to %s",
|
Core::DisplayMessage(StringFromFormat("Saved State to %s",
|
||||||
cur_filename.c_str()).c_str(), 2000);
|
filename.c_str()).c_str(), 2000);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -176,22 +186,43 @@ void LoadStateCallback(u64 userdata, int cyclesLate)
|
|||||||
|
|
||||||
bool bCompressedState;
|
bool bCompressedState;
|
||||||
|
|
||||||
|
// If saving state, wait for it to finish
|
||||||
|
if(saveThread)
|
||||||
|
{
|
||||||
|
delete saveThread;
|
||||||
|
saveThread = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
FILE *f = fopen(cur_filename.c_str(), "rb");
|
FILE *f = fopen(cur_filename.c_str(), "rb");
|
||||||
if (!f) {
|
if (!f) {
|
||||||
Core::DisplayMessage("State not found", 2000);
|
Core::DisplayMessage("State not found", 2000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
jit.ClearCache();
|
|
||||||
|
|
||||||
u8 *buffer = NULL;
|
u8 *buffer = NULL;
|
||||||
|
state_header header;
|
||||||
|
size_t sz;
|
||||||
|
|
||||||
int sz;
|
fread(&header, sizeof(state_header), 1, f);
|
||||||
fread(&sz, sizeof(int), 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);
|
bCompressedState = (sz != 0);
|
||||||
|
|
||||||
if (bCompressedState) {
|
if (bCompressedState) {
|
||||||
|
Core::DisplayMessage("Decompressing State...", 500);
|
||||||
|
|
||||||
if (lzo_init() != LZO_E_OK)
|
if (lzo_init() != LZO_E_OK)
|
||||||
PanicAlert("Internal LZO Error - lzo_init() failed");
|
PanicAlert("Internal LZO Error - lzo_init() failed");
|
||||||
else {
|
else {
|
||||||
@ -225,6 +256,8 @@ void LoadStateCallback(u64 userdata, int cyclesLate)
|
|||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
|
jit.ClearCache();
|
||||||
|
|
||||||
u8 *ptr = buffer;
|
u8 *ptr = buffer;
|
||||||
PointerWrap p(&ptr, PointerWrap::MODE_READ);
|
PointerWrap p(&ptr, PointerWrap::MODE_READ);
|
||||||
DoState(p);
|
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);
|
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)
|
void State_Save(int slot)
|
||||||
{
|
{
|
||||||
cur_filename = MakeStateFilename(slot);
|
State_SaveAs(MakeStateFilename(slot));
|
||||||
CoreTiming::ScheduleEvent_Threadsafe(0, ev_Save);
|
}
|
||||||
|
|
||||||
|
void State_LoadAs(const std::string &filename)
|
||||||
|
{
|
||||||
|
cur_filename = filename;
|
||||||
|
CoreTiming::ScheduleEvent_Threadsafe(0, ev_Load);
|
||||||
}
|
}
|
||||||
|
|
||||||
void State_Load(int slot)
|
void State_Load(int slot)
|
||||||
{
|
{
|
||||||
cur_filename = MakeStateFilename(slot);
|
State_LoadAs(MakeStateFilename(slot));
|
||||||
CoreTiming::ScheduleEvent_Threadsafe(0, ev_Load);
|
}
|
||||||
|
|
||||||
|
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_Save(int slot);
|
||||||
void State_Load(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
|
typedef struct
|
||||||
{
|
{
|
||||||
u8 *buffer;
|
u8 *buffer;
|
||||||
size_t size;
|
size_t size;
|
||||||
} saveStruct;
|
} saveStruct;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
u8 gameID[6];
|
||||||
|
size_t sz;
|
||||||
|
} state_header;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -285,6 +285,11 @@ EVT_MENU(IDM_LISTPAL, CFrame::GameListChanged)
|
|||||||
EVT_MENU(IDM_LISTUSA, CFrame::GameListChanged)
|
EVT_MENU(IDM_LISTUSA, CFrame::GameListChanged)
|
||||||
EVT_MENU(IDM_PURGECACHE, 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_LOADSLOT1, IDM_LOADSLOT10, CFrame::OnLoadState)
|
||||||
EVT_MENU_RANGE(IDM_SAVESLOT1, IDM_SAVESLOT10, CFrame::OnSaveState)
|
EVT_MENU_RANGE(IDM_SAVESLOT1, IDM_SAVESLOT10, CFrame::OnSaveState)
|
||||||
EVT_MENU_RANGE(IDM_DRIVE1, IDM_DRIVE24, CFrame::OnBootDrive)
|
EVT_MENU_RANGE(IDM_DRIVE1, IDM_DRIVE24, CFrame::OnBootDrive)
|
||||||
|
@ -185,6 +185,10 @@ class CFrame : public wxFrame
|
|||||||
void OnClose(wxCloseEvent &event);
|
void OnClose(wxCloseEvent &event);
|
||||||
void OnLoadState(wxCommandEvent& event);
|
void OnLoadState(wxCommandEvent& event);
|
||||||
void OnSaveState(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 OnConfigMain(wxCommandEvent& event); // Options
|
||||||
void OnPluginGFX(wxCommandEvent& event);
|
void OnPluginGFX(wxCommandEvent& event);
|
||||||
|
@ -139,11 +139,20 @@ void CFrame::CreateMenu()
|
|||||||
emulationMenu->AppendSeparator();
|
emulationMenu->AppendSeparator();
|
||||||
wxMenu *saveMenu = new wxMenu;
|
wxMenu *saveMenu = new wxMenu;
|
||||||
wxMenu *loadMenu = new wxMenu;
|
wxMenu *loadMenu = new wxMenu;
|
||||||
m_pSubMenuLoad = emulationMenu->AppendSubMenu(saveMenu, _T("&Load State"));
|
m_pSubMenuLoad = emulationMenu->AppendSubMenu(loadMenu, _T("&Load State"));
|
||||||
m_pSubMenuSave = emulationMenu->AppendSubMenu(loadMenu, _T("Sa&ve 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++) {
|
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_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));
|
saveMenu->Append(IDM_SAVESLOT1 + i - 1, wxString::Format(_T("Slot %i\tShift+F%i"), i, i));
|
||||||
}
|
}
|
||||||
menuBar->Append(emulationMenu, _T("&Emulation"));
|
menuBar->Append(emulationMenu, _T("&Emulation"));
|
||||||
|
|
||||||
@ -717,6 +726,52 @@ void CFrame::OnToggleSkipIdle(wxCommandEvent& WXUNUSED (event))
|
|||||||
SConfig::GetInstance().SaveSettings();
|
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)
|
void CFrame::OnLoadState(wxCommandEvent& event)
|
||||||
{
|
{
|
||||||
int id = event.GetId();
|
int id = event.GetId();
|
||||||
@ -724,13 +779,6 @@ void CFrame::OnLoadState(wxCommandEvent& event)
|
|||||||
State_Load(slot);
|
State_Load(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CFrame::OnResize(wxSizeEvent& event)
|
|
||||||
{
|
|
||||||
FitInside();
|
|
||||||
DoMoveIcons(); // In FrameWiimote.cpp
|
|
||||||
event.Skip();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CFrame::OnSaveState(wxCommandEvent& event)
|
void CFrame::OnSaveState(wxCommandEvent& event)
|
||||||
{
|
{
|
||||||
int id = event.GetId();
|
int id = event.GetId();
|
||||||
@ -738,6 +786,12 @@ void CFrame::OnSaveState(wxCommandEvent& event)
|
|||||||
State_Save(slot);
|
State_Save(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CFrame::OnResize(wxSizeEvent& event)
|
||||||
|
{
|
||||||
|
FitInside();
|
||||||
|
DoMoveIcons(); // In FrameWiimote.cpp
|
||||||
|
event.Skip();
|
||||||
|
}
|
||||||
|
|
||||||
// Enable and disable the toolbar
|
// Enable and disable the toolbar
|
||||||
void CFrame::OnToggleToolbar(wxCommandEvent& event)
|
void CFrame::OnToggleToolbar(wxCommandEvent& event)
|
||||||
|
@ -31,6 +31,10 @@ enum
|
|||||||
{
|
{
|
||||||
IDM_LOADSTATE = 200, // File menu
|
IDM_LOADSTATE = 200, // File menu
|
||||||
IDM_SAVESTATE,
|
IDM_SAVESTATE,
|
||||||
|
IDM_LOADLASTSTATE,
|
||||||
|
IDM_UNDOSTATE,
|
||||||
|
IDM_LOADSTATEFILE,
|
||||||
|
IDM_SAVESTATEFILE,
|
||||||
IDM_SAVESLOT1,
|
IDM_SAVESLOT1,
|
||||||
IDM_SAVESLOT2,
|
IDM_SAVESLOT2,
|
||||||
IDM_SAVESLOT3,
|
IDM_SAVESLOT3,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user