aboutsummaryrefslogtreecommitdiff
path: root/engines/agi/detection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/agi/detection.cpp')
-rw-r--r--engines/agi/detection.cpp154
1 files changed, 112 insertions, 42 deletions
diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp
index 823ec7be66..7d5243e023 100644
--- a/engines/agi/detection.cpp
+++ b/engines/agi/detection.cpp
@@ -177,8 +177,8 @@ static const ADExtraGuiOptionsMap optionsList[] = {
using namespace Agi;
class AgiMetaEngine : public AdvancedMetaEngine {
- mutable Common::String _gameid;
- mutable Common::String _extra;
+ mutable Common::String _gameid;
+ mutable Common::String _extra;
public:
AgiMetaEngine() : AdvancedMetaEngine(Agi::gameDescriptions, sizeof(Agi::AGIGameDescription), agiGames, optionsList) {
@@ -205,20 +205,20 @@ public:
bool AgiMetaEngine::hasFeature(MetaEngineFeature f) const {
return
- (f == kSupportsListSaves) ||
- (f == kSupportsLoadingDuringStartup) ||
- (f == kSupportsDeleteSave) ||
- (f == kSavesSupportMetaInfo) ||
- (f == kSavesSupportThumbnail) ||
- (f == kSavesSupportCreationDate) ||
- (f == kSavesSupportPlayTime);
+ (f == kSupportsListSaves) ||
+ (f == kSupportsLoadingDuringStartup) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail) ||
+ (f == kSavesSupportCreationDate) ||
+ (f == kSavesSupportPlayTime);
}
bool AgiBase::hasFeature(EngineFeature f) const {
return
- (f == kSupportsRTL) ||
- (f == kSupportsLoadingDuringRuntime) ||
- (f == kSupportsSavingDuringRuntime);
+ (f == kSupportsRTL) ||
+ (f == kSupportsLoadingDuringRuntime) ||
+ (f == kSupportsSavingDuringRuntime);
}
@@ -254,29 +254,47 @@ bool AgiMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameD
}
SaveStateList AgiMetaEngine::listSaves(const char *target) const {
- const uint32 AGIflag = MKTAG('A','G','I',':');
+ const uint32 AGIflag = MKTAG('A', 'G', 'I', ':');
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
Common::StringArray filenames;
- char saveDesc[31];
Common::String pattern = target;
- pattern += ".???";
+ pattern += ".###";
filenames = saveFileMan->listSavefiles(pattern);
- sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
+ sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
SaveStateList saveList;
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
// Obtain the last 3 digits of the filename, since they correspond to the save slot
- int slotNum = atoi(file->c_str() + file->size() - 3);
+ int slotNr = atoi(file->c_str() + file->size() - 3);
- if (slotNum >= 0 && slotNum <= 999) {
+ if (slotNr >= 0 && slotNr <= 999) {
Common::InSaveFile *in = saveFileMan->openForLoading(*file);
if (in) {
uint32 type = in->readUint32BE();
- if (type == AGIflag)
- in->read(saveDesc, 31);
- saveList.push_back(SaveStateDescriptor(slotNum, saveDesc));
+ char description[31];
+
+ if (type == AGIflag) {
+ uint16 descriptionPos = 0;
+
+ in->read(description, 31);
+
+ // Security-check, if saveDescription has a terminating NUL
+ while (description[descriptionPos]) {
+ descriptionPos++;
+ if (descriptionPos >= sizeof(description))
+ break;
+ }
+ if (descriptionPos >= sizeof(description)) {
+ strcpy(description, "[broken saved game]");
+ }
+ } else {
+ strcpy(description, "[not an AGI saved game]");
+ }
+
delete in;
+
+ saveList.push_back(SaveStateDescriptor(slotNr, description));
}
}
}
@@ -291,9 +309,9 @@ void AgiMetaEngine::removeSaveState(const char *target, int slot) const {
g_system->getSavefileManager()->removeSavefile(fileName);
}
-SaveStateDescriptor AgiMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
- const uint32 AGIflag = MKTAG('A','G','I',':');
- Common::String fileName = Common::String::format("%s.%03d", target, slot);
+SaveStateDescriptor AgiMetaEngine::querySaveMetaInfos(const char *target, int slotNr) const {
+ const uint32 AGIflag = MKTAG('A', 'G', 'I', ':');
+ Common::String fileName = Common::String::format("%s.%03d", target, slotNr);
Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fileName);
@@ -303,49 +321,76 @@ SaveStateDescriptor AgiMetaEngine::querySaveMetaInfos(const char *target, int sl
return SaveStateDescriptor();
}
- char name[32];
- in->read(name, 31);
+ char description[31];
+ uint16 descriptionPos = 0;
+
+ in->read(description, 31);
+
+ while (description[descriptionPos]) {
+ descriptionPos++;
+ if (descriptionPos >= sizeof(description))
+ break;
+ }
+ if (descriptionPos >= sizeof(description)) {
+ // broken description, ignore it
+ delete in;
+
+ SaveStateDescriptor descriptor(slotNr, "[broken saved game]");
+ return descriptor;
+ }
- SaveStateDescriptor desc(slot, name);
+ SaveStateDescriptor descriptor(slotNr, description);
// Do not allow save slot 0 (used for auto-saving) to be deleted or
// overwritten.
- desc.setDeletableFlag(slot != 0);
- desc.setWriteProtectedFlag(slot == 0);
+ if (slotNr == 0) {
+ descriptor.setWriteProtectedFlag(true);
+ descriptor.setDeletableFlag(false);
+ } else {
+ descriptor.setWriteProtectedFlag(false);
+ descriptor.setDeletableFlag(true);
+ }
char saveVersion = in->readByte();
if (saveVersion >= 4) {
Graphics::Surface *const thumbnail = Graphics::loadThumbnail(*in);
- desc.setThumbnail(thumbnail);
+ descriptor.setThumbnail(thumbnail);
uint32 saveDate = in->readUint32BE();
uint16 saveTime = in->readUint16BE();
+ if (saveVersion >= 9) {
+ in->readByte(); // skip over seconds of saveTime (not needed here)
+ }
if (saveVersion >= 6) {
uint32 playTime = in->readUint32BE();
- desc.setPlayTime(playTime * 1000);
+ descriptor.setPlayTime(playTime * 1000);
}
int day = (saveDate >> 24) & 0xFF;
int month = (saveDate >> 16) & 0xFF;
int year = saveDate & 0xFFFF;
- desc.setSaveDate(year, month, day);
+ descriptor.setSaveDate(year, month, day);
int hour = (saveTime >> 8) & 0xFF;
int minutes = saveTime & 0xFF;
- desc.setSaveTime(hour, minutes);
+ descriptor.setSaveTime(hour, minutes);
}
-
delete in;
- return desc;
+ return descriptor;
+
} else {
SaveStateDescriptor emptySave;
// Do not allow save slot 0 (used for auto-saving) to be overwritten.
- emptySave.setWriteProtectedFlag(slot == 0);
+ if (slotNr == 0) {
+ emptySave.setWriteProtectedFlag(true);
+ } else {
+ emptySave.setWriteProtectedFlag(false);
+ }
return emptySave;
}
}
@@ -389,9 +434,9 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXX
}
if (allFiles.contains("logdir") && allFiles.contains("object") &&
- allFiles.contains("picdir") && allFiles.contains("snddir") &&
- allFiles.contains("viewdir") && allFiles.contains("vol.0") &&
- allFiles.contains("words.tok")) { // Check for v2
+ allFiles.contains("picdir") && allFiles.contains("snddir") &&
+ allFiles.contains("viewdir") && allFiles.contains("vol.0") &&
+ allFiles.contains("words.tok")) { // Check for v2
// The default AGI interpreter version 0x2917 is okay for v2 games
// so we don't have to change it here.
@@ -423,7 +468,7 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXX
strncpy(name, f->_key.c_str(), MIN((uint)8, f->_key.size() > 5 ? f->_key.size() - 5 : f->_key.size()));
if (allFiles.contains("object") && allFiles.contains("words.tok") &&
- allFiles.contains(Common::String(name) + "dir")) {
+ allFiles.contains(Common::String(name) + "dir")) {
matchedUsingFilenames = true;
description = "Unknown v3 Game";
g_fallbackDesc.version = 0x3149; // Set the default AGI version for an AGI v3 game
@@ -528,14 +573,39 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXX
namespace Agi {
bool AgiBase::canLoadGameStateCurrently() {
- return (!(getGameType() == GType_PreAGI) && getflag(fMenusWork) && !_noSaveLoadAllowed);
+ if (!(getGameType() == GType_PreAGI)) {
+ if (getFlag(VM_FLAG_MENUS_ACCESSIBLE)) {
+ if (!_noSaveLoadAllowed) {
+ if (!cycleInnerLoopIsActive()) {
+ // We can't allow to restore a game, while inner loop is active
+ // For example Mixed Up Mother Goose has an endless loop for user name input
+ // Which means even if we abort the inner loop, the game would keep on calling
+ // GetString() until something is entered. And this would of course also happen
+ // right after restoring a saved game.
+ return true;
+ }
+ }
+ }
+ }
+ return false;
}
bool AgiBase::canSaveGameStateCurrently() {
if (getGameID() == GID_BC) // Technically in Black Cauldron we may save anytime
return true;
- return (!(getGameType() == GType_PreAGI) && getflag(fMenusWork) && !_noSaveLoadAllowed && _game.inputEnabled);
+ if (!(getGameType() == GType_PreAGI)) {
+ if (getFlag(VM_FLAG_MENUS_ACCESSIBLE)) {
+ if (!_noSaveLoadAllowed) {
+ if (!cycleInnerLoopIsActive()) {
+ if (promptIsEnabled()) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
}
int AgiEngine::agiDetectGame() {