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.cpp183
1 files changed, 137 insertions, 46 deletions
diff --git a/engines/agi/detection.cpp b/engines/agi/detection.cpp
index 823ec7be66..9f66d78d80 100644
--- a/engines/agi/detection.cpp
+++ b/engines/agi/detection.cpp
@@ -171,19 +171,39 @@ static const ADExtraGuiOptionsMap optionsList[] = {
}
},
+ {
+ GAMEOPTION_USE_HERCULES_FONT,
+ {
+ _s("Use Hercules hires font"),
+ _s("Uses Hercules hires font, when font file is available."),
+ "herculesfont",
+ false
+ }
+ },
+
+ {
+ GAMEOPTION_COMMAND_PROMPT_WINDOW,
+ {
+ _s("Pause when entering commands"),
+ _s("Shows a command prompt window and pauses the game (like in SCI) instead of a real-time prompt."),
+ "commandpromptwindow",
+ false
+ }
+ },
+
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
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) {
- _singleid = "agi";
- _guioptions = GUIO1(GUIO_NOSPEECH);
+ _singleId = "agi";
+ _guiOptions = GUIO1(GUIO_NOSPEECH);
}
virtual const char *getName() const {
@@ -205,20 +225,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,33 +274,52 @@ 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..)
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));
}
}
}
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
@@ -291,9 +330,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 +342,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 +455,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 +489,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
@@ -501,13 +567,13 @@ const ADGameDescription *AgiMetaEngine::fallbackDetect(const FileMap &allFilesXX
// Override the gameid & extra values in g_fallbackDesc.desc. This only works
// until the fallback detector is called again, and while the MetaEngine instance
// is alive (as else the string storage is modified/deleted).
- g_fallbackDesc.desc.gameid = _gameid.c_str();
+ g_fallbackDesc.desc.gameId = _gameid.c_str();
g_fallbackDesc.desc.extra = _extra.c_str();
Common::String fallbackWarning;
fallbackWarning = "Your game version has been detected using fallback matching as a\n";
- fallbackWarning += Common::String::format("variant of %s (%s).\n", g_fallbackDesc.desc.gameid, g_fallbackDesc.desc.extra);
+ fallbackWarning += Common::String::format("variant of %s (%s).\n", g_fallbackDesc.desc.gameId, g_fallbackDesc.desc.extra);
fallbackWarning += "If this is an original and unmodified version or new made Fanmade game,\n";
fallbackWarning += "please report any, information previously printed by ScummVM to the team.\n";
@@ -528,14 +594,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() {