Eliminated memory leaks of the save state code and put it in a namespace. It is prettier than before, but it could be better (less global usage). Other minor stuff.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@7366 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
Jordan Woyak
2011-03-17 10:17:45 +00:00
parent e4141c331e
commit 78d9f927e1
14 changed files with 264 additions and 392 deletions

View File

@ -57,6 +57,7 @@
#include "DSPEmulator.h" #include "DSPEmulator.h"
#include "ConfigManager.h" #include "ConfigManager.h"
#include "VideoBackendBase.h" #include "VideoBackendBase.h"
#include "OnScreenDisplay.h"
#include "VolumeHandler.h" #include "VolumeHandler.h"
#include "FileMonitor.h" #include "FileMonitor.h"
@ -200,6 +201,8 @@ bool Init()
return false; return false;
} }
OSD::AddMessage(("Dolphin " + g_video_backend->GetName() + " Video Backend.").c_str(), 5000);
HW::Init(); HW::Init();
if (!DSP::GetDSPEmulator()->Initialize(g_pWindowHandle, if (!DSP::GetDSPEmulator()->Initialize(g_pWindowHandle,
_CoreParameter.bWii, _CoreParameter.bDSPThread)) _CoreParameter.bWii, _CoreParameter.bDSPThread))
@ -321,7 +324,7 @@ void CpuThread()
EMM::InstallExceptionHandler(); // Let's run under memory watch EMM::InstallExceptionHandler(); // Let's run under memory watch
if (!g_stateFileName.empty()) if (!g_stateFileName.empty())
State_LoadAs(g_stateFileName); State::LoadAs(g_stateFileName);
// Enter CPU run loop. When we leave it - we are done. // Enter CPU run loop. When we leave it - we are done.
CCPU::Run(); CCPU::Run();

View File

@ -44,7 +44,7 @@ namespace HW
{ {
CoreTiming::Init(); CoreTiming::Init();
State_Init(); State::Init();
// Init the whole Hardware // Init the whole Hardware
AudioInterface::Init(); AudioInterface::Init();
@ -82,7 +82,7 @@ namespace HW
WII_IPC_HLE_Interface::Shutdown(); WII_IPC_HLE_Interface::Shutdown();
} }
State_Shutdown(); State::Shutdown();
CoreTiming::Shutdown(); CoreTiming::Shutdown();
} }

View File

@ -17,6 +17,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <regex>
#include <windows.h> #include <windows.h>
#include <dbt.h> #include <dbt.h>
@ -406,6 +407,9 @@ int UnPair()
// negative number on failure // negative number on failure
int PairUp(bool unpair) int PairUp(bool unpair)
{ {
// match strings like "Nintendo RVL-WBC-01", "Nintendo RVL-CNT-01"
const std::wregex wiimote_device_name(L"Nintendo RVL-\\w{3}-\\d{2}");
int nPaired = 0; int nPaired = 0;
BLUETOOTH_DEVICE_SEARCH_PARAMS srch; BLUETOOTH_DEVICE_SEARCH_PARAMS srch;
@ -451,9 +455,7 @@ int PairUp(bool unpair)
DEBUG_LOG(WIIMOTE, "authed %i connected %i remembered %i ", DEBUG_LOG(WIIMOTE, "authed %i connected %i remembered %i ",
btdi.fAuthenticated, btdi.fConnected, btdi.fRemembered); btdi.fAuthenticated, btdi.fConnected, btdi.fRemembered);
// TODO: Probably could just check for "Nintendo RVL" if (std::regex_match(btdi.szName, wiimote_device_name))
if (0 == wcscmp(btdi.szName, L"Nintendo RVL-WBC-01") ||
0 == wcscmp(btdi.szName, L"Nintendo RVL-CNT-01"))
{ {
if (unpair) if (unpair)
{ {

View File

@ -205,7 +205,7 @@ bool BeginRecordingInput(int controllers)
const std::string stateFilename = g_recordFile + ".sav"; const std::string stateFilename = g_recordFile + ".sav";
if(File::Exists(stateFilename)) if(File::Exists(stateFilename))
File::Delete(stateFilename); File::Delete(stateFilename);
State_SaveAs(stateFilename.c_str()); State::SaveAs(stateFilename.c_str());
g_bRecordingFromSaveState = true; g_bRecordingFromSaveState = true;
} }

View File

@ -16,7 +16,6 @@
// http://code.google.com/p/dolphin-emu/ // http://code.google.com/p/dolphin-emu/
#include "Common.h" #include "Common.h"
#include "State.h" #include "State.h"
#include "Core.h" #include "Core.h"
#include "ConfigManager.h" #include "ConfigManager.h"
@ -29,61 +28,64 @@
#include "HW/HW.h" #include "HW/HW.h"
#include "HW/CPU.h" #include "HW/CPU.h"
#include "PowerPC/JitCommon/JitBase.h" #include "PowerPC/JitCommon/JitBase.h"
#include "VideoBackendBase.h" #include "VideoBackendBase.h"
#include <string>
#include <lzo/lzo1x.h> #include <lzo/lzo1x.h>
// TODO: Move to namespace namespace State
{
// TODO: Investigate a memory leak on save/load state
#if defined(__LZO_STRICT_16BIT) #if defined(__LZO_STRICT_16BIT)
#define IN_LEN (8*1024u) static const u32 IN_LEN = 8 * 1024u;
#elif defined(LZO_ARCH_I086) && !defined(LZO_HAVE_MM_HUGE_ARRAY) #elif defined(LZO_ARCH_I086) && !defined(LZO_HAVE_MM_HUGE_ARRAY)
#define IN_LEN (60*1024u) static const u32 IN_LEN = 60 * 1024u;
#else #else
#define IN_LEN (128*1024ul) static const u32 IN_LEN = 128 * 1024u;
#endif #endif
#define OUT_LEN (IN_LEN + IN_LEN / 16 + 64 + 3)
static unsigned char __LZO_MMODEL out [ OUT_LEN ]; static const u32 OUT_LEN = IN_LEN + (IN_LEN / 16) + 64 + 3;
#define HEAP_ALLOC(var,size) \ static unsigned char __LZO_MMODEL out[OUT_LEN];
lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ]
static HEAP_ALLOC(wrkmem,LZO1X_1_MEM_COMPRESS); #define HEAP_ALLOC(var, size) \
lzo_align_t __LZO_MMODEL var[((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t)]
static bool state_op_in_progress = false; static HEAP_ALLOC(wrkmem, LZO1X_1_MEM_COMPRESS);
static int ev_Save, ev_BufferSave; static volatile bool g_op_in_progress = false;
static int ev_Load, ev_BufferLoad;
static int ev_Verify, ev_BufferVerify;
static std::string cur_filename, lastFilename; static int ev_FileSave, ev_BufferSave, ev_FileLoad, ev_BufferLoad, ev_FileVerify, ev_BufferVerify;
static u8 **cur_buffer = NULL;
// Temporary undo state buffers static std::string g_current_filename, g_last_filename;
static u8 *undoLoad = NULL;
static bool const bCompressed = true; // Temporary undo state buffer
static std::vector<u8> g_undo_load_buffer;
static std::thread saveThread; static std::vector<u8> g_current_buffer;
static std::thread g_save_thread;
// Don't forget to increase this after doing changes on the savestate system // Don't forget to increase this after doing changes on the savestate system
#define STATE_VERSION 4 static const int VERSION = 4;
struct StateHeader
{
u8 gameID[6];
size_t size;
};
static bool g_use_compression = true;
void EnableCompression(bool compression)
{
g_use_compression = compression;
}
void DoState(PointerWrap &p) void DoState(PointerWrap &p)
{ {
u32 cookie = 0xBAADBABE + STATE_VERSION; u32 cookie = 0xBAADBABE + VERSION;
p.Do(cookie); p.Do(cookie);
if (cookie != 0xBAADBABE + STATE_VERSION) if (cookie != 0xBAADBABE + VERSION)
{ {
//PanicAlert("Savestate version mismatch !\nSorry, you can't load states from other revisions.");
p.SetMode(PointerWrap::MODE_MEASURE); p.SetMode(PointerWrap::MODE_MEASURE);
return; return;
} }
@ -105,115 +107,88 @@ void DoState(PointerWrap &p)
void LoadBufferStateCallback(u64 userdata, int cyclesLate) void LoadBufferStateCallback(u64 userdata, int cyclesLate)
{ {
if (!cur_buffer || !*cur_buffer) { u8* ptr = &g_current_buffer[0];
Core::DisplayMessage("State does not exist", 1000);
return;
}
u8 *ptr = *cur_buffer;
PointerWrap p(&ptr, PointerWrap::MODE_READ); PointerWrap p(&ptr, PointerWrap::MODE_READ);
DoState(p); DoState(p);
Core::DisplayMessage("Loaded state", 2000); Core::DisplayMessage("Loaded state", 2000);
state_op_in_progress = false;
g_op_in_progress = false;
} }
void SaveBufferStateCallback(u64 userdata, int cyclesLate) void SaveBufferStateCallback(u64 userdata, int cyclesLate)
{ {
if (!cur_buffer) { u8* ptr = NULL;
Core::DisplayMessage("Error saving state", 1000);
return;
}
u8 *ptr = NULL;
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
if (!*cur_buffer) DoState(p);
{ const size_t buffer_size = reinterpret_cast<size_t>(ptr);
// if we got passed an empty buffer, g_current_buffer.resize(buffer_size);
// 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
}
ptr = *cur_buffer; ptr = &g_current_buffer[0];
p.SetMode(PointerWrap::MODE_WRITE); p.SetMode(PointerWrap::MODE_WRITE);
DoState(p); DoState(p);
state_op_in_progress = false; g_op_in_progress = false;
} }
void VerifyBufferStateCallback(u64 userdata, int cyclesLate) void VerifyBufferStateCallback(u64 userdata, int cyclesLate)
{ {
if (!cur_buffer || !*cur_buffer) { u8* ptr = &g_current_buffer[0];
Core::DisplayMessage("State does not exist", 1000);
return;
}
u8 *ptr = *cur_buffer;
PointerWrap p(&ptr, PointerWrap::MODE_VERIFY); PointerWrap p(&ptr, PointerWrap::MODE_VERIFY);
DoState(p); DoState(p);
Core::DisplayMessage("Verified state", 2000); Core::DisplayMessage("Verified state", 2000);
state_op_in_progress = false;
g_op_in_progress = false;
} }
void CompressAndDumpState(saveStruct* saveArg) void CompressAndDumpState(const std::vector<u8>* save_arg)
{ {
u8 *buffer = saveArg->buffer; const u8* const buffer_data = &(*save_arg)[0];
size_t sz = saveArg->size; const size_t buffer_size = save_arg->size();
lzo_uint out_len = 0;
state_header header;
std::string filename = cur_filename;
delete saveArg;
// For easy debugging // For easy debugging
Common::SetCurrentThreadName("SaveState thread"); Common::SetCurrentThreadName("SaveState thread");
// Moving to last overwritten save-state // Moving to last overwritten save-state
if (File::Exists(cur_filename)) if (File::Exists(g_current_filename))
{ {
if (File::Exists(File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav")) if (File::Exists(File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav"))
File::Delete((File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav")); File::Delete((File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav"));
if (!File::Rename(cur_filename, File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav")) if (!File::Rename(g_current_filename, File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav"))
Core::DisplayMessage("Failed to move previous state to state undo backup", 1000); Core::DisplayMessage("Failed to move previous state to state undo backup", 1000);
} }
File::IOFile f(filename, "wb"); File::IOFile f(g_current_filename, "wb");
if (!f) if (!f)
{ {
Core::DisplayMessage("Could not save state", 2000); Core::DisplayMessage("Could not save state", 2000);
delete[] buffer;
return; return;
} }
// Setting up the header // Setting up the header
StateHeader header;
memcpy(header.gameID, SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), 6); memcpy(header.gameID, SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), 6);
header.sz = bCompressed ? sz : 0; header.size = g_use_compression ? buffer_size : 0;
f.WriteArray(&header, 1); f.WriteArray(&header, 1);
if (bCompressed)
{
lzo_uint cur_len = 0;
lzo_uint i = 0;
for (;;) if (0 != header.size) // non-zero header size means the state is compressed
{
lzo_uint i = 0;
while (true)
{ {
if ((i + IN_LEN) >= sz) lzo_uint cur_len = 0;
cur_len = sz - i; lzo_uint out_len = 0;
if ((i + IN_LEN) >= buffer_size)
cur_len = buffer_size - i;
else else
cur_len = IN_LEN; cur_len = IN_LEN;
if (lzo1x_1_compress((buffer + i), cur_len, out, &out_len, wrkmem) != LZO_E_OK) if (lzo1x_1_compress(buffer_data + i, cur_len, out, &out_len, wrkmem) != LZO_E_OK)
PanicAlertT("Internal LZO Error - compression failed"); PanicAlertT("Internal LZO Error - compression failed");
// The size of the data to write is 'out_len' // The size of the data to write is 'out_len'
@ -222,123 +197,83 @@ void CompressAndDumpState(saveStruct* saveArg)
if (cur_len != IN_LEN) if (cur_len != IN_LEN)
break; break;
i += cur_len; i += cur_len;
} }
} }
else else // uncompressed
{ {
f.WriteBytes(buffer, sz); f.WriteBytes(buffer_data, buffer_size);
} }
delete[] buffer;
Core::DisplayMessage(StringFromFormat("Saved State to %s", Core::DisplayMessage(StringFromFormat("Saved State to %s",
filename.c_str()).c_str(), 2000); g_current_filename.c_str()).c_str(), 2000);
state_op_in_progress = false; g_op_in_progress = false;
} }
void SaveStateCallback(u64 userdata, int cyclesLate) void SaveFileStateCallback(u64 userdata, int cyclesLate)
{ {
// Pause the core while we save the state // Pause the core while we save the state
CCPU::EnableStepping(true); CCPU::EnableStepping(true);
// Wait for the other threaded sub-systems to stop too // Wait for the other threaded sub-systems to stop too
// TODO: this is ugly
SLEEP(100); SLEEP(100);
State_Flush(); Flush();
// Measure the size of the buffer. // Measure the size of the buffer.
u8 *ptr = 0; u8 *ptr = NULL;
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
DoState(p); DoState(p);
size_t sz = (size_t)ptr; const size_t buffer_size = reinterpret_cast<size_t>(ptr);
// Then actually do the write. // Then actually do the write.
u8 *buffer = new u8[sz]; g_current_buffer.resize(buffer_size);
ptr = buffer; ptr = &g_current_buffer[0];
p.SetMode(PointerWrap::MODE_WRITE); p.SetMode(PointerWrap::MODE_WRITE);
DoState(p); DoState(p);
saveStruct *saveData = new saveStruct;
saveData->buffer = buffer;
saveData->size = sz;
if ((Frame::IsRecordingInput() || Frame::IsPlayingInput()) && !Frame::IsRecordingInputFromSaveState()) if ((Frame::IsRecordingInput() || Frame::IsPlayingInput()) && !Frame::IsRecordingInputFromSaveState())
Frame::SaveRecording(StringFromFormat("%s.dtm", cur_filename.c_str()).c_str()); Frame::SaveRecording(StringFromFormat("%s.dtm", g_current_filename.c_str()).c_str());
Core::DisplayMessage("Saving State...", 1000); Core::DisplayMessage("Saving State...", 1000);
saveThread = std::thread(CompressAndDumpState, saveData); g_save_thread = std::thread(CompressAndDumpState, &g_current_buffer);
// Resume the core and disable stepping // Resume the core and disable stepping
CCPU::EnableStepping(false); CCPU::EnableStepping(false);
} }
void LoadStateCallback(u64 userdata, int cyclesLate) void LoadFileStateData(std::string& filename, std::vector<u8>& ret_data)
{ {
bool bCompressedState; File::IOFile f(filename, "rb");
// Stop the core while we load the state
CCPU::EnableStepping(true);
// Wait for the other threaded sub-systems to stop too
SLEEP(100);
State_Flush();
// Save temp buffer for undo load state
// 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);
}
File::IOFile f(cur_filename, "rb");
if (!f) if (!f)
{ {
Core::DisplayMessage("State not found", 2000); Core::DisplayMessage("State not found", 2000);
// Resume the clock
CCPU::EnableStepping(false);
return; return;
} }
u8 *buffer = NULL; StateHeader header;
state_header header;
size_t sz;
f.ReadArray(&header, 1); f.ReadArray(&header, 1);
if (memcmp(SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), header.gameID, 6)) if (memcmp(SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), header.gameID, 6))
{ {
char gameID[7] = {0}; Core::DisplayMessage(StringFromFormat("State belongs to a different game (ID %.*s)",
memcpy(gameID, header.gameID, 6); 6, header.gameID), 2000);
Core::DisplayMessage(StringFromFormat("State belongs to a different game (ID %s)",
gameID), 2000);
// Resume the clock
CCPU::EnableStepping(false);
return; return;
} }
sz = header.sz; std::vector<u8> buffer;
bCompressedState = (sz != 0);
if (bCompressedState) if (0 != header.size) // non-zero size means the state is compressed
{ {
Core::DisplayMessage("Decompressing State...", 500); Core::DisplayMessage("Decompressing State...", 500);
buffer.resize(header.size);
lzo_uint i = 0; lzo_uint i = 0;
buffer = new u8[sz];
if (!buffer)
{
PanicAlertT("Error allocating buffer");
// Resume the clock
CCPU::EnableStepping(false);
return;
}
while (true) while (true)
{ {
lzo_uint cur_len = 0; // number of bytes to read lzo_uint cur_len = 0; // number of bytes to read
@ -348,143 +283,103 @@ void LoadStateCallback(u64 userdata, int cyclesLate)
break; break;
f.ReadBytes(out, cur_len); f.ReadBytes(out, cur_len);
int res = lzo1x_decompress(out, cur_len, (buffer + i), &new_len, NULL); const int res = lzo1x_decompress(out, cur_len, &buffer[i], &new_len, NULL);
if (res != LZO_E_OK) if (res != LZO_E_OK)
{ {
// This doesn't seem to happen anymore. // This doesn't seem to happen anymore.
PanicAlertT("Internal LZO Error - decompression failed (%d) (%li, %li) \n" PanicAlertT("Internal LZO Error - decompression failed (%d) (%li, %li) \n"
"Try loading the state again", res, i, new_len); "Try loading the state again", res, i, new_len);
delete[] buffer;
// Resume the clock
CCPU::EnableStepping(false);
return; return;
} }
i += new_len; i += new_len;
} }
} }
else else // uncompressed
{ {
sz = (int)(f.GetSize() - sizeof(state_header)); const size_t size = (size_t)(f.GetSize() - sizeof(StateHeader));
buffer = new u8[sz]; buffer.resize(size);
if (!f.ReadBytes(buffer, sz))
PanicAlert("wtf? reading bytes: %lu", (unsigned long)sz); if (!f.ReadBytes(&buffer[0], size))
{
PanicAlert("wtf? reading bytes: %i", (int)size);
return;
}
} }
f.Close(); // all good
ret_data.swap(buffer);
}
u8 *ptr = buffer; void LoadFileStateCallback(u64 userdata, int cyclesLate)
PointerWrap p(&ptr, PointerWrap::MODE_READ); {
DoState(p); // Stop the core while we load the state
CCPU::EnableStepping(true);
if (p.GetMode() == PointerWrap::MODE_READ) // Wait for the other threaded sub-systems to stop too
Core::DisplayMessage(StringFromFormat("Loaded state from %s", cur_filename.c_str()).c_str(), 2000); // TODO: uglyyy
else SLEEP(100);
Core::DisplayMessage("Unable to Load : Can't load state from other revisions !", 4000);
delete[] buffer; Flush();
// Save temp buffer for undo load state
// TODO: this should be controlled by a user option,
// because it slows down every savestate load to provide an often-unused feature.
SaveBufferStateCallback(userdata, cyclesLate);
g_undo_load_buffer.swap(g_current_buffer);
std::vector<u8> buffer;
LoadFileStateData(g_current_filename, buffer);
if (!buffer.empty())
{
u8 *ptr = &buffer[0];
PointerWrap p(&ptr, PointerWrap::MODE_READ);
DoState(p);
if (p.GetMode() == PointerWrap::MODE_READ)
Core::DisplayMessage(StringFromFormat("Loaded state from %s", g_current_filename.c_str()).c_str(), 2000);
else
Core::DisplayMessage("Unable to Load : Can't load state from other revisions !", 4000);
if (File::Exists(cur_filename + ".dtm")) if (File::Exists(g_current_filename + ".dtm"))
Frame::LoadInput((cur_filename + ".dtm").c_str()); Frame::LoadInput((g_current_filename + ".dtm").c_str());
else if (!Frame::IsRecordingInputFromSaveState()) else if (!Frame::IsRecordingInputFromSaveState())
Frame::EndPlayInput(false); Frame::EndPlayInput(false);
}
state_op_in_progress = false; g_op_in_progress = false;
// Resume the clock // resume dat core
CCPU::EnableStepping(false); CCPU::EnableStepping(false);
} }
void VerifyStateCallback(u64 userdata, int cyclesLate) void VerifyFileStateCallback(u64 userdata, int cyclesLate)
{ {
bool bCompressedState; Flush();
State_Flush(); std::vector<u8> buffer;
LoadFileStateData(g_current_filename, buffer);
File::IOFile f(cur_filename, "rb"); if (!buffer.empty())
if (!f)
{ {
Core::DisplayMessage("State not found", 2000); u8 *ptr = &buffer[0];
return; PointerWrap p(&ptr, PointerWrap::MODE_VERIFY);
DoState(p);
if (p.GetMode() == PointerWrap::MODE_READ)
Core::DisplayMessage(StringFromFormat("Verified state at %s", g_current_filename.c_str()).c_str(), 2000);
else
Core::DisplayMessage("Unable to Verify : Can't verify state from other revisions !", 4000);
} }
u8 *buffer = NULL;
state_header header;
size_t sz;
f.ReadArray(&header, 1);
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);
return;
}
sz = header.sz;
bCompressedState = (sz != 0);
if (bCompressedState)
{
Core::DisplayMessage("Decompressing State...", 500);
lzo_uint i = 0;
buffer = new u8[sz];
if (!buffer) {
PanicAlertT("Error allocating buffer");
return;
}
while (true)
{
lzo_uint cur_len = 0;
lzo_uint new_len = 0;
if (!f.ReadArray(&cur_len, 1))
break;
f.ReadBytes(out, cur_len);
int res = lzo1x_decompress(out, cur_len, (buffer + i), &new_len, NULL);
if (res != LZO_E_OK)
{
// This doesn't seem to happen anymore.
PanicAlertT("Internal LZO Error - decompression failed (%d) (%ld, %ld) \n"
"Try verifying the state again", res, i, new_len);
delete [] buffer;
return;
}
// The size of the data to read to our buffer is 'new_len'
i += new_len;
}
}
else
{
sz = (int)(f.GetSize() - sizeof(int));
buffer = new u8[sz];
if (!f.ReadBytes(buffer, sz))
PanicAlert("wtf? failed to read bytes: %lu", (unsigned long)sz);
}
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;
} }
void State_Init() void Init()
{ {
ev_Load = CoreTiming::RegisterEvent("LoadState", &LoadStateCallback); ev_FileLoad = CoreTiming::RegisterEvent("LoadState", &LoadFileStateCallback);
ev_Save = CoreTiming::RegisterEvent("SaveState", &SaveStateCallback); ev_FileSave = CoreTiming::RegisterEvent("SaveState", &SaveFileStateCallback);
ev_Verify = CoreTiming::RegisterEvent("VerifyState", &VerifyStateCallback); ev_FileVerify = CoreTiming::RegisterEvent("VerifyState", &VerifyFileStateCallback);
ev_BufferLoad = CoreTiming::RegisterEvent("LoadBufferState", &LoadBufferStateCallback); ev_BufferLoad = CoreTiming::RegisterEvent("LoadBufferState", &LoadBufferStateCallback);
ev_BufferSave = CoreTiming::RegisterEvent("SaveBufferState", &SaveBufferStateCallback); ev_BufferSave = CoreTiming::RegisterEvent("SaveBufferState", &SaveBufferStateCallback);
ev_BufferVerify = CoreTiming::RegisterEvent("VerifyBufferState", &VerifyBufferStateCallback); ev_BufferVerify = CoreTiming::RegisterEvent("VerifyBufferState", &VerifyBufferStateCallback);
@ -493,126 +388,114 @@ void State_Init()
PanicAlertT("Internal LZO Error - lzo_init() failed"); PanicAlertT("Internal LZO Error - lzo_init() failed");
} }
void State_Shutdown() void Shutdown()
{ {
State_Flush(); Flush();
if (undoLoad) g_current_buffer.swap(std::vector<u8>());
{ g_undo_load_buffer.swap(std::vector<u8>());
delete[] undoLoad;
undoLoad = NULL;
}
} }
static std::string MakeStateFilename(int state_number) static std::string MakeStateFilename(int number)
{ {
return StringFromFormat("%s%s.s%02i", File::GetUserPath(D_STATESAVES_IDX).c_str(), SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), state_number); return StringFromFormat("%s%s.s%02i", File::GetUserPath(D_STATESAVES_IDX).c_str(),
SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), number);
} }
void State_SaveAs(const std::string &filename) void ScheduleFileEvent(const std::string &filename, int ev)
{ {
if (state_op_in_progress) if (g_op_in_progress)
return; return;
state_op_in_progress = true; g_op_in_progress = true;
cur_filename = filename;
lastFilename = filename; g_current_filename = filename;
CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev_Save); CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev);
} }
void State_Save(int slot) void SaveAs(const std::string &filename)
{ {
State_SaveAs(MakeStateFilename(slot)); g_last_filename = filename;
ScheduleFileEvent(filename, ev_FileSave);
} }
void State_LoadAs(const std::string &filename) void LoadAs(const std::string &filename)
{ {
if (state_op_in_progress) ScheduleFileEvent(filename, ev_FileLoad);
return;
state_op_in_progress = true;
cur_filename = filename;
CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev_Load);
} }
void State_Load(int slot) void VerifyAt(const std::string &filename)
{ {
State_LoadAs(MakeStateFilename(slot)); ScheduleFileEvent(filename, ev_FileVerify);
} }
void State_VerifyAt(const std::string &filename) void Save(int slot)
{ {
if (state_op_in_progress) SaveAs(MakeStateFilename(slot));
return;
state_op_in_progress = true;
cur_filename = filename;
CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev_Verify);
} }
void State_Verify(int slot) void Load(int slot)
{ {
State_VerifyAt(MakeStateFilename(slot)); LoadAs(MakeStateFilename(slot));
} }
void State_LoadLastSaved() void Verify(int slot)
{ {
if (lastFilename.empty()) VerifyAt(MakeStateFilename(slot));
}
void LoadLastSaved()
{
if (g_last_filename.empty())
Core::DisplayMessage("There is no last saved state", 2000); Core::DisplayMessage("There is no last saved state", 2000);
else else
State_LoadAs(lastFilename); LoadAs(g_last_filename);
} }
void State_LoadFromBuffer(u8 **buffer) void ScheduleBufferEvent(std::vector<u8>& buffer, int ev)
{ {
if (state_op_in_progress) if (g_op_in_progress)
return; return;
state_op_in_progress = true; g_op_in_progress = true;
cur_buffer = buffer;
CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev_BufferLoad); g_current_buffer.swap(buffer);
CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev);
} }
void State_SaveToBuffer(u8 **buffer) void SaveToBuffer(std::vector<u8>& buffer)
{ {
if (state_op_in_progress) ScheduleBufferEvent(buffer, ev_BufferSave);
return;
state_op_in_progress = true;
cur_buffer = buffer;
CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev_BufferSave);
} }
void State_VerifyBuffer(u8 **buffer) void LoadFromBuffer(std::vector<u8>& buffer)
{ {
if (state_op_in_progress) ScheduleBufferEvent(buffer, ev_BufferLoad);
return;
state_op_in_progress = true;
cur_buffer = buffer;
CoreTiming::ScheduleEvent_Threadsafe_Immediate(ev_BufferVerify);
} }
void State_Flush() void VerifyBuffer(std::vector<u8>& buffer)
{
ScheduleBufferEvent(buffer, ev_BufferVerify);
}
void Flush()
{ {
// If already saving state, wait for it to finish // If already saving state, wait for it to finish
if (saveThread.joinable()) if (g_save_thread.joinable())
{ g_save_thread.join();
saveThread.join();
}
} }
// Load the last state before loading the state // Load the last state before loading the state
void State_UndoLoadState() void UndoLoadState()
{ {
State_LoadFromBuffer(&undoLoad); if (!g_undo_load_buffer.empty())
LoadFromBuffer(g_undo_load_buffer);
else
PanicAlert("There is nothing to undo!");
} }
// Load the state that the last save state overwritten on // Load the state that the last save state overwritten on
void State_UndoSaveState() void UndoSaveState()
{ {
State_LoadAs((File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav").c_str()); LoadAs((File::GetUserPath(D_STATESAVES_IDX) + "lastState.sav").c_str());
} }
size_t State_GetSize() } // namespace State
{
// Measure the size of the buffer.
u8 *ptr = 0;
PointerWrap p(&ptr, PointerWrap::MODE_MEASURE);
DoState(p);
return (size_t)ptr;
}

View File

@ -22,44 +22,37 @@
#include <string> #include <string>
typedef struct namespace State
{ {
u8 *buffer;
size_t size;
} saveStruct;
void State_Init(); void Init();
void State_Shutdown(); void Shutdown();
void EnableCompression(bool compression);
// These don't happen instantly - they get scheduled as events. // These don't happen instantly - they get scheduled as events.
// ...But only if we're not in the main cpu thread. // ...But only if we're not in the main cpu thread.
// If we're in the main cpu thread then they run immediately instead // If we're in the main cpu thread then they run immediately instead
// because some things (like Lua) need them to run immediately. // because some things (like Lua) need them to run immediately.
// Slots from 0-99. // Slots from 0-99.
void State_Save(int slot); void Save(int slot);
void State_Load(int slot); void Load(int slot);
void State_Verify(int slot); void Verify(int slot);
void State_SaveAs(const std::string &filename); void SaveAs(const std::string &filename);
void State_LoadAs(const std::string &filename); void LoadAs(const std::string &filename);
void State_VerifyAt(const std::string &filename); void VerifyAt(const std::string &filename);
void State_LoadFromBuffer(u8 **buffer); void SaveToBuffer(u8 **buffer);
void State_SaveToBuffer(u8 **buffer); void LoadFromBuffer(u8 **buffer);
void State_VerifyBuffer(u8 **buffer); void VerifyBuffer(u8 **buffer);
void State_LoadLastSaved(); void LoadLastSaved();
void State_UndoSaveState(); void UndoSaveState();
void State_UndoLoadState(); void UndoLoadState();
size_t State_GetSize(); void Flush(); // wait until previously scheduled savestate event (if any) is done
void State_Flush(); // wait until previously scheduled savestate event (if any) is done
}
typedef struct
{
u8 gameID[6];
size_t sz;
} state_header;
#endif #endif

View File

@ -885,20 +885,20 @@ void CFrame::OnKeyDown(wxKeyEvent& event)
{ {
int slot_number = event.GetKeyCode() - WXK_F1 + 1; int slot_number = event.GetKeyCode() - WXK_F1 + 1;
if (event.GetModifiers() == wxMOD_NONE) if (event.GetModifiers() == wxMOD_NONE)
State_Load(slot_number); State::Load(slot_number);
else if (event.GetModifiers() == wxMOD_SHIFT) else if (event.GetModifiers() == wxMOD_SHIFT)
State_Save(slot_number); State::Save(slot_number);
else else
event.Skip(); event.Skip();
}*/ }*/
else if (event.GetKeyCode() == WXK_F11 && event.GetModifiers() == wxMOD_NONE) else if (event.GetKeyCode() == WXK_F11 && event.GetModifiers() == wxMOD_NONE)
State_LoadLastSaved(); State::LoadLastSaved();
else if (event.GetKeyCode() == WXK_F12) else if (event.GetKeyCode() == WXK_F12)
{ {
if (event.GetModifiers() == wxMOD_NONE) if (event.GetModifiers() == wxMOD_NONE)
State_UndoSaveState(); State::UndoSaveState();
else if (event.GetModifiers() == wxMOD_SHIFT) else if (event.GetModifiers() == wxMOD_SHIFT)
State_UndoLoadState(); State::UndoLoadState();
else else
event.Skip(); event.Skip();
} }

View File

@ -1401,8 +1401,8 @@ void CFrame::OnLoadStateFromFile(wxCommandEvent& WXUNUSED (event))
wxFD_OPEN | wxFD_PREVIEW | wxFD_FILE_MUST_EXIST, wxFD_OPEN | wxFD_PREVIEW | wxFD_FILE_MUST_EXIST,
this); this);
if(!path.IsEmpty()) if (!path.IsEmpty())
State_LoadAs((const char*)path.mb_str()); State::LoadAs((const char*)path.mb_str());
} }
void CFrame::OnSaveStateToFile(wxCommandEvent& WXUNUSED (event)) void CFrame::OnSaveStateToFile(wxCommandEvent& WXUNUSED (event))
@ -1415,23 +1415,23 @@ void CFrame::OnSaveStateToFile(wxCommandEvent& WXUNUSED (event))
wxFD_SAVE, wxFD_SAVE,
this); this);
if(! path.IsEmpty()) if (!path.IsEmpty())
State_SaveAs((const char*)path.mb_str()); State::SaveAs((const char*)path.mb_str());
} }
void CFrame::OnLoadLastState(wxCommandEvent& WXUNUSED (event)) void CFrame::OnLoadLastState(wxCommandEvent& WXUNUSED (event))
{ {
State_LoadLastSaved(); State::LoadLastSaved();
} }
void CFrame::OnUndoLoadState(wxCommandEvent& WXUNUSED (event)) void CFrame::OnUndoLoadState(wxCommandEvent& WXUNUSED (event))
{ {
State_UndoLoadState(); State::UndoLoadState();
} }
void CFrame::OnUndoSaveState(wxCommandEvent& WXUNUSED (event)) void CFrame::OnUndoSaveState(wxCommandEvent& WXUNUSED (event))
{ {
State_UndoSaveState(); State::UndoSaveState();
} }
@ -1439,14 +1439,14 @@ void CFrame::OnLoadState(wxCommandEvent& event)
{ {
int id = event.GetId(); int id = event.GetId();
int slot = id - IDM_LOADSLOT1 + 1; int slot = id - IDM_LOADSLOT1 + 1;
State_Load(slot); State::Load(slot);
} }
void CFrame::OnSaveState(wxCommandEvent& event) void CFrame::OnSaveState(wxCommandEvent& event)
{ {
int id = event.GetId(); int id = event.GetId();
int slot = id - IDM_SAVESLOT1 + 1; int slot = id - IDM_SAVESLOT1 + 1;
State_Save(slot); State::Save(slot);
} }
void CFrame::OnFrameSkip(wxCommandEvent& event) void CFrame::OnFrameSkip(wxCommandEvent& event)

View File

@ -214,20 +214,20 @@ void X11_MainLoop()
{ {
int slot_number = key - XK_F1 + 1; int slot_number = key - XK_F1 + 1;
if (event.xkey.state & ShiftMask) if (event.xkey.state & ShiftMask)
State_Save(slot_number); State::Save(slot_number);
else else
State_Load(slot_number); State::Load(slot_number);
} }
else if (key == XK_F9) else if (key == XK_F9)
Core::SaveScreenShot(); Core::SaveScreenShot();
else if (key == XK_F11) else if (key == XK_F11)
State_LoadLastSaved(); State::LoadLastSaved();
else if (key == XK_F12) else if (key == XK_F12)
{ {
if (event.xkey.state & ShiftMask) if (event.xkey.state & ShiftMask)
State_UndoLoadState(); State::UndoLoadState();
else else
State_UndoSaveState(); State::UndoSaveState();
} }
break; break;
case FocusIn: case FocusIn:

View File

@ -270,7 +270,6 @@ VideoConfigDiag::VideoConfigDiag(wxWindow* parent, const std::string &title, con
// - EFB // - EFB
// EFB scale // EFB scale
wxBoxSizer* const efb_scale_szr = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer* const efb_scale_szr = new wxBoxSizer(wxHORIZONTAL);
// TODO: give this a label (?)
const wxString efbscale_choices[] = { _("Fractional"), _("Integral [recommended]"), const wxString efbscale_choices[] = { _("Fractional"), _("Integral [recommended]"),
wxT("1x"), wxT("2x"), wxT("3x"), wxT("0.75x"), wxT("0.5x"), wxT("0.375x") }; wxT("1x"), wxT("2x"), wxT("3x"), wxT("0.75x"), wxT("0.5x"), wxT("0.375x") };

View File

@ -171,7 +171,6 @@ bool VideoBackend::Initialize(void *&window_handle)
return false; return false;
} }
OSD::AddMessage("Dolphin Direct3D11 Video Backend.", 5000);
s_BackendInitialized = true; s_BackendInitialized = true;
return true; return true;

View File

@ -156,7 +156,6 @@ bool VideoBackend::Initialize(void *&window_handle)
return false; return false;
} }
OSD::AddMessage("Dolphin Direct3D9 Video Backend.", 5000);
s_BackendInitialized = true; s_BackendInitialized = true;
return true; return true;

View File

@ -173,7 +173,6 @@ bool VideoBackend::Initialize(void *&window_handle)
if (!OpenGL_Create(window_handle)) if (!OpenGL_Create(window_handle))
return false; return false;
OSD::AddMessage("Dolphin OpenGL Video Backend.", 5000);
s_BackendInitialized = true; s_BackendInitialized = true;
return true; return true;

View File

@ -178,17 +178,12 @@ void VideoSoftware::Video_EnterLoop()
Common::YieldCPU(); Common::YieldCPU();
} }
if (!emuRunningState) while (!emuRunningState && fifoStateRun)
{ {
while (!emuRunningState && fifoStateRun) g_video_backend->PeekMessages();
{ Common::SleepCurrentThread(1);
g_video_backend->PeekMessages();
Common::SleepCurrentThread(1);
}
} }
} }
} }
void VideoSoftware::Video_ExitLoop() void VideoSoftware::Video_ExitLoop()