Skip to content

Commit

Permalink
IOS7: Save state to a separate file when closing ScummVM
Browse files Browse the repository at this point in the history
The state information was saved using ConfMan in the scummvm ini file,
but this could cause corruption of that ini file if the application
was killed while ConfMan was flushed (see bug #14623). To reduce the
risk of losing the config file, we now save the state to a separate
file.
  • Loading branch information
criezy committed Dec 11, 2023
1 parent e1fc76c commit 9267373
Showing 1 changed file with 38 additions and 21 deletions.
59 changes: 38 additions & 21 deletions backends/platform/ios7/ios7_osys_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

#include <unistd.h>
#include <string.h>
#include <stdio.h>

#include <sys/time.h>
#include <QuartzCore/QuartzCore.h>
Expand All @@ -36,6 +37,7 @@
#include "common/fs.h"
#include "common/config-manager.h"
#include "common/translation.h"
#include "common/formats/ini-file.h"

#include "base/main.h"

Expand Down Expand Up @@ -280,49 +282,64 @@ void OSystem_iOS7::saveState() {
if (g_engine && g_engine->hasFeature(Engine::kSupportsSavingDuringRuntime) && g_engine->canSaveAutosaveCurrently()) {
Common::String targetName(ConfMan.getActiveDomainName());
int saveSlot = g_engine->getAutosaveSlot();
if (saveSlot == -1)
return;
// Make sure we do not overwrite a user save
SaveStateDescriptor desc = g_engine->getMetaEngine()->querySaveMetaInfos(targetName.c_str(), saveSlot);
if (desc.getSaveSlot() != -1 && !desc.isAutosave())
return;

// Do the auto-save, and if successful store this it in the config
// Do the auto-save, and if successful store this it in the uset settings
if (g_engine->saveGameState(saveSlot, _("Autosave"), true).getCode() == Common::kNoError) {
ConfMan.set("restore_target", targetName, Common::ConfigManager::kApplicationDomain);
ConfMan.setInt("restore_slot", saveSlot, Common::ConfigManager::kApplicationDomain);
ConfMan.flushToDisk();
Common::INIFile stateFile;
stateFile.addSection("state");
stateFile.setKey("restore_target", "state", targetName);
stateFile.setKey("restore_slot", "state", Common::String::format("%d", saveSlot));
stateFile.saveToFile("/scummvm.state");
}
}
}

void OSystem_iOS7::restoreState() {
Common::String target;
int slot = -1;
if (ConfMan.hasKey("restore_target", Common::ConfigManager::kApplicationDomain) &&
ConfMan.hasKey("restore_slot", Common::ConfigManager::kApplicationDomain)) {
target = ConfMan.get("restore_target", Common::ConfigManager::kApplicationDomain);
slot = ConfMan.getInt("restore_slot", Common::ConfigManager::kApplicationDomain);
// If the g_engine is still running (i.e. the application was not terminated) we don't need to do anything other than clear the saved state.
if (g_engine) {
clearState();
return;
}

// If the g_engine is still running (i.e. the application was not terminated) we don't need to do anything.
if (g_engine)
// Read the state
Common::FSNode node("/scummvm.state");
Common::File stateFile;
if (!stateFile.open(node))
return;

if (!target.empty() && slot != -1) {
ConfMan.setInt("save_slot", slot, Common::ConfigManager::kTransientDomain);
ConfMan.setActiveDomain(target);
Common::String targetName, slotString;
int saveSlot = -1;
Common::INIFile stateIniFile;
if (stateIniFile.loadFromStream(stateFile) &&
stateIniFile.getKey("restore_target", "state", targetName) &&
stateIniFile.getKey("restore_slot", "state", slotString) &&
!slotString.empty()) {
char *errpos;
saveSlot = (int)strtol(slotString.c_str(), &errpos, 10);
if (slotString.c_str() == errpos)
saveSlot = -1;
}

clearState();

// Reload the state
if (!targetName.empty() && saveSlot != -1) {
ConfMan.setInt("save_slot", saveSlot, Common::ConfigManager::kTransientDomain);
ConfMan.setActiveDomain(targetName);
if (GUI::GuiManager::hasInstance())
g_gui.exitLoop();
}
}

void OSystem_iOS7::clearState() {
if (ConfMan.hasKey("restore_target", Common::ConfigManager::kApplicationDomain) &&
ConfMan.hasKey("restore_slot", Common::ConfigManager::kApplicationDomain)) {
ConfMan.removeKey("restore_target", Common::ConfigManager::kApplicationDomain);
ConfMan.removeKey("restore_slot", Common::ConfigManager::kApplicationDomain);
ConfMan.flushToDisk();
}
Common::String statePath = _chrootBasePath + "/scummvm.state";
remove(statePath.c_str());
}

uint32 OSystem_iOS7::getMillis(bool skipRecord) {
Expand Down

0 comments on commit 9267373

Please sign in to comment.