diff options
Diffstat (limited to 'engines/agi/detection.cpp')
-rw-r--r-- | engines/agi/detection.cpp | 183 |
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() { |