mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Savestate load undo
This commit is contained in:
parent
7c769d2511
commit
626dfb5bd7
6 changed files with 91 additions and 18 deletions
|
@ -532,6 +532,7 @@ static ConfigSetting generalSettings[] = {
|
|||
ConfigSetting("SaveLoadResetsAVdumping", &g_Config.bSaveLoadResetsAVdumping, false),
|
||||
ConfigSetting("StateSlot", &g_Config.iCurrentStateSlot, 0, true, true),
|
||||
ConfigSetting("EnableStateUndo", &g_Config.bEnableStateUndo, &DefaultEnableStateUndo, true, true),
|
||||
ConfigSetting("StateLoadUndoGame", &g_Config.sStateLoadUndoGame, "NA", true, false),
|
||||
ConfigSetting("RewindFlipFrequency", &g_Config.iRewindFlipFrequency, 0, true, true),
|
||||
|
||||
ConfigSetting("ShowOnScreenMessage", &g_Config.bShowOnScreenMessages, true, true, false),
|
||||
|
|
|
@ -209,6 +209,7 @@ public:
|
|||
int iRewindFlipFrequency;
|
||||
bool bUISound;
|
||||
bool bEnableStateUndo;
|
||||
std::string sStateLoadUndoGame;
|
||||
int iAutoLoadSaveState; // 0 = off, 1 = oldest, 2 = newest, >2 = slot number + 3
|
||||
bool bEnableCheats;
|
||||
bool bReloadCheats;
|
||||
|
|
|
@ -412,18 +412,19 @@ namespace SaveState
|
|||
return filename.GetFilename() + " " + sy->T("(broken)");
|
||||
}
|
||||
|
||||
Path GenerateSaveSlotFilename(const Path &gameFilename, int slot, const char *extension)
|
||||
{
|
||||
std::string GenerateFullDiskId(const Path &gameFilename) {
|
||||
std::string discId = g_paramSFO.GetValueString("DISC_ID");
|
||||
std::string discVer = g_paramSFO.GetValueString("DISC_VERSION");
|
||||
std::string fullDiscId;
|
||||
if (discId.empty()) {
|
||||
discId = g_paramSFO.GenerateFakeID();
|
||||
discVer = "1.00";
|
||||
}
|
||||
fullDiscId = StringFromFormat("%s_%s", discId.c_str(), discVer.c_str());
|
||||
return StringFromFormat("%s_%s", discId.c_str(), discVer.c_str());
|
||||
}
|
||||
|
||||
std::string filename = StringFromFormat("%s_%d.%s", fullDiscId.c_str(), slot, extension);
|
||||
Path GenerateSaveSlotFilename(const Path &gameFilename, int slot, const char *extension)
|
||||
{
|
||||
std::string filename = StringFromFormat("%s_%d.%s", GenerateFullDiskId(gameFilename).c_str(), slot, extension);
|
||||
return GetSysDirectory(DIRECTORY_SAVESTATE) / filename;
|
||||
}
|
||||
|
||||
|
@ -437,18 +438,6 @@ namespace SaveState
|
|||
g_Config.iCurrentStateSlot = (g_Config.iCurrentStateSlot + 1) % NUM_SLOTS;
|
||||
}
|
||||
|
||||
void LoadSlot(const Path &gameFilename, int slot, Callback callback, void *cbUserData)
|
||||
{
|
||||
Path fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION);
|
||||
if (!fn.empty()) {
|
||||
Load(fn, slot, callback, cbUserData);
|
||||
} else {
|
||||
auto sy = GetI18NCategory("System");
|
||||
if (callback)
|
||||
callback(Status::FAILURE, sy->T("Failed to load state. Error in the file system."), cbUserData);
|
||||
}
|
||||
}
|
||||
|
||||
static void DeleteIfExists(const Path &fn) {
|
||||
// Just avoiding error messages.
|
||||
if (File::Exists(fn)) {
|
||||
|
@ -471,6 +460,63 @@ namespace SaveState
|
|||
}
|
||||
}
|
||||
|
||||
void LoadSlot(const Path &gameFilename, int slot, Callback callback, void *cbUserData)
|
||||
{
|
||||
Path fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION);
|
||||
if (!fn.empty()) {
|
||||
// This add only 1 extra state, should we just always enable it?
|
||||
if (g_Config.bEnableStateUndo) {
|
||||
Path backup = GetSysDirectory(DIRECTORY_SAVESTATE) / LOAD_UNDO_NAME;
|
||||
|
||||
auto saveCallback = [=](Status status, const std::string &message, void *data) {
|
||||
if (status != Status::FAILURE) {
|
||||
DeleteIfExists(backup);
|
||||
File::Rename(backup.WithExtraExtension(".tmp"), backup);
|
||||
g_Config.sStateLoadUndoGame = GenerateFullDiskId(gameFilename);
|
||||
Load(fn, slot, callback, cbUserData);
|
||||
} else if (callback) {
|
||||
callback(status, message, data);
|
||||
}
|
||||
};
|
||||
|
||||
if (!backup.empty()) {
|
||||
Save(backup.WithExtraExtension(".tmp"), -2, saveCallback, cbUserData);
|
||||
} else {
|
||||
auto sy = GetI18NCategory("System");
|
||||
if (callback)
|
||||
callback(Status::FAILURE, sy->T("Failed to save state for load undo. Error in the file system."), cbUserData);
|
||||
}
|
||||
} else {
|
||||
Load(fn, slot, callback, cbUserData);
|
||||
}
|
||||
} else {
|
||||
auto sy = GetI18NCategory("System");
|
||||
if (callback)
|
||||
callback(Status::FAILURE, sy->T("Failed to load state. Error in the file system."), cbUserData);
|
||||
}
|
||||
}
|
||||
|
||||
bool UndoLoad(const Path &gameFilename, Callback callback, void *cbUserData)
|
||||
{
|
||||
if (g_Config.sStateLoadUndoGame != GenerateFullDiskId(gameFilename)) {
|
||||
auto sy = GetI18NCategory("System");
|
||||
if (callback)
|
||||
callback(Status::FAILURE, sy->T("Error: load undo state is from a different game"), cbUserData);
|
||||
return false;
|
||||
}
|
||||
|
||||
Path fn = GetSysDirectory(DIRECTORY_SAVESTATE) / LOAD_UNDO_NAME;
|
||||
if (!fn.empty()) {
|
||||
Load(fn, -2, callback, cbUserData); // Slot number is visual only, -2 will display special message (kinda have to find a better way)
|
||||
return true;
|
||||
} else {
|
||||
auto sy = GetI18NCategory("System");
|
||||
if (callback)
|
||||
callback(Status::FAILURE, sy->T("Failed to load state for load undo. Error in the file system."), cbUserData);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SaveSlot(const Path &gameFilename, int slot, Callback callback, void *cbUserData)
|
||||
{
|
||||
Path fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION);
|
||||
|
@ -541,6 +587,12 @@ namespace SaveState
|
|||
return File::Exists(fn);
|
||||
}
|
||||
|
||||
bool HasUndoLoad(const Path &gameFilename)
|
||||
{
|
||||
Path fn = GetSysDirectory(DIRECTORY_SAVESTATE) / LOAD_UNDO_NAME;
|
||||
return File::Exists(fn) && g_Config.sStateLoadUndoGame == GenerateFullDiskId(gameFilename);
|
||||
}
|
||||
|
||||
bool operator < (const tm &t1, const tm &t2) {
|
||||
if (t1.tm_year < t2.tm_year) return true;
|
||||
if (t1.tm_year > t2.tm_year) return false;
|
||||
|
@ -752,7 +804,7 @@ namespace SaveState
|
|||
// Use the state's latest version as a guess for saveStateInitialGitVersion.
|
||||
result = CChunkFileReader::Load(op.filename, &saveStateInitialGitVersion, state, &errorString);
|
||||
if (result == CChunkFileReader::ERROR_NONE) {
|
||||
callbackMessage = slot_prefix + sc->T("Loaded State");
|
||||
callbackMessage = op.slot != -2 ? slot_prefix + sc->T("Loaded State") : sc->T("State load undone");
|
||||
callbackResult = Status::SUCCESS;
|
||||
hasLoadedState = true;
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@ namespace SaveState
|
|||
static const char *UNDO_STATE_EXTENSION = "undo.ppst";
|
||||
static const char *UNDO_SCREENSHOT_EXTENSION = "undo.jpg";
|
||||
|
||||
static const char *LOAD_UNDO_NAME = "load_undo.ppst";
|
||||
|
||||
void Init();
|
||||
void Shutdown();
|
||||
|
||||
|
@ -45,9 +47,11 @@ namespace SaveState
|
|||
void SaveSlot(const Path &gameFilename, int slot, Callback callback, void *cbUserData = 0);
|
||||
void LoadSlot(const Path &gameFilename, int slot, Callback callback, void *cbUserData = 0);
|
||||
bool UndoSaveSlot(const Path &gameFilename, int slot);
|
||||
bool UndoLoad(const Path &gameFilename, Callback callback, void *cbUserData = 0);
|
||||
// Checks whether there's an existing save in the specified slot.
|
||||
bool HasSaveInSlot(const Path &gameFilename, int slot);
|
||||
bool HasUndoSaveInSlot(const Path &gameFilename, int slot);
|
||||
bool HasUndoLoad(const Path &gameFilename);
|
||||
bool HasScreenshotInSlot(const Path &gameFilename, int slot);
|
||||
|
||||
int GetCurrentSlot();
|
||||
|
@ -57,6 +61,7 @@ namespace SaveState
|
|||
int GetOldestSlot(const Path &gameFilename);
|
||||
|
||||
std::string GetSlotDateAsString(const Path &gameFilename, int slot);
|
||||
std::string GenerateFullDiskId(const Path &gameFilename);
|
||||
Path GenerateSaveSlotFilename(const Path &gameFilename, int slot, const char *extension);
|
||||
|
||||
std::string GetTitle(const Path &filename);
|
||||
|
|
|
@ -396,6 +396,12 @@ void GamePauseScreen::CreateViews() {
|
|||
rewindButton->OnClick.Handle(this, &GamePauseScreen::OnRewind);
|
||||
}
|
||||
|
||||
if (g_Config.bEnableStateUndo) {
|
||||
UI::Choice *loadUndoButton = leftColumnItems->Add(new Choice(pa->T("Undo last state load")));
|
||||
loadUndoButton->SetEnabled(SaveState::HasUndoLoad(gamePath_));
|
||||
loadUndoButton->OnClick.Handle(this, &GamePauseScreen::OnLoadUndo);
|
||||
}
|
||||
|
||||
ViewGroup *rightColumn = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(300, FILL_PARENT, actionMenuMargins));
|
||||
root_->Add(rightColumn);
|
||||
|
||||
|
@ -492,6 +498,13 @@ UI::EventReturn GamePauseScreen::OnRewind(UI::EventParams &e) {
|
|||
return UI::EVENT_DONE;
|
||||
}
|
||||
|
||||
UI::EventReturn GamePauseScreen::OnLoadUndo(UI::EventParams &e) {
|
||||
SaveState::UndoLoad(gamePath_, &AfterSaveStateAction);
|
||||
|
||||
TriggerFinish(DR_CANCEL);
|
||||
return UI::EVENT_DONE;
|
||||
}
|
||||
|
||||
UI::EventReturn GamePauseScreen::OnCwCheat(UI::EventParams &e) {
|
||||
screenManager()->push(new CwCheatScreen(gamePath_));
|
||||
return UI::EVENT_DONE;
|
||||
|
|
|
@ -44,6 +44,7 @@ private:
|
|||
UI::EventReturn OnReportFeedback(UI::EventParams &e);
|
||||
|
||||
UI::EventReturn OnRewind(UI::EventParams &e);
|
||||
UI::EventReturn OnLoadUndo(UI::EventParams &e);
|
||||
|
||||
UI::EventReturn OnScreenshotClicked(UI::EventParams &e);
|
||||
UI::EventReturn OnCwCheat(UI::EventParams &e);
|
||||
|
|
Loading…
Add table
Reference in a new issue