Merge pull request #12262 from AdmiralCurtiss/last-state-order-refactor

Core/State: Refactor logic for determining the relative age of existing savestates.
This commit is contained in:
JosJuice 2023-10-31 19:39:47 +01:00 committed by GitHub
commit b32ac9353e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -3,6 +3,7 @@
#include "Core/State.h" #include "Core/State.h"
#include <algorithm>
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
#include <filesystem> #include <filesystem>
@ -230,21 +231,23 @@ void SaveToBuffer(std::vector<u8>& buffer)
true); true);
} }
// return state number not in map namespace
static int GetEmptySlot(std::map<double, int> m) {
struct SlotWithTimestamp
{
int slot;
double timestamp;
};
} // namespace
// returns first slot number not in the vector, or -1 if all are in the vector
static int GetEmptySlot(const std::vector<SlotWithTimestamp>& used_slots)
{ {
for (int i = 1; i <= (int)NUM_STATES; i++) for (int i = 1; i <= (int)NUM_STATES; i++)
{ {
bool found = false; const auto it = std::find_if(used_slots.begin(), used_slots.end(),
for (auto& p : m) [i](const SlotWithTimestamp& slot) { return slot.slot == i; });
{ if (it == used_slots.end())
if (p.second == i)
{
found = true;
break;
}
}
if (!found)
return i; return i;
} }
return -1; return -1;
@ -265,27 +268,29 @@ static double GetSystemTimeAsDouble()
static std::string SystemTimeAsDoubleToString(double time) static std::string SystemTimeAsDoubleToString(double time)
{ {
// revert adjustments from GetSystemTimeAsDouble() to get a normal Unix timestamp again // revert adjustments from GetSystemTimeAsDouble() to get a normal Unix timestamp again
time_t seconds = (time_t)time + DOUBLE_TIME_OFFSET; time_t seconds = static_cast<time_t>(time) + DOUBLE_TIME_OFFSET;
tm* localTime = localtime(&seconds); errno = 0;
tm* local_time = localtime(&seconds);
if (errno != 0 || !local_time)
return "";
#ifdef _WIN32 #ifdef _WIN32
wchar_t tmp[32] = {}; wchar_t tmp[32] = {};
wcsftime(tmp, std::size(tmp), L"%x %X", localTime); wcsftime(tmp, std::size(tmp), L"%x %X", local_time);
return WStringToUTF8(tmp); return WStringToUTF8(tmp);
#else #else
char tmp[32] = {}; char tmp[32] = {};
strftime(tmp, sizeof(tmp), "%x %X", localTime); strftime(tmp, sizeof(tmp), "%x %X", local_time);
return tmp; return tmp;
#endif #endif
} }
static std::string MakeStateFilename(int number); static std::string MakeStateFilename(int number);
// read state timestamps static std::vector<SlotWithTimestamp> GetUsedSlotsWithTimestamp()
static std::map<double, int> GetSavedStates()
{ {
std::vector<SlotWithTimestamp> result;
StateHeader header; StateHeader header;
std::map<double, int> m;
for (int i = 1; i <= (int)NUM_STATES; i++) for (int i = 1; i <= (int)NUM_STATES; i++)
{ {
std::string filename = MakeStateFilename(i); std::string filename = MakeStateFilename(i);
@ -293,17 +298,16 @@ static std::map<double, int> GetSavedStates()
{ {
if (ReadHeader(filename, header)) if (ReadHeader(filename, header))
{ {
double d = GetSystemTimeAsDouble() - header.legacy_header.time; result.emplace_back(SlotWithTimestamp{.slot = i, .timestamp = header.legacy_header.time});
// increase time until unique value is obtained
while (m.find(d) != m.end())
d += .001;
m.emplace(d, i);
} }
} }
} }
return m; return result;
}
static bool CompareTimestamp(const SlotWithTimestamp& lhs, const SlotWithTimestamp& rhs)
{
return lhs.timestamp < rhs.timestamp;
} }
static void CompressBufferToFile(const u8* raw_buffer, u64 size, File::IOFile& f) static void CompressBufferToFile(const u8* raw_buffer, u64 size, File::IOFile& f)
@ -954,33 +958,37 @@ void Load(int slot)
void LoadLastSaved(int i) void LoadLastSaved(int i)
{ {
std::map<double, int> savedStates = GetSavedStates(); if (i <= 0)
if (i > (int)savedStates.size())
Core::DisplayMessage("State doesn't exist", 2000);
else
{ {
std::map<double, int>::iterator it = savedStates.begin(); Core::DisplayMessage("State doesn't exist", 2000);
std::advance(it, i - 1); return;
Load(it->second);
} }
std::vector<SlotWithTimestamp> used_slots = GetUsedSlotsWithTimestamp();
if (static_cast<size_t>(i) > used_slots.size())
{
Core::DisplayMessage("State doesn't exist", 2000);
return;
}
std::stable_sort(used_slots.begin(), used_slots.end(), CompareTimestamp);
Load((used_slots.end() - i)->slot);
} }
// must wait for state to be written because it must know if all slots are taken // must wait for state to be written because it must know if all slots are taken
void SaveFirstSaved() void SaveFirstSaved()
{ {
std::map<double, int> savedStates = GetSavedStates(); std::vector<SlotWithTimestamp> used_slots = GetUsedSlotsWithTimestamp();
if (used_slots.size() < NUM_STATES)
// 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(); // save to an empty slot
std::advance(it, savedStates.size() - 1); Save(GetEmptySlot(used_slots), true);
Save(it->second, true); return;
} }
// overwrite the oldest state
std::stable_sort(used_slots.begin(), used_slots.end(), CompareTimestamp);
Save(used_slots.front().slot, true);
} }
// Load the last state before loading the state // Load the last state before loading the state