Improve WII state saves by saving /tmp and IPC request/reply queues
mp3:c stores temporary files in /tmp and needs them restored to resume properly. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@7564 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
parent
5b5e8edb17
commit
3bcb406c46
|
@ -29,6 +29,7 @@
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <deque>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
|
@ -127,6 +128,18 @@ public:
|
||||||
DoArray(&x[0], vec_size);
|
DoArray(&x[0], vec_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store deques.
|
||||||
|
template<class T>
|
||||||
|
void Do(std::deque<T> &x)
|
||||||
|
{
|
||||||
|
u32 deq_size = (u32)x.size();
|
||||||
|
Do(deq_size);
|
||||||
|
x.resize(deq_size);
|
||||||
|
u32 i;
|
||||||
|
for(i = 0; i < deq_size; i++)
|
||||||
|
DoVoid(&x[i],sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
// Store strings.
|
// Store strings.
|
||||||
void Do(std::string &x)
|
void Do(std::string &x)
|
||||||
{
|
{
|
||||||
|
|
|
@ -71,7 +71,7 @@ typedef std::map<u32, std::string> TFileNameMap;
|
||||||
TFileNameMap g_FileNameMap;
|
TFileNameMap g_FileNameMap;
|
||||||
u32 g_LastDeviceID;
|
u32 g_LastDeviceID;
|
||||||
|
|
||||||
typedef std::queue<u32> ipc_msg_queue;
|
typedef std::deque<u32> ipc_msg_queue;
|
||||||
static ipc_msg_queue request_queue; // ppc -> arm
|
static ipc_msg_queue request_queue; // ppc -> arm
|
||||||
static ipc_msg_queue reply_queue; // arm -> ppc
|
static ipc_msg_queue reply_queue; // arm -> ppc
|
||||||
|
|
||||||
|
@ -120,8 +120,8 @@ void Reset(bool _bHard)
|
||||||
g_DeviceMap.erase(itr, g_DeviceMap.end());
|
g_DeviceMap.erase(itr, g_DeviceMap.end());
|
||||||
g_FileNameMap.clear();
|
g_FileNameMap.clear();
|
||||||
|
|
||||||
request_queue = std::queue<u32>();
|
request_queue.clear();
|
||||||
reply_queue = std::queue<u32>();
|
reply_queue.clear();
|
||||||
|
|
||||||
g_LastDeviceID = IPC_FIRST_FILEIO_ID;
|
g_LastDeviceID = IPC_FIRST_FILEIO_ID;
|
||||||
}
|
}
|
||||||
|
@ -205,23 +205,20 @@ void DoState(PointerWrap &p)
|
||||||
{
|
{
|
||||||
p.Do(g_LastDeviceID);
|
p.Do(g_LastDeviceID);
|
||||||
|
|
||||||
// Currently only USB device needs to be saved
|
|
||||||
IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(GetDeviceIDByName(std::string("/dev/usb/oh1/57e/305")));
|
|
||||||
if (pDevice)
|
|
||||||
pDevice->DoState(p);
|
|
||||||
else
|
|
||||||
PanicAlert("WII_IPC_HLE: Save/Load State failed, Device /dev/usb/oh1/57e/305 doesn't exist!");
|
|
||||||
|
|
||||||
TFileNameMap::const_iterator itr;
|
|
||||||
if (p.GetMode() == PointerWrap::MODE_READ)
|
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||||
{
|
{
|
||||||
|
TFileNameMap::const_iterator itr;
|
||||||
// Delete file Handles
|
// Delete file Handles
|
||||||
itr = g_FileNameMap.begin();
|
itr = g_FileNameMap.begin();
|
||||||
while (itr != g_FileNameMap.end())
|
while (itr != g_FileNameMap.end())
|
||||||
{
|
{
|
||||||
if (g_DeviceMap[itr->first])
|
TDeviceMap::const_iterator devitr = g_DeviceMap.find(itr->first);
|
||||||
delete g_DeviceMap[itr->first];
|
if (devitr != g_DeviceMap.end())
|
||||||
g_DeviceMap.erase(itr->first);
|
{
|
||||||
|
if (devitr->second)
|
||||||
|
delete devitr->second;
|
||||||
|
g_DeviceMap.erase(itr->first);
|
||||||
|
}
|
||||||
++itr;
|
++itr;
|
||||||
}
|
}
|
||||||
// Load file names
|
// Load file names
|
||||||
|
@ -239,10 +236,26 @@ void DoState(PointerWrap &p)
|
||||||
p.Do(g_FileNameMap);
|
p.Do(g_FileNameMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
itr = g_FileNameMap.begin();
|
p.Do(request_queue);
|
||||||
while (itr != g_FileNameMap.end())
|
p.Do(reply_queue);
|
||||||
|
|
||||||
|
|
||||||
|
TDeviceMap::const_iterator itr;
|
||||||
|
//first, all the real devices
|
||||||
|
//(because we need fs to be deserialized first)
|
||||||
|
itr = g_DeviceMap.begin();
|
||||||
|
while (itr != g_DeviceMap.end())
|
||||||
{
|
{
|
||||||
g_DeviceMap[itr->first]->DoState(p);
|
if (itr->second->IsHardware())
|
||||||
|
itr->second->DoState(p);
|
||||||
|
++itr;
|
||||||
|
}
|
||||||
|
//then all the files
|
||||||
|
itr = g_DeviceMap.begin();
|
||||||
|
while (itr != g_DeviceMap.end())
|
||||||
|
{
|
||||||
|
if (!itr->second->IsHardware())
|
||||||
|
itr->second->DoState(p);
|
||||||
++itr;
|
++itr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -393,13 +406,13 @@ void ExecuteCommand(u32 _Address)
|
||||||
// Happens AS SOON AS IPC gets a new pointer!
|
// Happens AS SOON AS IPC gets a new pointer!
|
||||||
void EnqRequest(u32 _Address)
|
void EnqRequest(u32 _Address)
|
||||||
{
|
{
|
||||||
request_queue.push(_Address);
|
request_queue.push_back(_Address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when IOS module has some reply
|
// Called when IOS module has some reply
|
||||||
void EnqReply(u32 _Address)
|
void EnqReply(u32 _Address)
|
||||||
{
|
{
|
||||||
reply_queue.push(_Address);
|
reply_queue.push_back(_Address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is called every IPC_HLE_PERIOD from SystemTimers.cpp
|
// This is called every IPC_HLE_PERIOD from SystemTimers.cpp
|
||||||
|
@ -417,7 +430,7 @@ void Update()
|
||||||
INFO_LOG(WII_IPC_HLE, "||-- Acknowledge IPC Request @ 0x%08x", request_queue.front());
|
INFO_LOG(WII_IPC_HLE, "||-- Acknowledge IPC Request @ 0x%08x", request_queue.front());
|
||||||
|
|
||||||
ExecuteCommand(request_queue.front());
|
ExecuteCommand(request_queue.front());
|
||||||
request_queue.pop();
|
request_queue.pop_front();
|
||||||
|
|
||||||
#if MAX_LOGLEVEL >= DEBUG_LEVEL
|
#if MAX_LOGLEVEL >= DEBUG_LEVEL
|
||||||
Dolphin_Debugger::PrintCallstack(LogTypes::WII_IPC_HLE, LogTypes::LDEBUG);
|
Dolphin_Debugger::PrintCallstack(LogTypes::WII_IPC_HLE, LogTypes::LDEBUG);
|
||||||
|
@ -428,7 +441,7 @@ void Update()
|
||||||
{
|
{
|
||||||
WII_IPCInterface::GenerateReply(reply_queue.front());
|
WII_IPCInterface::GenerateReply(reply_queue.front());
|
||||||
INFO_LOG(WII_IPC_HLE, "<<-- Reply to IPC Request @ 0x%08x", reply_queue.front());
|
INFO_LOG(WII_IPC_HLE, "<<-- Reply to IPC Request @ 0x%08x", reply_queue.front());
|
||||||
reply_queue.pop();
|
reply_queue.pop_front();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,7 +87,6 @@ CWII_IPC_HLE_Device_FileIO::CWII_IPC_HLE_Device_FileIO(u32 _DeviceID, const std:
|
||||||
, m_pFileHandle(NULL)
|
, m_pFileHandle(NULL)
|
||||||
, m_FileLength(0)
|
, m_FileLength(0)
|
||||||
, m_Mode(0)
|
, m_Mode(0)
|
||||||
, m_Seek(0)
|
|
||||||
{
|
{
|
||||||
Common::ReadReplacements(replacements);
|
Common::ReadReplacements(replacements);
|
||||||
}
|
}
|
||||||
|
@ -104,10 +103,9 @@ bool CWII_IPC_HLE_Device_FileIO::Close(u32 _CommandAddress, bool _bForce)
|
||||||
|
|
||||||
m_FileLength = 0;
|
m_FileLength = 0;
|
||||||
m_Mode = 0;
|
m_Mode = 0;
|
||||||
m_Seek = 0;
|
|
||||||
|
|
||||||
// Close always return 0 for success
|
// Close always return 0 for success
|
||||||
if (!_bForce)
|
if (_CommandAddress && !_bForce)
|
||||||
Memory::Write_U32(0, _CommandAddress + 4);
|
Memory::Write_U32(0, _CommandAddress + 4);
|
||||||
m_Active = false;
|
m_Active = false;
|
||||||
return true;
|
return true;
|
||||||
|
@ -121,7 +119,7 @@ bool CWII_IPC_HLE_Device_FileIO::Open(u32 _CommandAddress, u32 _Mode)
|
||||||
// close the file handle if we get a reopen
|
// close the file handle if we get a reopen
|
||||||
m_pFileHandle.Close();
|
m_pFileHandle.Close();
|
||||||
|
|
||||||
const char* const Modes[] =
|
static const char* const Modes[] =
|
||||||
{
|
{
|
||||||
"Unk Mode",
|
"Unk Mode",
|
||||||
"Read only",
|
"Read only",
|
||||||
|
@ -326,20 +324,24 @@ bool CWII_IPC_HLE_Device_FileIO::IOCtl(u32 _CommandAddress)
|
||||||
|
|
||||||
void CWII_IPC_HLE_Device_FileIO::DoState(PointerWrap &p)
|
void CWII_IPC_HLE_Device_FileIO::DoState(PointerWrap &p)
|
||||||
{
|
{
|
||||||
if (p.GetMode() == PointerWrap::MODE_WRITE)
|
bool have_file_handle = m_pFileHandle;
|
||||||
{
|
s32 seek = (have_file_handle) ? (s32)m_pFileHandle.Tell() : 0;
|
||||||
m_Seek = (m_pFileHandle) ? (s32)m_pFileHandle.Tell() : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
p.Do(have_file_handle);
|
||||||
p.Do(m_Mode);
|
p.Do(m_Mode);
|
||||||
p.Do(m_Seek);
|
p.Do(seek);
|
||||||
|
|
||||||
if (p.GetMode() == PointerWrap::MODE_READ)
|
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||||
{
|
{
|
||||||
if (m_Mode)
|
if (have_file_handle)
|
||||||
{
|
{
|
||||||
Open(0, m_Mode);
|
Open(0, m_Mode);
|
||||||
m_pFileHandle.Seek(m_Seek, SEEK_SET);
|
_dbg_assert_msg_(WII_IPC_HLE, m_pFileHandle, "bad filehandle");
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
Close(0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (have_file_handle)
|
||||||
|
m_pFileHandle.Seek(seek, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,7 +76,6 @@ private:
|
||||||
File::IOFile m_pFileHandle;
|
File::IOFile m_pFileHandle;
|
||||||
u32 m_FileLength;
|
u32 m_FileLength;
|
||||||
u32 m_Mode;
|
u32 m_Mode;
|
||||||
s32 m_Seek;
|
|
||||||
|
|
||||||
std::string m_Filename;
|
std::string m_Filename;
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "FileSearch.h"
|
#include "FileSearch.h"
|
||||||
#include "FileUtil.h"
|
#include "FileUtil.h"
|
||||||
#include "NandPaths.h"
|
#include "NandPaths.h"
|
||||||
|
#include "ChunkFile.h"
|
||||||
|
|
||||||
#include "../VolumeHandler.h"
|
#include "../VolumeHandler.h"
|
||||||
|
|
||||||
|
@ -484,3 +485,96 @@ s32 CWII_IPC_HLE_Device_fs::ExecuteCommand(u32 _Parameter, u32 _BufferIn, u32 _B
|
||||||
|
|
||||||
return FS_RESULT_FATAL;
|
return FS_RESULT_FATAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CWII_IPC_HLE_Device_fs::DoState(PointerWrap& p)
|
||||||
|
{
|
||||||
|
// handle /tmp
|
||||||
|
|
||||||
|
std::string Path = File::GetUserPath(D_WIIUSER_IDX) + "tmp";
|
||||||
|
if (p.GetMode() == PointerWrap::MODE_READ)
|
||||||
|
{
|
||||||
|
File::DeleteDirRecursively(Path);
|
||||||
|
File::CreateDir(Path.c_str());
|
||||||
|
|
||||||
|
//now restore from the stream
|
||||||
|
while(1) {
|
||||||
|
char type;
|
||||||
|
p.Do(type);
|
||||||
|
if (!type)
|
||||||
|
break;
|
||||||
|
std::string filename;
|
||||||
|
p.Do(filename);
|
||||||
|
std::string name = Path + DIR_SEP + filename;
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case 'd':
|
||||||
|
{
|
||||||
|
File::CreateDir(name.c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'f':
|
||||||
|
{
|
||||||
|
u32 size;
|
||||||
|
p.Do(size);
|
||||||
|
|
||||||
|
File::IOFile handle(name, "wb");
|
||||||
|
char buf[65536];
|
||||||
|
u32 count = size;
|
||||||
|
while(count > 65536) {
|
||||||
|
p.DoArray(&buf[0], 65536);
|
||||||
|
handle.WriteArray(&buf[0], 65536);
|
||||||
|
count -= 65536;
|
||||||
|
}
|
||||||
|
p.DoArray(&buf[0], count);
|
||||||
|
handle.WriteArray(&buf[0], count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//recurse through tmp and save dirs and files
|
||||||
|
|
||||||
|
File::FSTEntry parentEntry;
|
||||||
|
File::ScanDirectoryTree(Path, parentEntry);
|
||||||
|
std::deque<File::FSTEntry> todo;
|
||||||
|
todo.insert(todo.end(), parentEntry.children.begin(),
|
||||||
|
parentEntry.children.end());
|
||||||
|
|
||||||
|
while(!todo.empty())
|
||||||
|
{
|
||||||
|
File::FSTEntry &entry = todo.front();
|
||||||
|
std::string name = entry.physicalName;
|
||||||
|
name.erase(0,Path.length()+1);
|
||||||
|
char type = entry.isDirectory?'d':'f';
|
||||||
|
p.Do(type);
|
||||||
|
p.Do(name);
|
||||||
|
if (entry.isDirectory)
|
||||||
|
{
|
||||||
|
todo.insert(todo.end(), entry.children.begin(),
|
||||||
|
entry.children.end());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
u32 size = entry.size;
|
||||||
|
p.Do(size);
|
||||||
|
|
||||||
|
File::IOFile handle(entry.physicalName, "rb");
|
||||||
|
char buf[65536];
|
||||||
|
u32 count = size;
|
||||||
|
while(count > 65536) {
|
||||||
|
handle.ReadArray(&buf[0], 65536);
|
||||||
|
p.DoArray(&buf[0], 65536);
|
||||||
|
count -= 65536;
|
||||||
|
}
|
||||||
|
handle.ReadArray(&buf[0], count);
|
||||||
|
p.DoArray(&buf[0], count);
|
||||||
|
}
|
||||||
|
todo.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
char type = 0;
|
||||||
|
p.Do(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -46,7 +46,9 @@ public:
|
||||||
CWII_IPC_HLE_Device_fs(u32 _DeviceID, const std::string& _rDeviceName);
|
CWII_IPC_HLE_Device_fs(u32 _DeviceID, const std::string& _rDeviceName);
|
||||||
virtual ~CWII_IPC_HLE_Device_fs();
|
virtual ~CWII_IPC_HLE_Device_fs();
|
||||||
|
|
||||||
virtual bool Open(u32 _CommandAddress, u32 _Mode);
|
virtual void DoState(PointerWrap& p);
|
||||||
|
|
||||||
|
virtual bool Open(u32 _CommandAddress, u32 _Mode);
|
||||||
virtual bool Close(u32 _CommandAddress, bool _bForce);
|
virtual bool Close(u32 _CommandAddress, bool _bForce);
|
||||||
|
|
||||||
virtual bool IOCtl(u32 _CommandAddress);
|
virtual bool IOCtl(u32 _CommandAddress);
|
||||||
|
|
|
@ -106,13 +106,26 @@ CWII_IPC_HLE_Device_usb_oh1_57e_305::~CWII_IPC_HLE_Device_usb_oh1_57e_305()
|
||||||
|
|
||||||
void CWII_IPC_HLE_Device_usb_oh1_57e_305::DoState(PointerWrap &p)
|
void CWII_IPC_HLE_Device_usb_oh1_57e_305::DoState(PointerWrap &p)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
//things that do not get saved:
|
||||||
|
|
||||||
|
std::vector<CWII_IPC_HLE_WiiMote> m_WiiMotes;
|
||||||
|
|
||||||
|
std::deque<SQueuedEvent> m_EventQueue;
|
||||||
|
*/
|
||||||
|
|
||||||
p.Do(m_CtrlSetup);
|
p.Do(m_CtrlSetup);
|
||||||
p.Do(m_ACLSetup);
|
p.Do(m_ACLSetup);
|
||||||
p.Do(m_HCIEndpoint);
|
p.Do(m_HCIEndpoint);
|
||||||
p.Do(m_ACLEndpoint);
|
p.Do(m_ACLEndpoint);
|
||||||
p.Do(m_last_ticks);
|
p.Do(m_last_ticks);
|
||||||
|
p.DoArray(m_PacketCount,4);
|
||||||
|
p.Do(m_ScanEnable);
|
||||||
m_acl_pool.DoState(p);
|
m_acl_pool.DoState(p);
|
||||||
|
|
||||||
|
if (p.GetMode() == PointerWrap::MODE_READ) {
|
||||||
|
m_EventQueue.clear();
|
||||||
|
}
|
||||||
if (p.GetMode() == PointerWrap::MODE_READ &&
|
if (p.GetMode() == PointerWrap::MODE_READ &&
|
||||||
SConfig::GetInstance().m_WiimoteReconnectOnLoad)
|
SConfig::GetInstance().m_WiimoteReconnectOnLoad)
|
||||||
{
|
{
|
||||||
|
@ -397,7 +410,7 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305::AddEventToQueue(const SQueuedEvent& _e
|
||||||
DEBUG_LOG(WII_IPC_WIIMOTE, "HCI endpoint not "
|
DEBUG_LOG(WII_IPC_WIIMOTE, "HCI endpoint not "
|
||||||
"currently valid, queueing(%lu)...",
|
"currently valid, queueing(%lu)...",
|
||||||
(unsigned long)m_EventQueue.size());
|
(unsigned long)m_EventQueue.size());
|
||||||
m_EventQueue.push(_event);
|
m_EventQueue.push_back(_event);
|
||||||
const SQueuedEvent& event = m_EventQueue.front();
|
const SQueuedEvent& event = m_EventQueue.front();
|
||||||
DEBUG_LOG(WII_IPC_WIIMOTE, "HCI event %x "
|
DEBUG_LOG(WII_IPC_WIIMOTE, "HCI event %x "
|
||||||
"being written from queue(%lu) to %08x...",
|
"being written from queue(%lu) to %08x...",
|
||||||
|
@ -409,14 +422,14 @@ void CWII_IPC_HLE_Device_usb_oh1_57e_305::AddEventToQueue(const SQueuedEvent& _e
|
||||||
// Send a reply to indicate HCI buffer is filled
|
// Send a reply to indicate HCI buffer is filled
|
||||||
WII_IPC_HLE_Interface::EnqReply(m_HCIEndpoint.m_address);
|
WII_IPC_HLE_Interface::EnqReply(m_HCIEndpoint.m_address);
|
||||||
m_HCIEndpoint.Invalidate();
|
m_HCIEndpoint.Invalidate();
|
||||||
m_EventQueue.pop();
|
m_EventQueue.pop_front();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DEBUG_LOG(WII_IPC_WIIMOTE, "HCI endpoint not currently valid, "
|
DEBUG_LOG(WII_IPC_WIIMOTE, "HCI endpoint not currently valid, "
|
||||||
"queueing(%lu)...", (unsigned long)m_EventQueue.size());
|
"queueing(%lu)...", (unsigned long)m_EventQueue.size());
|
||||||
m_EventQueue.push(_event);
|
m_EventQueue.push_back(_event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,7 +452,7 @@ u32 CWII_IPC_HLE_Device_usb_oh1_57e_305::Update()
|
||||||
// Send a reply to indicate HCI buffer is filled
|
// Send a reply to indicate HCI buffer is filled
|
||||||
WII_IPC_HLE_Interface::EnqReply(m_HCIEndpoint.m_address);
|
WII_IPC_HLE_Interface::EnqReply(m_HCIEndpoint.m_address);
|
||||||
m_HCIEndpoint.Invalidate();
|
m_HCIEndpoint.Invalidate();
|
||||||
m_EventQueue.pop();
|
m_EventQueue.pop_front();
|
||||||
packet_transferred = true;
|
packet_transferred = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -152,7 +152,7 @@ private:
|
||||||
|
|
||||||
SHCICommandMessage m_CtrlSetup;
|
SHCICommandMessage m_CtrlSetup;
|
||||||
CtrlBuffer m_HCIEndpoint;
|
CtrlBuffer m_HCIEndpoint;
|
||||||
std::queue<SQueuedEvent> m_EventQueue;
|
std::deque<SQueuedEvent> m_EventQueue;
|
||||||
|
|
||||||
u32 m_ACLSetup;
|
u32 m_ACLSetup;
|
||||||
CtrlBuffer m_ACLEndpoint;
|
CtrlBuffer m_ACLEndpoint;
|
||||||
|
|
|
@ -65,7 +65,7 @@ static std::vector<u8> g_current_buffer;
|
||||||
static std::thread g_save_thread;
|
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
|
||||||
static const int STATE_VERSION = 4;
|
static const int STATE_VERSION = 5;
|
||||||
|
|
||||||
struct StateHeader
|
struct StateHeader
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue