FullscreenUI: Add undo load state to load menu

This commit is contained in:
Connor McLaughlin 2021-07-13 22:39:55 +10:00
parent 921c5cdfbf
commit ab9109f3b1
3 changed files with 74 additions and 20 deletions

View file

@ -746,7 +746,7 @@ bool CommonHostInterface::SaveUndoLoadState()
m_undo_load_state.reset();
m_undo_load_state = ByteStream_CreateGrowableMemoryStream(nullptr, System::MAX_SAVE_STATE_SIZE);
if (!System::SaveState(m_undo_load_state.get(), 0))
if (!System::SaveState(m_undo_load_state.get()))
{
AddOSDMessage(TranslateStdString("OSDMessage", "Failed to save undo load state."), 15.0f);
m_undo_load_state.reset();
@ -2836,30 +2836,13 @@ std::optional<CommonHostInterface::SaveStateInfo> CommonHostInterface::GetSaveSt
}
std::optional<CommonHostInterface::ExtendedSaveStateInfo>
CommonHostInterface::GetExtendedSaveStateInfo(const char* game_code, s32 slot)
CommonHostInterface::GetExtendedSaveStateInfo(ByteStream* stream)
{
const bool global = (!game_code || game_code[0] == 0);
std::string path = global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(game_code, slot);
FILESYSTEM_STAT_DATA sd;
if (!FileSystem::StatFile(path.c_str(), &sd))
return std::nullopt;
std::unique_ptr<ByteStream> stream =
FileSystem::OpenFile(path.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_SEEKABLE);
if (!stream)
return std::nullopt;
SAVE_STATE_HEADER header;
if (!stream->Read(&header, sizeof(header)) || header.magic != SAVE_STATE_MAGIC)
return std::nullopt;
ExtendedSaveStateInfo ssi;
ssi.path = std::move(path);
ssi.timestamp = sd.ModificationTime.AsUnixTimestamp();
ssi.slot = slot;
ssi.global = global;
if (header.version < SAVE_STATE_MINIMUM_VERSION || header.version > SAVE_STATE_VERSION)
{
ssi.title = StringUtil::StdStringFromFormat(
@ -2902,6 +2885,53 @@ CommonHostInterface::GetExtendedSaveStateInfo(const char* game_code, s32 slot)
return ssi;
}
std::optional<CommonHostInterface::ExtendedSaveStateInfo>
CommonHostInterface::GetExtendedSaveStateInfo(const char* game_code, s32 slot)
{
const bool global = (!game_code || game_code[0] == 0);
std::string path = global ? GetGlobalSaveStateFileName(slot) : GetGameSaveStateFileName(game_code, slot);
FILESYSTEM_STAT_DATA sd;
if (!FileSystem::StatFile(path.c_str(), &sd))
return std::nullopt;
std::unique_ptr<ByteStream> stream =
FileSystem::OpenFile(path.c_str(), BYTESTREAM_OPEN_READ | BYTESTREAM_OPEN_SEEKABLE);
if (!stream)
return std::nullopt;
std::optional<ExtendedSaveStateInfo> ssi = GetExtendedSaveStateInfo(stream.get());
if (!ssi)
return std::nullopt;
ssi->path = std::move(path);
ssi->timestamp = sd.ModificationTime.AsUnixTimestamp();
ssi->slot = slot;
ssi->global = global;
return ssi;
}
std::optional<CommonHostInterface::ExtendedSaveStateInfo> CommonHostInterface::GetUndoSaveStateInfo()
{
std::optional<ExtendedSaveStateInfo> ssi;
if (m_undo_load_state)
{
m_undo_load_state->SeekAbsolute(0);
ssi = GetExtendedSaveStateInfo(m_undo_load_state.get());
m_undo_load_state->SeekAbsolute(0);
if (ssi)
{
ssi->timestamp = 0;
ssi->slot = 0;
ssi->global = false;
}
}
return ssi;
}
void CommonHostInterface::DeleteSaveStates(const char* game_code, bool resume)
{
const std::vector<SaveStateInfo> states(GetAvailableSaveStates(game_code));

View file

@ -209,9 +209,15 @@ public:
/// Returns save state info if present. If game_code is null or empty, assumes global state.
std::optional<SaveStateInfo> GetSaveStateInfo(const char* game_code, s32 slot);
/// Returns save state info from opened save state stream.
std::optional<ExtendedSaveStateInfo> GetExtendedSaveStateInfo(ByteStream* stream);
/// Returns save state info if present. If game_code is null or empty, assumes global state.
std::optional<ExtendedSaveStateInfo> GetExtendedSaveStateInfo(const char* game_code, s32 slot);
/// Returns save state info for the undo slot, if present.
std::optional<ExtendedSaveStateInfo> GetUndoSaveStateInfo();
/// Deletes save states for the specified game code. If resume is set, the resume state is deleted too.
void DeleteSaveStates(const char* game_code, bool resume);

View file

@ -2785,6 +2785,7 @@ void InitializeSaveStateListEntry(SaveStateListEntry* li, CommonHostInterface::E
li->summary =
StringUtil::StdStringFromFormat("%s - Saved %s", ssi->game_code.c_str(),
Timestamp::FromUnixTimestamp(ssi->timestamp).ToString("%c").GetCharArray());
li->slot = ssi->slot;
li->global = ssi->global;
li->path = std::move(ssi->path);
@ -2812,6 +2813,19 @@ void PopulateSaveStateListEntries()
{
s_save_state_selector_slots.clear();
if (s_save_state_selector_loading)
{
std::optional<CommonHostInterface::ExtendedSaveStateInfo> ssi = s_host_interface->GetUndoSaveStateInfo();
if (ssi)
{
SaveStateListEntry li;
InitializeSaveStateListEntry(&li, &ssi.value());
li.title = "Undo Load State";
li.summary = "Restores the state of the system prior to the last state loaded.";
s_save_state_selector_slots.push_back(std::move(li));
}
}
if (!System::GetRunningCode().empty())
{
for (s32 i = 1; i <= CommonHostInterface::PER_GAME_SAVE_STATE_SLOTS; i++)
@ -3025,7 +3039,11 @@ void DrawSaveStateSelector(bool is_loading, bool fullscreen)
{
const std::string& path = entry.path;
s_host_interface->RunLater([path]() {
s_host_interface->LoadState(path.c_str());
if (path.empty())
s_host_interface->UndoLoadState();
else
s_host_interface->LoadState(path.c_str());
CloseSaveStateSelector();
});
}