Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge remote-tracking branch 'John-Peterson/state'
  • Loading branch information
RachelBryk committed Apr 30, 2013
2 parents 76a316f + ef2e0a8 commit e5fdd30
Show file tree
Hide file tree
Showing 9 changed files with 258 additions and 152 deletions.
15 changes: 15 additions & 0 deletions Source/Core/Core/Src/ConfigManager.cpp
Expand Up @@ -35,6 +35,7 @@ static const struct {

{ "ToggleFullscreen", 70 /* 'F' */, 2 /* wxMOD_CMD */ },
{ "Screenshot", 83 /* 'S' */, 2 /* wxMOD_CMD */ },
{ "Exit", 0, 0 /* wxMOD_NONE */ },

{ "Wiimote1Connect", 49 /* '1' */, 2 /* wxMOD_CMD */ },
{ "Wiimote2Connect", 50 /* '2' */, 2 /* wxMOD_CMD */ },
Expand All @@ -57,6 +58,7 @@ static const struct {

{ "ToggleFullscreen", 13 /* WXK_RETURN */, 1 /* wxMOD_ALT */ },
{ "Screenshot", 348 /* WXK_F9 */, 0 /* wxMOD_NONE */ },
{ "Exit", 0, 0 /* wxMOD_NONE */ },

{ "Wiimote1Connect", 344 /* WXK_F5 */, 1 /* wxMOD_ALT */ },
{ "Wiimote2Connect", 345 /* WXK_F6 */, 1 /* wxMOD_ALT */ },
Expand All @@ -81,6 +83,19 @@ static const struct {
{ "SaveStateSlot6", 345 /* WXK_F6 */, 4 /* wxMOD_SHIFT */ },
{ "SaveStateSlot7", 346 /* WXK_F7 */, 4 /* wxMOD_SHIFT */ },
{ "SaveStateSlot8", 347 /* WXK_F8 */, 4 /* wxMOD_SHIFT */ },

{ "LoadLastState1", 0, 0 /* wxMOD_NONE */ },
{ "LoadLastState2", 0, 0 /* wxMOD_NONE */ },
{ "LoadLastState3", 0, 0 /* wxMOD_NONE */ },
{ "LoadLastState4", 0, 0 /* wxMOD_NONE */ },
{ "LoadLastState5", 0, 0 /* wxMOD_NONE */ },
{ "LoadLastState6", 0, 0 /* wxMOD_NONE */ },
{ "LoadLastState7", 0, 0 /* wxMOD_NONE */ },
{ "LoadLastState8", 0, 0 /* wxMOD_NONE */ },

{ "SaveFirstState", 0, 0 /* wxMOD_NONE */ },
{ "UndoLoadState", 351 /* WXK_F12 */, 0 /* wxMOD_NONE */ },
{ "UndoSaveState", 351 /* WXK_F12 */, 4 /* wxMOD_SHIFT */ },
};

SConfig::SConfig()
Expand Down
14 changes: 14 additions & 0 deletions Source/Core/Core/Src/CoreParameter.h
Expand Up @@ -26,6 +26,7 @@ enum Hotkey

HK_FULLSCREEN,
HK_SCREENSHOT,
HK_EXIT,

HK_WIIMOTE1_CONNECT,
HK_WIIMOTE2_CONNECT,
Expand All @@ -50,6 +51,19 @@ enum Hotkey
HK_SAVE_STATE_SLOT_7,
HK_SAVE_STATE_SLOT_8,

HK_LOAD_LAST_STATE_1,
HK_LOAD_LAST_STATE_2,
HK_LOAD_LAST_STATE_3,
HK_LOAD_LAST_STATE_4,
HK_LOAD_LAST_STATE_5,
HK_LOAD_LAST_STATE_6,
HK_LOAD_LAST_STATE_7,
HK_LOAD_LAST_STATE_8,

HK_SAVE_FIRST_STATE,
HK_UNDO_LOAD_STATE,
HK_UNDO_SAVE_STATE,

NUM_HOTKEYS,
};

Expand Down
108 changes: 93 additions & 15 deletions Source/Core/Core/Src/State.cpp
Expand Up @@ -3,6 +3,7 @@
// Refer to the license.txt file included.

#include "Common.h"
#include "Timer.h"
#include "State.h"
#include "Core.h"
#include "ConfigManager.h"
Expand Down Expand Up @@ -58,13 +59,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
static std::thread g_save_thread;

// Don't forget to increase this after doing changes on the savestate system
static const u32 STATE_VERSION = 16;

struct StateHeader
{
u8 gameID[6];
size_t size;
};
static const u32 STATE_VERSION = 17;

enum
{
Expand Down Expand Up @@ -159,17 +154,59 @@ void VerifyBuffer(std::vector<u8>& buffer)
Core::PauseAndLock(false, wasUnpaused);
}

// return state number not in map
int GetEmptySlot(std::map<double, int> m)
{
for (int i = 1; i <= NUM_STATES; i++)
{
bool found = false;
for (std::map<double, int>::iterator it = m.begin(); it != m.end(); it++)
{
if (it->second == i)
{
found = true;
break;
}
}
if (!found) return i;
}
return -1;
}

// read state timestamps
std::map<double, int> GetSavedStates()
{
StateHeader header;
std::map<double, int> m;
for (int i = 1; i <= NUM_STATES; i++)
{
if (File::Exists(MakeStateFilename(i)))
{
if (ReadHeader(MakeStateFilename(i), header))
{
double d = Common::Timer::GetDoubleTime() - header.time;
// increase time until unique value is obtained
while (m.find(d) != m.end()) d += .001;
m.insert(std::pair<double,int>(d, i));
}
}
}
return m;
}

struct CompressAndDumpState_args
{
std::vector<u8>* buffer_vector;
std::mutex* buffer_mutex;
std::string filename;
bool wait;
};

void CompressAndDumpState(CompressAndDumpState_args save_args)
{
std::lock_guard<std::mutex> lk(*save_args.buffer_mutex);
g_compressAndDumpStateSyncEvent.Set();
if (!save_args.wait)
g_compressAndDumpStateSyncEvent.Set();

const u8* const buffer_data = &(*(save_args.buffer_vector))[0];
const size_t buffer_size = (save_args.buffer_vector)->size();
Expand Down Expand Up @@ -201,13 +238,15 @@ void CompressAndDumpState(CompressAndDumpState_args save_args)
if (!f)
{
Core::DisplayMessage("Could not save state", 2000);
g_compressAndDumpStateSyncEvent.Set();
return;
}

// Setting up the header
StateHeader header;
memcpy(header.gameID, SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), 6);
header.size = g_use_compression ? buffer_size : 0;
header.time = Common::Timer::GetDoubleTime();

f.WriteArray(&header, 1);

Expand Down Expand Up @@ -244,9 +283,10 @@ void CompressAndDumpState(CompressAndDumpState_args save_args)

Core::DisplayMessage(StringFromFormat("Saved State to %s",
filename.c_str()).c_str(), 2000);
g_compressAndDumpStateSyncEvent.Set();
}

void SaveAs(const std::string& filename)
void SaveAs(const std::string& filename, bool wait)
{
// Pause the core while we save the state
bool wasUnpaused = Core::PauseAndLock(true);
Expand Down Expand Up @@ -274,6 +314,7 @@ void SaveAs(const std::string& filename)
save_args.buffer_vector = &g_current_buffer;
save_args.buffer_mutex = &g_cs_current_buffer;
save_args.filename = filename;
save_args.wait = wait;

Flush();
g_save_thread = std::thread(CompressAndDumpState, save_args);
Expand All @@ -291,6 +332,20 @@ void SaveAs(const std::string& filename)
Core::PauseAndLock(false, wasUnpaused);
}

bool ReadHeader(const std::string filename, StateHeader& header)
{
Flush();
File::IOFile f(filename, "rb");
if (!f)
{
Core::DisplayMessage("State not found", 2000);
return false;
}

f.ReadArray(&header, 1);
return true;
}

void LoadFileStateData(const std::string& filename, std::vector<u8>& ret_data)
{
Flush();
Expand Down Expand Up @@ -496,9 +551,9 @@ static std::string MakeStateFilename(int number)
SConfig::GetInstance().m_LocalCoreStartupParameter.GetUniqueID().c_str(), number);
}

void Save(int slot)
void Save(int slot, bool wait)
{
SaveAs(MakeStateFilename(slot));
SaveAs(MakeStateFilename(slot), wait);
}

void Load(int slot)
Expand All @@ -511,12 +566,35 @@ void Verify(int slot)
VerifyAt(MakeStateFilename(slot));
}

void LoadLastSaved()
void LoadLastSaved(int i)
{
if (g_last_filename.empty())
Core::DisplayMessage("There is no last saved state", 2000);
std::map<double, int> savedStates = GetSavedStates();

if (i > savedStates.size())
Core::DisplayMessage("State doesn't exist", 2000);
else
LoadAs(g_last_filename);
{
std::map<double, int>::iterator it = savedStates.begin();
std::advance(it, i-1);
Load(it->second);
}
}

// must wait for state to be written because it must know if all slots are taken
void SaveFirstSaved()
{
std::map<double, int> savedStates = GetSavedStates();

// save to an empty slot
if (savedStates.size() < NUM_STATES)
Save(GetEmptySlot(savedStates), true);
// overwrite the oldest state
else
{
std::map<double, int>::iterator it = savedStates.begin();
std::advance(it, savedStates.size()-1);
Save(it->second, true);
}
}

void Flush()
Expand Down
21 changes: 18 additions & 3 deletions Source/Core/Core/Src/State.h
Expand Up @@ -14,30 +14,45 @@
namespace State
{

// number of states
static const u32 NUM_STATES = 8;

struct StateHeader
{
u8 gameID[6];
u32 size;
double time;
};

void Init();

void Shutdown();

void EnableCompression(bool compression);

bool ReadHeader(const std::string filename, StateHeader& header);

// These don't happen instantly - they get scheduled as events.
// ...But only if we're not in the main cpu thread.
// If we're in the main cpu thread then they run immediately instead
// because some things (like Lua) need them to run immediately.
// Slots from 0-99.
void Save(int slot);
void Save(int slot, bool wait = false);
void Load(int slot);
void Verify(int slot);

void SaveAs(const std::string &filename);
void SaveAs(const std::string &filename, bool wait = false);
void LoadAs(const std::string &filename);
void VerifyAt(const std::string &filename);

void SaveToBuffer(std::vector<u8>& buffer);
void LoadFromBuffer(std::vector<u8>& buffer);
void VerifyBuffer(std::vector<u8>& buffer);

void LoadLastSaved();
static std::string MakeStateFilename(int number);

void LoadLastSaved(int i = 1);
void SaveFirstSaved();
void UndoSaveState();
void UndoLoadState();

Expand Down

0 comments on commit e5fdd30

Please sign in to comment.