2009-07-28 21:32:10 +00:00
|
|
|
// Copyright (C) 2003 Dolphin Project.
|
2008-12-08 05:30:24 +00:00
|
|
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
// the Free Software Foundation, version 2.0.
|
|
|
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
|
|
|
|
// Official SVN repository and contact information can be found at
|
|
|
|
// http://code.google.com/p/dolphin-emu/
|
|
|
|
|
|
|
|
#include "Common.h"
|
|
|
|
|
|
|
|
#include "State.h"
|
|
|
|
#include "Core.h"
|
2010-02-06 05:22:53 +00:00
|
|
|
#include "ConfigManager.h"
|
2008-12-08 05:30:24 +00:00
|
|
|
#include "StringUtil.h"
|
2009-07-12 21:58:32 +00:00
|
|
|
#include "Thread.h"
|
2008-12-08 05:30:24 +00:00
|
|
|
#include "CoreTiming.h"
|
2010-08-30 07:05:47 +00:00
|
|
|
#include "OnFrame.h"
|
2010-10-12 19:42:29 +00:00
|
|
|
#include "HW/Wiimote.h"
|
2008-12-08 05:30:24 +00:00
|
|
|
#include "HW/HW.h"
|
|
|
|
#include "PowerPC/PowerPC.h"
|
2010-01-19 19:28:27 +00:00
|
|
|
#include "PowerPC/JitCommon/JitBase.h"
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2009-01-15 06:48:15 +00:00
|
|
|
#include "PluginManager.h"
|
2008-12-08 05:30:24 +00:00
|
|
|
|
|
|
|
#include <string>
|
|
|
|
|
2010-03-09 22:17:33 +00:00
|
|
|
#include <lzo/lzo1x.h>
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2009-11-07 20:01:39 +00:00
|
|
|
// TODO: Move to namespace
|
2009-09-02 21:00:45 +00:00
|
|
|
|
2009-07-03 12:22:32 +00:00
|
|
|
// TODO: Investigate a memory leak on save/load state
|
2009-09-02 21:00:45 +00:00
|
|
|
|
2008-12-08 05:30:24 +00:00
|
|
|
#if defined(__LZO_STRICT_16BIT)
|
|
|
|
#define IN_LEN (8*1024u)
|
|
|
|
#elif defined(LZO_ARCH_I086) && !defined(LZO_HAVE_MM_HUGE_ARRAY)
|
|
|
|
#define IN_LEN (60*1024u)
|
|
|
|
#else
|
|
|
|
#define IN_LEN (128*1024ul)
|
|
|
|
#endif
|
|
|
|
#define OUT_LEN (IN_LEN + IN_LEN / 16 + 64 + 3)
|
|
|
|
|
|
|
|
static unsigned char __LZO_MMODEL out [ OUT_LEN ];
|
|
|
|
|
|
|
|
#define HEAP_ALLOC(var,size) \
|
|
|
|
lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ]
|
|
|
|
|
|
|
|
static HEAP_ALLOC(wrkmem,LZO1X_1_MEM_COMPRESS);
|
|
|
|
|
2009-09-15 18:54:10 +00:00
|
|
|
static bool state_op_in_progress = false;
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2009-06-28 19:47:02 +00:00
|
|
|
static int ev_Save, ev_BufferSave;
|
|
|
|
static int ev_Load, ev_BufferLoad;
|
2010-04-17 21:02:03 +00:00
|
|
|
static int ev_Verify, ev_BufferVerify;
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2009-06-28 01:11:35 +00:00
|
|
|
static std::string cur_filename, lastFilename;
|
2009-06-28 21:11:51 +00:00
|
|
|
static u8 **cur_buffer = NULL;
|
|
|
|
|
|
|
|
// Temporary undo state buffers
|
|
|
|
static u8 *undoLoad = NULL;
|
2008-12-08 05:30:24 +00:00
|
|
|
|
|
|
|
static bool const bCompressed = true;
|
|
|
|
|
2009-06-28 21:11:51 +00:00
|
|
|
static Common::Thread *saveThread = NULL;
|
|
|
|
|
2009-10-20 23:09:44 +00:00
|
|
|
|
|
|
|
// Don't forget to increase this after doing changes on the savestate system
|
2010-07-30 09:24:23 +00:00
|
|
|
#define STATE_VERSION 3
|
2009-10-20 23:09:44 +00:00
|
|
|
|
2008-12-08 05:30:24 +00:00
|
|
|
|
|
|
|
void DoState(PointerWrap &p)
|
|
|
|
{
|
2009-10-20 23:09:44 +00:00
|
|
|
u32 cookie = 0xBAADBABE + STATE_VERSION;
|
2008-12-08 05:30:24 +00:00
|
|
|
p.Do(cookie);
|
2009-10-20 23:09:44 +00:00
|
|
|
if (cookie != 0xBAADBABE + STATE_VERSION)
|
2009-09-15 18:54:10 +00:00
|
|
|
{
|
2009-11-01 01:24:30 +00:00
|
|
|
//PanicAlert("Savestate version mismatch !\nSorry, you can't load states from other revisions.");
|
|
|
|
p.SetMode(PointerWrap::MODE_MEASURE);
|
2008-12-08 05:30:24 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Begin with video plugin, so that it gets a chance to clear it's caches and writeback modified things to RAM
|
2009-01-15 06:48:15 +00:00
|
|
|
CPluginManager &pm = CPluginManager::GetInstance();
|
2010-04-08 16:59:35 +00:00
|
|
|
pm.GetVideo()->DoState(p.GetPPtr(), p.GetMode());
|
|
|
|
pm.GetDSP()->DoState(p.GetPPtr(), p.GetMode());
|
|
|
|
if (Core::g_CoreStartupParameter.bWii)
|
2010-10-12 19:42:29 +00:00
|
|
|
Wiimote::DoState(p.GetPPtr(), p.GetMode());
|
2008-12-08 05:30:24 +00:00
|
|
|
PowerPC::DoState(p);
|
|
|
|
HW::DoState(p);
|
|
|
|
CoreTiming::DoState(p);
|
|
|
|
}
|
|
|
|
|
2009-06-28 21:11:51 +00:00
|
|
|
void LoadBufferStateCallback(u64 userdata, int cyclesLate)
|
|
|
|
{
|
2009-09-15 18:54:10 +00:00
|
|
|
if (!cur_buffer || !*cur_buffer) {
|
2009-06-28 21:11:51 +00:00
|
|
|
Core::DisplayMessage("State does not exist", 1000);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 *ptr = *cur_buffer;
|
|
|
|
PointerWrap p(&ptr, PointerWrap::MODE_READ);
|
|
|
|
DoState(p);
|
|
|
|
|
|
|
|
Core::DisplayMessage("Loaded state", 2000);
|
2009-09-15 18:54:10 +00:00
|
|
|
state_op_in_progress = false;
|
2009-06-28 21:11:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SaveBufferStateCallback(u64 userdata, int cyclesLate)
|
|
|
|
{
|
2009-09-15 18:54:10 +00:00
|
|
|
if (!cur_buffer) {
|
2009-06-28 21:11:51 +00:00
|
|
|
Core::DisplayMessage("Error saving state", 1000);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 *ptr = NULL;
|
|
|
|
|
|
|
|
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
|
|
|
|
|
2010-04-17 21:02:03 +00:00
|
|
|
if (!*cur_buffer)
|
|
|
|
{
|
|
|
|
// if we got passed an empty buffer,
|
|
|
|
// allocate it with new[]
|
|
|
|
// (and the caller is responsible for delete[]ing it later)
|
|
|
|
DoState(p);
|
|
|
|
size_t sz = (size_t)ptr;
|
|
|
|
*cur_buffer = new u8[sz];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// otherwise the caller is telling us that they have already allocated it with enough space
|
|
|
|
}
|
2009-07-22 22:50:52 +00:00
|
|
|
|
2009-06-28 21:11:51 +00:00
|
|
|
ptr = *cur_buffer;
|
|
|
|
p.SetMode(PointerWrap::MODE_WRITE);
|
|
|
|
DoState(p);
|
|
|
|
|
2009-09-15 18:54:10 +00:00
|
|
|
state_op_in_progress = false;
|
2009-06-28 21:11:51 +00:00
|
|
|
}
|
|
|
|
|
2010-04-17 21:02:03 +00:00
|
|
|
void VerifyBufferStateCallback(u64 userdata, int cyclesLate)
|
|
|
|
{
|
|
|
|
if (!cur_buffer || !*cur_buffer) {
|
|
|
|
Core::DisplayMessage("State does not exist", 1000);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 *ptr = *cur_buffer;
|
|
|
|
PointerWrap p(&ptr, PointerWrap::MODE_VERIFY);
|
|
|
|
DoState(p);
|
|
|
|
|
|
|
|
Core::DisplayMessage("Verified state", 2000);
|
|
|
|
state_op_in_progress = false;
|
|
|
|
}
|
|
|
|
|
2009-06-26 22:36:44 +00:00
|
|
|
THREAD_RETURN CompressAndDumpState(void *pArgs)
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
2009-06-26 22:36:44 +00:00
|
|
|
saveStruct *saveArg = (saveStruct *)pArgs;
|
|
|
|
u8 *buffer = saveArg->buffer;
|
|
|
|
size_t sz = saveArg->size;
|
2008-12-08 05:30:24 +00:00
|
|
|
lzo_uint out_len = 0;
|
2009-06-28 01:11:35 +00:00
|
|
|
state_header header;
|
|
|
|
std::string filename = cur_filename;
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2009-06-26 22:36:44 +00:00
|
|
|
delete saveArg;
|
|
|
|
|
2009-06-28 01:11:35 +00:00
|
|
|
// Moving to last overwritten save-state
|
2010-04-08 16:59:35 +00:00
|
|
|
if (File::Exists(cur_filename.c_str()))
|
|
|
|
{
|
2010-02-02 21:56:29 +00:00
|
|
|
if (File::Exists((std::string(File::GetUserPath(D_STATESAVES_IDX)) + "lastState.sav").c_str()))
|
|
|
|
File::Delete((std::string(File::GetUserPath(D_STATESAVES_IDX)) + "lastState.sav").c_str());
|
2009-06-28 01:11:35 +00:00
|
|
|
|
2010-02-02 21:56:29 +00:00
|
|
|
if (!File::Rename(cur_filename.c_str(), (std::string(File::GetUserPath(D_STATESAVES_IDX)) + "lastState.sav").c_str()))
|
2009-06-28 01:11:35 +00:00
|
|
|
Core::DisplayMessage("Failed to move previous state to state undo backup", 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE *f = fopen(filename.c_str(), "wb");
|
2010-04-08 16:59:35 +00:00
|
|
|
if (f == NULL)
|
|
|
|
{
|
2008-12-08 05:30:24 +00:00
|
|
|
Core::DisplayMessage("Could not save state", 2000);
|
2010-04-11 11:16:57 +00:00
|
|
|
delete[] buffer;
|
2009-06-26 22:36:44 +00:00
|
|
|
return 0;
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
|
|
|
|
2009-06-28 01:11:35 +00:00
|
|
|
// Setting up the header
|
2010-02-06 05:22:53 +00:00
|
|
|
memcpy(header.gameID, SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), 6);
|
2009-06-28 01:11:35 +00:00
|
|
|
header.sz = bCompressed ? sz : 0;
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2009-06-28 01:11:35 +00:00
|
|
|
fwrite(&header, sizeof(state_header), 1, f);
|
2010-04-08 16:59:35 +00:00
|
|
|
if (bCompressed)
|
|
|
|
{
|
2009-09-15 18:54:10 +00:00
|
|
|
lzo_uint cur_len = 0;
|
2009-07-22 22:50:52 +00:00
|
|
|
lzo_uint i = 0;
|
|
|
|
|
2010-04-08 16:59:35 +00:00
|
|
|
for (;;)
|
|
|
|
{
|
2009-09-15 18:54:10 +00:00
|
|
|
if ((i + IN_LEN) >= sz)
|
2009-07-22 22:50:52 +00:00
|
|
|
cur_len = sz - i;
|
|
|
|
else
|
|
|
|
cur_len = IN_LEN;
|
|
|
|
|
2009-09-15 18:54:10 +00:00
|
|
|
if (lzo1x_1_compress((buffer + i), cur_len, out, &out_len, wrkmem) != LZO_E_OK)
|
2009-07-22 22:50:52 +00:00
|
|
|
PanicAlert("Internal LZO Error - compression failed");
|
|
|
|
|
|
|
|
// The size of the data to write is 'out_len'
|
|
|
|
fwrite(&out_len, sizeof(int), 1, f);
|
|
|
|
fwrite(out, out_len, 1, f);
|
|
|
|
|
2009-09-15 18:54:10 +00:00
|
|
|
if (cur_len != IN_LEN)
|
2009-07-22 22:50:52 +00:00
|
|
|
break;
|
|
|
|
i += cur_len;
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
2009-09-15 18:54:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-12-08 05:30:24 +00:00
|
|
|
fwrite(buffer, sz, 1, f);
|
2009-09-15 18:54:10 +00:00
|
|
|
}
|
2008-12-08 05:30:24 +00:00
|
|
|
|
|
|
|
fclose(f);
|
2010-04-11 11:16:57 +00:00
|
|
|
delete[] buffer;
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2009-01-15 06:48:15 +00:00
|
|
|
Core::DisplayMessage(StringFromFormat("Saved State to %s",
|
2009-06-28 01:11:35 +00:00
|
|
|
filename.c_str()).c_str(), 2000);
|
2009-06-26 22:36:44 +00:00
|
|
|
|
2009-09-15 18:54:10 +00:00
|
|
|
state_op_in_progress = false;
|
2009-06-26 22:36:44 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SaveStateCallback(u64 userdata, int cyclesLate)
|
|
|
|
{
|
2010-07-30 09:24:23 +00:00
|
|
|
// Stop the clock while we save the state
|
|
|
|
PowerPC::Pause();
|
2009-06-26 22:36:44 +00:00
|
|
|
|
2010-07-30 09:24:23 +00:00
|
|
|
// Wait for the other threaded sub-systems to stop too
|
2010-07-30 11:58:18 +00:00
|
|
|
SLEEP(100);
|
2010-07-30 09:24:23 +00:00
|
|
|
|
|
|
|
State_Flush();
|
2009-09-15 18:54:10 +00:00
|
|
|
|
|
|
|
// Measure the size of the buffer.
|
2009-06-26 22:36:44 +00:00
|
|
|
u8 *ptr = 0;
|
|
|
|
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
|
|
|
|
DoState(p);
|
|
|
|
size_t sz = (size_t)ptr;
|
2009-09-15 18:54:10 +00:00
|
|
|
|
|
|
|
// Then actually do the write.
|
2009-06-26 22:36:44 +00:00
|
|
|
u8 *buffer = new u8[sz];
|
|
|
|
ptr = buffer;
|
|
|
|
p.SetMode(PointerWrap::MODE_WRITE);
|
|
|
|
DoState(p);
|
|
|
|
|
|
|
|
saveStruct *saveData = new saveStruct;
|
|
|
|
saveData->buffer = buffer;
|
|
|
|
saveData->size = sz;
|
2010-08-30 07:05:47 +00:00
|
|
|
|
|
|
|
if (Frame::IsRecordingInput())
|
|
|
|
Frame::SaveRecording(StringFromFormat("%s.dtm", cur_filename.c_str()).c_str());
|
2009-06-26 22:36:44 +00:00
|
|
|
|
|
|
|
Core::DisplayMessage("Saving State...", 1000);
|
|
|
|
|
|
|
|
saveThread = new Common::Thread(CompressAndDumpState, saveData);
|
2010-07-30 09:24:23 +00:00
|
|
|
|
|
|
|
// Resume the clock
|
|
|
|
PowerPC::Start();
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void LoadStateCallback(u64 userdata, int cyclesLate)
|
|
|
|
{
|
|
|
|
bool bCompressedState;
|
|
|
|
|
2010-07-30 09:24:23 +00:00
|
|
|
// Stop the clock while we load the state
|
|
|
|
PowerPC::Pause();
|
|
|
|
|
|
|
|
// Wait for the other threaded sub-systems to stop too
|
2010-07-30 11:58:18 +00:00
|
|
|
SLEEP(100);
|
2010-07-30 09:24:23 +00:00
|
|
|
|
2010-04-17 21:02:03 +00:00
|
|
|
State_Flush();
|
2009-06-28 01:11:35 +00:00
|
|
|
|
2009-06-28 21:11:51 +00:00
|
|
|
// Save temp buffer for undo load state
|
2010-04-17 21:02:03 +00:00
|
|
|
// TODO: this should be controlled by a user option,
|
|
|
|
// because it slows down every savestate load to provide an often-unused feature.
|
|
|
|
{
|
|
|
|
delete[] undoLoad;
|
|
|
|
undoLoad = NULL;
|
|
|
|
cur_buffer = &undoLoad;
|
|
|
|
SaveBufferStateCallback(userdata, cyclesLate);
|
|
|
|
}
|
2009-06-28 21:11:51 +00:00
|
|
|
|
2008-12-08 05:30:24 +00:00
|
|
|
FILE *f = fopen(cur_filename.c_str(), "rb");
|
2009-09-15 18:54:10 +00:00
|
|
|
if (!f)
|
|
|
|
{
|
2008-12-08 05:30:24 +00:00
|
|
|
Core::DisplayMessage("State not found", 2000);
|
2010-12-09 12:04:03 +00:00
|
|
|
// Resume the clock
|
|
|
|
PowerPC::Start();
|
2008-12-08 05:30:24 +00:00
|
|
|
return;
|
|
|
|
}
|
2009-01-15 06:48:15 +00:00
|
|
|
|
2008-12-08 05:30:24 +00:00
|
|
|
u8 *buffer = NULL;
|
2009-06-28 01:11:35 +00:00
|
|
|
state_header header;
|
|
|
|
size_t sz;
|
|
|
|
|
|
|
|
fread(&header, sizeof(state_header), 1, f);
|
|
|
|
|
2010-02-06 05:22:53 +00:00
|
|
|
if (memcmp(SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), header.gameID, 6))
|
2009-06-28 01:11:35 +00:00
|
|
|
{
|
|
|
|
char gameID[7] = {0};
|
|
|
|
memcpy(gameID, header.gameID, 6);
|
|
|
|
Core::DisplayMessage(StringFromFormat("State belongs to a different game (ID %s)",
|
|
|
|
gameID), 2000);
|
|
|
|
|
|
|
|
fclose(f);
|
2010-12-09 12:04:03 +00:00
|
|
|
// Resume the clock
|
|
|
|
PowerPC::Start();
|
2009-06-28 01:11:35 +00:00
|
|
|
return;
|
|
|
|
}
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2009-06-28 01:11:35 +00:00
|
|
|
sz = header.sz;
|
2008-12-08 05:30:24 +00:00
|
|
|
bCompressedState = (sz != 0);
|
2009-09-15 18:54:10 +00:00
|
|
|
if (bCompressedState)
|
|
|
|
{
|
2009-06-28 01:11:35 +00:00
|
|
|
Core::DisplayMessage("Decompressing State...", 500);
|
|
|
|
|
2009-07-22 22:50:52 +00:00
|
|
|
lzo_uint i = 0;
|
|
|
|
buffer = new u8[sz];
|
2010-04-08 16:59:35 +00:00
|
|
|
if (!buffer)
|
|
|
|
{
|
2009-07-22 22:50:52 +00:00
|
|
|
PanicAlert("Error allocating buffer");
|
2010-12-09 12:04:03 +00:00
|
|
|
// Resume the clock
|
|
|
|
PowerPC::Start();
|
2009-07-22 22:50:52 +00:00
|
|
|
return;
|
|
|
|
}
|
2009-09-15 18:54:10 +00:00
|
|
|
while (true)
|
|
|
|
{
|
2010-04-08 16:59:35 +00:00
|
|
|
lzo_uint cur_len = 0; // number of bytes to read
|
|
|
|
lzo_uint new_len = 0; // number of bytes to write
|
2009-07-22 22:50:52 +00:00
|
|
|
if (fread(&cur_len, 1, sizeof(int), f) == 0)
|
|
|
|
break;
|
2009-09-15 18:54:10 +00:00
|
|
|
if (feof(f))
|
|
|
|
break; // don't know if this happens.
|
2009-07-22 22:50:52 +00:00
|
|
|
fread(out, 1, cur_len, f);
|
|
|
|
int res = lzo1x_decompress(out, cur_len, (buffer + i), &new_len, NULL);
|
2009-09-15 18:54:10 +00:00
|
|
|
if (res != LZO_E_OK)
|
|
|
|
{
|
|
|
|
// This doesn't seem to happen anymore.
|
2010-12-05 09:04:34 +00:00
|
|
|
PanicAlert("Internal LZO Error - decompression failed (%d) (%li, %li) \n"
|
2009-07-22 22:50:52 +00:00
|
|
|
"Try loading the state again", res, i, new_len);
|
|
|
|
fclose(f);
|
2010-04-11 11:16:57 +00:00
|
|
|
delete[] buffer;
|
2010-12-09 12:04:03 +00:00
|
|
|
// Resume the clock
|
|
|
|
PowerPC::Start();
|
2009-07-22 22:50:52 +00:00
|
|
|
return;
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
2009-07-22 22:50:52 +00:00
|
|
|
|
|
|
|
i += new_len;
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
2009-09-15 18:54:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-12-03 12:42:01 +00:00
|
|
|
sz = (int)(File::GetSize(f) - sizeof(state_header));
|
2008-12-08 05:30:24 +00:00
|
|
|
buffer = new u8[sz];
|
|
|
|
int x;
|
2009-09-08 21:16:05 +00:00
|
|
|
if ((x = (int)fread(buffer, 1, sz, f)) != (int)sz)
|
2010-12-05 18:22:41 +00:00
|
|
|
PanicAlert("wtf? %d %lu", x, (unsigned long)sz);
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
u8 *ptr = buffer;
|
|
|
|
PointerWrap p(&ptr, PointerWrap::MODE_READ);
|
|
|
|
DoState(p);
|
|
|
|
|
2009-11-01 01:24:30 +00:00
|
|
|
if (p.GetMode() == PointerWrap::MODE_READ)
|
|
|
|
Core::DisplayMessage(StringFromFormat("Loaded state from %s", cur_filename.c_str()).c_str(), 2000);
|
|
|
|
else
|
|
|
|
Core::DisplayMessage("Unable to Load : Can't load state from other revisions !", 4000);
|
|
|
|
|
2010-04-11 11:16:57 +00:00
|
|
|
delete[] buffer;
|
2010-08-30 07:05:47 +00:00
|
|
|
|
|
|
|
if (File::Exists(StringFromFormat("%s.dtm", cur_filename.c_str()).c_str()))
|
|
|
|
Frame::LoadInput(StringFromFormat("%s.dtm", cur_filename.c_str()).c_str());
|
|
|
|
else
|
|
|
|
Frame::EndPlayInput();
|
2010-07-30 09:24:23 +00:00
|
|
|
|
|
|
|
state_op_in_progress = false;
|
|
|
|
|
|
|
|
// Resume the clock
|
|
|
|
PowerPC::Start();
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
|
|
|
|
2010-04-17 21:02:03 +00:00
|
|
|
void VerifyStateCallback(u64 userdata, int cyclesLate)
|
|
|
|
{
|
|
|
|
bool bCompressedState;
|
|
|
|
|
|
|
|
State_Flush();
|
|
|
|
|
|
|
|
FILE *f = fopen(cur_filename.c_str(), "rb");
|
|
|
|
if (!f)
|
|
|
|
{
|
|
|
|
Core::DisplayMessage("State not found", 2000);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 *buffer = NULL;
|
|
|
|
state_header header;
|
|
|
|
size_t sz;
|
|
|
|
|
|
|
|
fread(&header, sizeof(state_header), 1, f);
|
|
|
|
|
|
|
|
if (memcmp(SConfig::GetInstance().m_LocalCoreStartupParameter.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);
|
|
|
|
|
|
|
|
lzo_uint i = 0;
|
|
|
|
buffer = new u8[sz];
|
|
|
|
if (!buffer) {
|
|
|
|
PanicAlert("Error allocating buffer");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
lzo_uint cur_len = 0;
|
|
|
|
lzo_uint new_len = 0;
|
|
|
|
if (fread(&cur_len, 1, sizeof(int), f) == 0)
|
|
|
|
break;
|
|
|
|
if (feof(f))
|
|
|
|
break; // don't know if this happens.
|
|
|
|
fread(out, 1, cur_len, f);
|
|
|
|
int res = lzo1x_decompress(out, cur_len, (buffer + i), &new_len, NULL);
|
|
|
|
if (res != LZO_E_OK)
|
|
|
|
{
|
|
|
|
// This doesn't seem to happen anymore.
|
2010-12-05 09:04:34 +00:00
|
|
|
PanicAlert("Internal LZO Error - decompression failed (%d) (%ld, %ld) \n"
|
2010-04-17 21:02:03 +00:00
|
|
|
"Try verifying the state again", res, i, new_len);
|
|
|
|
fclose(f);
|
|
|
|
delete [] buffer;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The size of the data to read to our buffer is 'new_len'
|
|
|
|
i += new_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-12-03 12:42:01 +00:00
|
|
|
sz = (int)(File::GetSize(f) - sizeof(int));
|
2010-04-17 21:02:03 +00:00
|
|
|
buffer = new u8[sz];
|
|
|
|
int x;
|
|
|
|
if ((x = (int)fread(buffer, 1, sz, f)) != (int)sz)
|
2010-12-05 18:22:41 +00:00
|
|
|
PanicAlert("wtf? %d %lu", x, (unsigned long)sz);
|
2010-04-17 21:02:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
u8 *ptr = buffer;
|
|
|
|
PointerWrap p(&ptr, PointerWrap::MODE_VERIFY);
|
|
|
|
DoState(p);
|
|
|
|
|
|
|
|
if (p.GetMode() == PointerWrap::MODE_READ)
|
|
|
|
Core::DisplayMessage(StringFromFormat("Verified state at %s", cur_filename.c_str()).c_str(), 2000);
|
|
|
|
else
|
|
|
|
Core::DisplayMessage("Unable to Verify : Can't verify state from other revisions !", 4000);
|
|
|
|
|
|
|
|
delete [] buffer;
|
|
|
|
}
|
|
|
|
|
2008-12-08 05:30:24 +00:00
|
|
|
void State_Init()
|
|
|
|
{
|
|
|
|
ev_Load = CoreTiming::RegisterEvent("LoadState", &LoadStateCallback);
|
|
|
|
ev_Save = CoreTiming::RegisterEvent("SaveState", &SaveStateCallback);
|
2010-04-17 21:02:03 +00:00
|
|
|
ev_Verify = CoreTiming::RegisterEvent("VerifyState", &VerifyStateCallback);
|
2009-06-28 21:11:51 +00:00
|
|
|
ev_BufferLoad = CoreTiming::RegisterEvent("LoadBufferState", &LoadBufferStateCallback);
|
|
|
|
ev_BufferSave = CoreTiming::RegisterEvent("SaveBufferState", &SaveBufferStateCallback);
|
2010-04-17 21:02:03 +00:00
|
|
|
ev_BufferVerify = CoreTiming::RegisterEvent("VerifyBufferState", &VerifyBufferStateCallback);
|
2009-07-22 22:50:52 +00:00
|
|
|
|
|
|
|
if (lzo_init() != LZO_E_OK)
|
|
|
|
PanicAlert("Internal LZO Error - lzo_init() failed");
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void State_Shutdown()
|
|
|
|
{
|
2010-04-17 21:02:03 +00:00
|
|
|
State_Flush();
|
2009-06-28 21:11:51 +00:00
|
|
|
|
2009-09-15 18:54:10 +00:00
|
|
|
if (undoLoad)
|
2009-06-28 21:11:51 +00:00
|
|
|
{
|
|
|
|
delete[] undoLoad;
|
|
|
|
undoLoad = NULL;
|
|
|
|
}
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
|
|
|
|
2008-12-20 12:10:59 +00:00
|
|
|
std::string MakeStateFilename(int state_number)
|
|
|
|
{
|
2010-02-06 05:22:53 +00:00
|
|
|
return StringFromFormat("%s%s.s%02i", File::GetUserPath(D_STATESAVES_IDX), SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), state_number);
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
|
|
|
|
2009-06-28 01:11:35 +00:00
|
|
|
void State_SaveAs(const std::string &filename)
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
2009-09-15 18:54:10 +00:00
|
|
|
if (state_op_in_progress)
|
|
|
|
return;
|
|
|
|
state_op_in_progress = true;
|
2009-06-28 01:11:35 +00:00
|
|
|
cur_filename = filename;
|
|
|
|
lastFilename = filename;
|
2010-04-17 21:02:03 +00:00
|
|
|
CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev_Save);
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
|
|
|
|
2009-06-28 01:11:35 +00:00
|
|
|
void State_Save(int slot)
|
|
|
|
{
|
|
|
|
State_SaveAs(MakeStateFilename(slot));
|
|
|
|
}
|
|
|
|
|
|
|
|
void State_LoadAs(const std::string &filename)
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
2009-09-15 18:54:10 +00:00
|
|
|
if (state_op_in_progress)
|
|
|
|
return;
|
|
|
|
state_op_in_progress = true;
|
2009-06-28 01:11:35 +00:00
|
|
|
cur_filename = filename;
|
2010-04-17 21:02:03 +00:00
|
|
|
CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev_Load);
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
2009-06-28 01:11:35 +00:00
|
|
|
|
|
|
|
void State_Load(int slot)
|
|
|
|
{
|
|
|
|
State_LoadAs(MakeStateFilename(slot));
|
|
|
|
}
|
|
|
|
|
2010-04-17 21:02:03 +00:00
|
|
|
void State_VerifyAt(const std::string &filename)
|
|
|
|
{
|
|
|
|
if (state_op_in_progress)
|
|
|
|
return;
|
|
|
|
state_op_in_progress = true;
|
|
|
|
cur_filename = filename;
|
|
|
|
CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev_Verify);
|
|
|
|
}
|
|
|
|
|
|
|
|
void State_Verify(int slot)
|
|
|
|
{
|
|
|
|
State_VerifyAt(MakeStateFilename(slot));
|
|
|
|
}
|
|
|
|
|
2009-06-28 01:11:35 +00:00
|
|
|
void State_LoadLastSaved()
|
|
|
|
{
|
2009-09-15 18:54:10 +00:00
|
|
|
if (lastFilename.empty())
|
2009-06-28 01:11:35 +00:00
|
|
|
Core::DisplayMessage("There is no last saved state", 2000);
|
|
|
|
else
|
|
|
|
State_LoadAs(lastFilename);
|
|
|
|
}
|
2009-06-28 21:11:51 +00:00
|
|
|
|
|
|
|
void State_LoadFromBuffer(u8 **buffer)
|
|
|
|
{
|
2009-09-15 18:54:10 +00:00
|
|
|
if (state_op_in_progress)
|
|
|
|
return;
|
|
|
|
state_op_in_progress = true;
|
2009-06-28 21:11:51 +00:00
|
|
|
cur_buffer = buffer;
|
2010-04-17 21:02:03 +00:00
|
|
|
CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev_BufferLoad);
|
2009-06-28 21:11:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void State_SaveToBuffer(u8 **buffer)
|
|
|
|
{
|
2009-09-15 18:54:10 +00:00
|
|
|
if (state_op_in_progress)
|
|
|
|
return;
|
|
|
|
state_op_in_progress = true;
|
2009-06-28 21:11:51 +00:00
|
|
|
cur_buffer = buffer;
|
2010-04-17 21:02:03 +00:00
|
|
|
CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev_BufferSave);
|
|
|
|
}
|
|
|
|
|
|
|
|
void State_VerifyBuffer(u8 **buffer)
|
|
|
|
{
|
|
|
|
if (state_op_in_progress)
|
|
|
|
return;
|
|
|
|
state_op_in_progress = true;
|
|
|
|
cur_buffer = buffer;
|
|
|
|
CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev_BufferVerify);
|
|
|
|
}
|
|
|
|
|
|
|
|
void State_Flush()
|
|
|
|
{
|
|
|
|
// If already saving state, wait for it to finish
|
|
|
|
if (saveThread)
|
|
|
|
{
|
|
|
|
delete saveThread;
|
|
|
|
saveThread = NULL;
|
|
|
|
}
|
2009-06-28 21:11:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Load the last state before loading the state
|
|
|
|
void State_UndoLoadState()
|
|
|
|
{
|
|
|
|
State_LoadFromBuffer(&undoLoad);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load the state that the last save state overwritten on
|
|
|
|
void State_UndoSaveState()
|
|
|
|
{
|
2010-02-02 21:56:29 +00:00
|
|
|
State_LoadAs((std::string(File::GetUserPath(D_STATESAVES_IDX)) + "lastState.sav").c_str());
|
2009-06-28 21:11:51 +00:00
|
|
|
}
|
2009-11-07 20:01:39 +00:00
|
|
|
|
|
|
|
size_t State_GetSize()
|
|
|
|
{
|
|
|
|
// Measure the size of the buffer.
|
|
|
|
u8 *ptr = 0;
|
|
|
|
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
|
|
|
|
DoState(p);
|
|
|
|
return (size_t)ptr;
|
|
|
|
}
|