mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 14:19:46 -06:00
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:
@ -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();
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
|
||||||
{
|
|
||||||
// if we got passed an empty buffer,
|
|
||||||
// allocate it with new[]
|
|
||||||
// (and the caller is responsible for delete[]ing it later)
|
|
||||||
DoState(p);
|
DoState(p);
|
||||||
size_t sz = (size_t)ptr;
|
const size_t buffer_size = reinterpret_cast<size_t>(ptr);
|
||||||
*cur_buffer = new u8[sz];
|
g_current_buffer.resize(buffer_size);
|
||||||
}
|
|
||||||
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)
|
|
||||||
|
if (0 != header.size) // non-zero header size means the state is compressed
|
||||||
|
{
|
||||||
|
lzo_uint i = 0;
|
||||||
|
while (true)
|
||||||
{
|
{
|
||||||
lzo_uint cur_len = 0;
|
lzo_uint cur_len = 0;
|
||||||
lzo_uint i = 0;
|
lzo_uint out_len = 0;
|
||||||
|
|
||||||
for (;;)
|
if ((i + IN_LEN) >= buffer_size)
|
||||||
{
|
cur_len = buffer_size - i;
|
||||||
if ((i + IN_LEN) >= sz)
|
|
||||||
cur_len = sz - 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)
|
||||||
|
{
|
||||||
|
// Stop the core while we load the state
|
||||||
|
CCPU::EnableStepping(true);
|
||||||
|
|
||||||
|
// Wait for the other threaded sub-systems to stop too
|
||||||
|
// TODO: uglyyy
|
||||||
|
SLEEP(100);
|
||||||
|
|
||||||
|
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);
|
PointerWrap p(&ptr, PointerWrap::MODE_READ);
|
||||||
DoState(p);
|
DoState(p);
|
||||||
|
|
||||||
if (p.GetMode() == PointerWrap::MODE_READ)
|
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||||
Core::DisplayMessage(StringFromFormat("Loaded state from %s", cur_filename.c_str()).c_str(), 2000);
|
Core::DisplayMessage(StringFromFormat("Loaded state from %s", g_current_filename.c_str()).c_str(), 2000);
|
||||||
else
|
else
|
||||||
Core::DisplayMessage("Unable to Load : Can't load state from other revisions !", 4000);
|
Core::DisplayMessage("Unable to Load : Can't load state from other revisions !", 4000);
|
||||||
|
|
||||||
delete[] buffer;
|
if (File::Exists(g_current_filename + ".dtm"))
|
||||||
|
Frame::LoadInput((g_current_filename + ".dtm").c_str());
|
||||||
if (File::Exists(cur_filename + ".dtm"))
|
|
||||||
Frame::LoadInput((cur_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
PointerWrap p(&ptr, PointerWrap::MODE_VERIFY);
|
||||||
DoState(p);
|
DoState(p);
|
||||||
|
|
||||||
if (p.GetMode() == PointerWrap::MODE_READ)
|
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||||
Core::DisplayMessage(StringFromFormat("Verified state at %s", cur_filename.c_str()).c_str(), 2000);
|
Core::DisplayMessage(StringFromFormat("Verified state at %s", g_current_filename.c_str()).c_str(), 2000);
|
||||||
else
|
else
|
||||||
Core::DisplayMessage("Unable to Verify : Can't verify state from other revisions !", 4000);
|
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;
|
|
||||||
}
|
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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:
|
||||||
|
@ -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") };
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -178,17 +178,12 @@ void VideoSoftware::Video_EnterLoop()
|
|||||||
Common::YieldCPU();
|
Common::YieldCPU();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!emuRunningState)
|
|
||||||
{
|
|
||||||
while (!emuRunningState && fifoStateRun)
|
while (!emuRunningState && fifoStateRun)
|
||||||
{
|
{
|
||||||
g_video_backend->PeekMessages();
|
g_video_backend->PeekMessages();
|
||||||
Common::SleepCurrentThread(1);
|
Common::SleepCurrentThread(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoSoftware::Video_ExitLoop()
|
void VideoSoftware::Video_ExitLoop()
|
||||||
|
Reference in New Issue
Block a user