Decouple save states from UI.

It's the UI's job to decide how and what things are displayed, not Core's.
This also reduces reasons for linkage issues.
This commit is contained in:
Unknown W. Brackets 2016-05-27 21:25:05 -07:00
parent 2862253f12
commit 27d135d48a
7 changed files with 58 additions and 35 deletions

View File

@ -43,7 +43,6 @@
#include "Core/MIPS/JitCommon/JitBlockCache.h"
#include "HW/MemoryStick.h"
#include "GPU/GPUState.h"
#include "UI/OnScreenDisplay.h"
namespace SaveState
{
@ -389,9 +388,8 @@ namespace SaveState
Load(fn, callback, cbUserData);
} else {
I18NCategory *sy = GetI18NCategory("System");
osm.Show(sy->T("Failed to load state. Error in the file system."), 2.0);
if (callback)
callback(false, cbUserData);
callback(false, sy->T("Failed to load state. Error in the file system."), cbUserData);
}
}
@ -400,7 +398,7 @@ namespace SaveState
std::string fn = GenerateSaveSlotFilename(gameFilename, slot, STATE_EXTENSION);
std::string shot = GenerateSaveSlotFilename(gameFilename, slot, SCREENSHOT_EXTENSION);
if (!fn.empty()) {
auto renameCallback = [=](bool status, void *data) {
auto renameCallback = [=](bool status, const std::string &message, void *data) {
if (status) {
if (File::Exists(fn)) {
File::Delete(fn);
@ -408,17 +406,16 @@ namespace SaveState
File::Rename(fn + ".tmp", fn);
}
if (callback) {
callback(status, data);
callback(status, message, data);
}
};
// Let's also create a screenshot.
SaveScreenshot(shot, Callback(), 0);
Save(fn + ".tmp", renameCallback, cbUserData);
} else {
I18NCategory *sc = GetI18NCategory("Screen");
osm.Show("Failed to save state. Error in the file system.", 2.0);
I18NCategory *sy = GetI18NCategory("System");
if (callback)
callback(false, cbUserData);
callback(false, sy->T("Failed to save state. Error in the file system."), cbUserData);
}
}
@ -564,6 +561,7 @@ namespace SaveState
Operation &op = operations[i];
CChunkFileReader::Error result;
bool callbackResult;
std::string callbackMessage;
std::string reason;
I18NCategory *sc = GetI18NCategory("Screen");
@ -580,16 +578,16 @@ namespace SaveState
INFO_LOG(COMMON, "Loading state from %s", op.filename.c_str());
result = CChunkFileReader::Load(op.filename, PPSSPP_GIT_VERSION, state, &reason);
if (result == CChunkFileReader::ERROR_NONE) {
osm.Show(sc->T("Loaded State"), 2.0);
callbackMessage = sc->T("Loaded State");
callbackResult = true;
hasLoadedState = true;
} else if (result == CChunkFileReader::ERROR_BROKEN_STATE) {
HandleFailure();
osm.Show(i18nLoadFailure, 2.0);
callbackMessage = i18nLoadFailure;
ERROR_LOG(COMMON, "Load state failure: %s", reason.c_str());
callbackResult = false;
} else {
osm.Show(sc->T(reason.c_str(), i18nLoadFailure), 2.0);
callbackMessage = sc->T(reason.c_str(), i18nLoadFailure);
callbackResult = false;
}
break;
@ -598,45 +596,48 @@ namespace SaveState
INFO_LOG(COMMON, "Saving state to %s", op.filename.c_str());
result = CChunkFileReader::Save(op.filename, g_paramSFO.GetValueString("TITLE"), PPSSPP_GIT_VERSION, state);
if (result == CChunkFileReader::ERROR_NONE) {
osm.Show(sc->T("Saved State"), 2.0);
callbackMessage = sc->T("Saved State");
callbackResult = true;
} else if (result == CChunkFileReader::ERROR_BROKEN_STATE) {
HandleFailure();
osm.Show(i18nSaveFailure, 2.0);
callbackMessage = i18nSaveFailure;
ERROR_LOG(COMMON, "Save state failure: %s", reason.c_str());
callbackResult = false;
} else {
osm.Show(i18nSaveFailure, 2.0);
callbackMessage = i18nSaveFailure;
callbackResult = false;
}
break;
case SAVESTATE_VERIFY:
INFO_LOG(COMMON, "Verifying save state system");
callbackResult = CChunkFileReader::Verify(state) == CChunkFileReader::ERROR_NONE;
if (callbackResult) {
INFO_LOG(COMMON, "Verified save state system");
} else {
ERROR_LOG(COMMON, "Save state system verification failed");
}
break;
case SAVESTATE_REWIND:
INFO_LOG(COMMON, "Rewinding to recent savestate snapshot");
result = rewindStates.Restore();
if (result == CChunkFileReader::ERROR_NONE) {
osm.Show(sc->T("Loaded State"), 2.0);
callbackMessage = sc->T("Loaded State");
callbackResult = true;
hasLoadedState = true;
} else if (result == CChunkFileReader::ERROR_BROKEN_STATE) {
// Cripes. Good news is, we might have more. Let's try those too, better than a reset.
if (HandleFailure()) {
// Well, we did rewind, even if too much...
osm.Show(sc->T("Loaded State"), 2.0);
callbackMessage = sc->T("Loaded State");
callbackResult = true;
hasLoadedState = true;
} else {
osm.Show(i18nLoadFailure, 2.0);
callbackMessage = i18nLoadFailure;
callbackResult = false;
}
} else {
osm.Show(i18nLoadFailure, 2.0);
callbackMessage = i18nLoadFailure;
callbackResult = false;
}
break;
@ -655,7 +656,7 @@ namespace SaveState
}
if (op.callback)
op.callback(callbackResult, op.cbUserData);
op.callback(callbackResult, callbackMessage, op.cbUserData);
}
if (operations.size()) {
// Avoid triggering frame skipping due to slowdown

View File

@ -23,7 +23,7 @@
namespace SaveState
{
typedef std::function<void(bool status, void *cbUserData)> Callback;
typedef std::function<void(bool status, const std::string &message, void *cbUserData)> Callback;
static const int NUM_SLOTS = 5;
static const char *STATE_EXTENSION = "ppst";

View File

@ -178,7 +178,7 @@ void MainWindow::closeAct()
SetGameTitle("");
}
void SaveStateActionFinished(bool result, void *userdata)
void SaveStateActionFinished(bool result, const std::string &message, void *userdata)
{
// TODO: Improve messaging?
if (!result)

View File

@ -218,7 +218,14 @@ void EmuScreen::dialogFinished(const Screen *dialog, DialogResult result) {
RecreateViews();
}
static void AfterStateLoad(bool success, void *ignored) {
static void AfterSaveStateAction(bool success, const std::string &message, void *) {
if (!message.empty()) {
osm.Show(message, 2.0);
}
}
static void AfterStateBoot(bool success, const std::string &message, void *ignored) {
AfterSaveStateAction(success, message, ignored);
Core_EnableStepping(false);
host->UpdateDisassembly();
}
@ -252,7 +259,7 @@ void EmuScreen::sendMessage(const char *message, const char *value) {
} else if (!strcmp(message, "boot")) {
const char *ext = strrchr(value, '.');
if (ext != nullptr && !strcmp(ext, ".ppst")) {
SaveState::Load(value, &AfterStateLoad);
SaveState::Load(value, &AfterStateBoot);
} else {
PSP_Shutdown();
bootPending_ = true;
@ -385,16 +392,16 @@ void EmuScreen::onVKeyDown(int virtualKeyCode) {
case VIRTKEY_REWIND:
if (SaveState::CanRewind()) {
SaveState::Rewind();
SaveState::Rewind(&AfterSaveStateAction);
} else {
osm.Show(sc->T("norewind", "No rewind save states available"), 2.0);
}
break;
case VIRTKEY_SAVE_STATE:
SaveState::SaveSlot(gamePath_, g_Config.iCurrentStateSlot, SaveState::Callback());
SaveState::SaveSlot(gamePath_, g_Config.iCurrentStateSlot, &AfterSaveStateAction);
break;
case VIRTKEY_LOAD_STATE:
SaveState::LoadSlot(gamePath_, g_Config.iCurrentStateSlot, SaveState::Callback());
SaveState::LoadSlot(gamePath_, g_Config.iCurrentStateSlot, &AfterSaveStateAction);
break;
case VIRTKEY_NEXT_SLOT:
SaveState::NextSlot();
@ -1024,7 +1031,7 @@ void EmuScreen::autoLoad() {
//check if save state has save, if so, load
int lastSlot = SaveState::GetNewestSlot(gamePath_);
if (g_Config.bEnableAutoLoad && lastSlot != -1) {
SaveState::LoadSlot(gamePath_, lastSlot, SaveState::Callback(), 0);
SaveState::LoadSlot(gamePath_, lastSlot, &AfterSaveStateAction);
g_Config.iCurrentStateSlot = lastSlot;
}
}

View File

@ -489,8 +489,13 @@ void NativeInit(int argc, const char *argv[], const char *savegame_dir, const ch
}
#endif
if (!boot_filename.empty() && stateToLoad != NULL)
SaveState::Load(stateToLoad);
if (!boot_filename.empty() && stateToLoad != NULL) {
SaveState::Load(stateToLoad, [](bool status, const std::string &message, void *) {
if (!message.empty()) {
osm.Show(message, 2.0);
}
});
}
screenManager = new ScreenManager();
if (skipLogo) {

View File

@ -40,6 +40,7 @@
#include "UI/ReportScreen.h"
#include "UI/CwCheatScreen.h"
#include "UI/MainScreen.h"
#include "UI/OnScreenDisplay.h"
#include "UI/GameInfoCache.h"
void AsyncImageFileView::GetContentDimensions(const UIContext &dc, float &w, float &h) const {
@ -224,9 +225,15 @@ void SaveSlotView::Draw(UIContext &dc) {
UI::LinearLayout::Draw(dc);
}
static void AfterSaveStateAction(bool status, const std::string &message, void *) {
if (!message.empty()) {
osm.Show(message, 2.0);
}
}
UI::EventReturn SaveSlotView::OnLoadState(UI::EventParams &e) {
g_Config.iCurrentStateSlot = slot_;
SaveState::LoadSlot(gamePath_, slot_, SaveState::Callback(), 0);
SaveState::LoadSlot(gamePath_, slot_, &AfterSaveStateAction);
UI::EventParams e2;
e2.v = this;
OnStateLoaded.Trigger(e2);
@ -235,7 +242,7 @@ UI::EventReturn SaveSlotView::OnLoadState(UI::EventParams &e) {
UI::EventReturn SaveSlotView::OnSaveState(UI::EventParams &e) {
g_Config.iCurrentStateSlot = slot_;
SaveState::SaveSlot(gamePath_, slot_, SaveState::Callback(), 0);
SaveState::SaveSlot(gamePath_, slot_, &AfterSaveStateAction);
UI::EventParams e2;
e2.v = this;
OnStateSaved.Trigger(e2);
@ -389,7 +396,7 @@ UI::EventReturn GamePauseScreen::OnReportFeedback(UI::EventParams &e) {
}
UI::EventReturn GamePauseScreen::OnRewind(UI::EventParams &e) {
SaveState::Rewind(SaveState::Callback(), 0);
SaveState::Rewind(&AfterSaveStateAction);
screenManager()->finishDialog(this, DR_CANCEL);
return UI::EVENT_DONE;

View File

@ -400,7 +400,10 @@ namespace MainWindow {
g_Config.iInternalScreenRotation = rotation;
}
static void SaveStateActionFinished(bool result, void *userdata) {
static void SaveStateActionFinished(bool result, const std::string &message, void *userdata) {
if (!message.empty()) {
osm.Show(message, 2.0);
}
PostMessage(MainWindow::GetHWND(), WM_USER_SAVESTATE_FINISH, 0, 0);
}