diff options
-rw-r--r-- | engines/hugo/detection.cpp | 127 | ||||
-rw-r--r-- | engines/hugo/file.cpp | 103 | ||||
-rw-r--r-- | engines/hugo/file.h | 4 | ||||
-rw-r--r-- | engines/hugo/game.h | 2 | ||||
-rw-r--r-- | engines/hugo/global.h | 3 | ||||
-rw-r--r-- | engines/hugo/hugo.cpp | 11 | ||||
-rw-r--r-- | engines/hugo/hugo.h | 44 | ||||
-rw-r--r-- | engines/hugo/parser.cpp | 6 | ||||
-rw-r--r-- | engines/hugo/parser_v1d.cpp | 10 | ||||
-rw-r--r-- | engines/hugo/parser_v1w.cpp | 4 | ||||
-rw-r--r-- | engines/hugo/parser_v2d.cpp | 10 | ||||
-rw-r--r-- | engines/hugo/parser_v3d.cpp | 10 |
12 files changed, 263 insertions, 71 deletions
diff --git a/engines/hugo/detection.cpp b/engines/hugo/detection.cpp index a4bc5a4c1c..05fe984615 100644 --- a/engines/hugo/detection.cpp +++ b/engines/hugo/detection.cpp @@ -24,6 +24,10 @@ */ #include "engines/advancedDetector.h" +#include "common/system.h" +#include "common/savefile.h" +#include "graphics/thumbnail.h" +#include "graphics/surface.h" #include "hugo/hugo.h" @@ -38,6 +42,11 @@ uint32 HugoEngine::getFeatures() const { return _gameDescription->desc.flags; } +const char *HugoEngine::getGameId() const { + return _gameDescription->desc.gameid; +} + + static const PlainGameDescriptor hugoGames[] = { // Games {"hugo1", "Hugo 1: Hugo's House of Horrors"}, @@ -162,8 +171,12 @@ public: } bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const; - bool hasFeature(MetaEngineFeature f) const; + + int getMaximumSaveSlot() const; + SaveStateList listSaves(const char *target) const; + SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; + void removeSaveState(const char *target, int slot) const; }; bool HugoMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const { @@ -175,9 +188,117 @@ bool HugoMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGame } bool HugoMetaEngine::hasFeature(MetaEngineFeature f) const { - return false; + return + (f == kSupportsListSaves) || + (f == kSupportsLoadingDuringStartup) || + (f == kSupportsDeleteSave) || + (f == kSavesSupportMetaInfo) || + (f == kSavesSupportThumbnail) || + (f == kSavesSupportCreationDate); +} + +int HugoMetaEngine::getMaximumSaveSlot() const { return 99; } + +SaveStateList HugoMetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::StringArray filenames; + Common::String pattern = target; + pattern += "-??.SAV"; + + filenames = saveFileMan->listSavefiles(pattern); + sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) + + SaveStateList saveList; + int slotNum = 0; + for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); ++filename) { + // Obtain the last 3 digits of the filename, since they correspond to the save slot + slotNum = atoi(filename->c_str() + filename->size() - 3); + + if (slotNum >= 0 && slotNum <= getMaximumSaveSlot()) { + Common::InSaveFile *file = saveFileMan->openForLoading(*filename); + if (file) { + int saveVersion = file->readByte(); + + if (saveVersion != kSavegameVersion) { + warning("Savegame of incompatible version"); + delete file; + continue; + } + + // read name + uint16 nameSize = file->readUint16BE(); + if (nameSize >= 255) { + delete file; + continue; + } + char name[256]; + file->read(name, nameSize); + name[nameSize] = 0; + + saveList.push_back(SaveStateDescriptor(slotNum, name)); + delete file; + } + } + } + + return saveList; } +SaveStateDescriptor HugoMetaEngine::querySaveMetaInfos(const char *target, int slot) const { + Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot); + Common::InSaveFile *file = g_system->getSavefileManager()->openForLoading(fileName); + + if (file) { + int saveVersion = file->readByte(); + + if (saveVersion != kSavegameVersion) { + warning("Savegame of incompatible version"); + delete file; + return SaveStateDescriptor(); + } + + uint32 saveNameLength = file->readUint16BE(); + char saveName[256]; + file->read(saveName, saveNameLength); + saveName[saveNameLength] = 0; + + SaveStateDescriptor desc(slot, saveName); + + Graphics::Surface *thumbnail = new Graphics::Surface(); + assert(thumbnail); + if (!Graphics::loadThumbnail(*file, *thumbnail)) { + delete thumbnail; + thumbnail = 0; + } + desc.setThumbnail(thumbnail); + + desc.setDeletableFlag(true); + desc.setWriteProtectedFlag(false); + + uint32 saveDate = file->readUint32BE(); + uint16 saveTime = file->readUint16BE(); + + int day = (saveDate >> 24) & 0xFF; + int month = (saveDate >> 16) & 0xFF; + int year = saveDate & 0xFFFF; + + desc.setSaveDate(year, month, day); + + int hour = (saveTime >> 8) & 0xFF; + int minutes = saveTime & 0xFF; + + desc.setSaveTime(hour, minutes); + + delete file; + return desc; + } + return SaveStateDescriptor(); +} + +void HugoMetaEngine::removeSaveState(const char *target, int slot) const { + Common::String fileName = Common::String::format("%s-%02d.SAV", target, slot); + g_system->getSavefileManager()->removeSavefile(fileName); +} } // End of namespace Hugo #if PLUGIN_ENABLED_DYNAMIC(HUGO) @@ -195,7 +316,7 @@ void HugoEngine::initGame(const HugoGameDescription *gd) { _gameVariant = _gameType - 1 + ((_platform == Common::kPlatformWindows) ? 0 : 3); // Generate filename - _saveFilename = _targetName + "-%d.SAV"; + _saveFilename = _targetName + "-%02d.SAV"; } } // End of namespace Hugo diff --git a/engines/hugo/file.cpp b/engines/hugo/file.cpp index 78bcc46e85..e34679fce3 100644 --- a/engines/hugo/file.cpp +++ b/engines/hugo/file.cpp @@ -32,6 +32,9 @@ #include "common/system.h" #include "common/savefile.h" +#include "common/config-manager.h" +#include "graphics/thumbnail.h" +#include "gui/saveload.h" #include "hugo/hugo.h" #include "hugo/file.h" @@ -299,22 +302,61 @@ bool FileManager::fileExists(char *filename) { /** * Save game to supplied slot */ -void FileManager::saveGame(int16 slot, const char *descrip) { +bool FileManager::saveGame(int16 slot, Common::String descrip) { debugC(1, kDebugFile, "saveGame(%d, %s)", slot, descrip); - // Get full path of saved game file - note test for INITFILE - Common::String path = Common::String::format(_vm->_saveFilename.c_str(), slot); - Common::WriteStream *out = _vm->getSaveFileManager()->openForSaving(path); + const EnginePlugin *plugin = NULL; + int16 savegameId; + Common::String savegameDescription; + EngineMan.findGame(_vm->getGameId(), &plugin); + + if (slot == -1) { + GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Save game:", "Save"); + dialog->setSaveMode(true); + savegameId = dialog->runModal(plugin, ConfMan.getActiveDomainName()); + savegameDescription = dialog->getResultString(); + delete dialog; + } else { + savegameId = slot; + if (!descrip.empty()) { + savegameDescription = descrip; + } else { + savegameDescription = Common::String::format("Quick save #%d", slot); + } + } + + if (savegameId < 0) // dialog aborted + return false; + + Common::String savegameFile = Common::String::format(_vm->_saveFilename.c_str(), savegameId); + Common::SaveFileManager *saveMan = g_system->getSavefileManager(); + Common::OutSaveFile *out = saveMan->openForSaving(savegameFile); + if (!out) { - warning("Can't create file '%s', game not saved", path.c_str()); - return; + warning("Can't create file '%s', game not saved", savegameFile.c_str()); + return false; } // Write version. We can't restore from obsolete versions out->writeByte(kSavegameVersion); - // Save description of saved game - out->write(descrip, DESCRIPLEN); + if (savegameDescription == "") { + savegameDescription = "Untitled savegame"; + } + + out->writeSint16BE(savegameDescription.size() + 1); + out->write(savegameDescription.c_str(), savegameDescription.size() + 1); + + Graphics::saveThumbnail(*out); + + TimeDate curTime; + _vm->_system->getTimeAndDate(curTime); + + uint32 saveDate = (curTime.tm_mday & 0xFF) << 24 | ((curTime.tm_mon + 1) & 0xFF) << 16 | ((curTime.tm_year + 1900) & 0xFFFF); + uint16 saveTime = (curTime.tm_hour & 0xFF) << 8 | ((curTime.tm_min) & 0xFF); + + out->writeUint32BE(saveDate); + out->writeUint16BE(saveTime); _vm->_object->saveObjects(out); @@ -365,35 +407,57 @@ void FileManager::saveGame(int16 slot, const char *descrip) { out->finalize(); delete out; + + return true; } /** * Restore game from supplied slot number */ -void FileManager::restoreGame(int16 slot) { +bool FileManager::restoreGame(int16 slot) { debugC(1, kDebugFile, "restoreGame(%d)", slot); - // Initialize new-game status - _vm->initStatus(); + const EnginePlugin *plugin = NULL; + int16 savegameId; + EngineMan.findGame(_vm->getGameId(), &plugin); - // Get full path of saved game file - note test for INITFILE - Common::String path; // Full path of saved game + if (slot == -1) { + GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Restore game:", "Restore"); + dialog->setSaveMode(false); + savegameId = dialog->runModal(plugin, ConfMan.getActiveDomainName()); + delete dialog; + } else { + savegameId = slot; + } + + if (savegameId < 0) // dialog aborted + return false; - path = Common::String::format(_vm->_saveFilename.c_str(), slot); + Common::String savegameFile = Common::String::format(_vm->_saveFilename.c_str(), savegameId); + Common::SaveFileManager *saveMan = g_system->getSavefileManager(); + Common::InSaveFile *in = saveMan->openForLoading(savegameFile); - Common::SeekableReadStream *in = _vm->getSaveFileManager()->openForLoading(path); if (!in) - return; + return false; + + // Initialize new-game status + _vm->initStatus(); // Check version, can't restore from different versions int saveVersion = in->readByte(); if (saveVersion != kSavegameVersion) { - error("Savegame of incompatible version"); - return; + warning("Savegame of incompatible version"); + delete in; + return false; } // Skip over description - in->seek(DESCRIPLEN, SEEK_CUR); + int32 saveGameNameSize = in->readSint16BE(); + in->skip(saveGameNameSize); + + Graphics::skipThumbnail(*in); + + in->skip(6); // Skip date & time // If hero image is currently swapped, swap it back before restore if (_vm->_heroImage != HERO) @@ -446,6 +510,7 @@ void FileManager::restoreGame(int16 slot) { _maze.firstScreenIndex = in->readByte(); delete in; + return true; } /** diff --git a/engines/hugo/file.h b/engines/hugo/file.h index 1f231d0604..4dfd7167c0 100644 --- a/engines/hugo/file.h +++ b/engines/hugo/file.h @@ -63,8 +63,8 @@ public: void readImage(int objNum, object_t *objPtr); void readUIFImages(); void readUIFItem(int16 id, byte *buf); - void restoreGame(int16 slot); - void saveGame(int16 slot, const char *descrip); + bool restoreGame(int16 slot); + bool saveGame(int16 slot, Common::String descrip); virtual void openDatabaseFiles() = 0; virtual void closeDatabaseFiles() = 0; diff --git a/engines/hugo/game.h b/engines/hugo/game.h index a4f450bfbf..3c88c4cafa 100644 --- a/engines/hugo/game.h +++ b/engines/hugo/game.h @@ -836,7 +836,6 @@ struct status_t { // Game status (not saved) go_t go_for; // Purpose of an automatic route int16 go_id; // Index of exit of object walking to fpath_t path; // Alternate path for saved files - int16 saveSlot; // Current slot to save/restore game int16 song; // Current song int16 cx, cy; // Cursor position (dib coords) @@ -847,6 +846,7 @@ struct status_t { // Game status (not saved) // bool mmtimeFl; // Multimedia timer supported // int16 screenWidth; // Desktop screen width // uint32 saveTick; // Time of last save in ticks +// int16 saveSlot; // Current slot to save/restore game }; struct config_t { // User's config (saved) diff --git a/engines/hugo/global.h b/engines/hugo/global.h index 43a1e39b8f..d44b5cb44f 100644 --- a/engines/hugo/global.h +++ b/engines/hugo/global.h @@ -47,7 +47,4 @@ namespace Hugo { // User interface database (Windows Only) // This file contains, between others, the bitmaps of the fonts used in the application #define UIF_FILE "uif.dat" - -static const int kSavegameVersion = 1; - } // End of namespace Hugo diff --git a/engines/hugo/hugo.cpp b/engines/hugo/hugo.cpp index 06cd7db62b..c3e4658e8b 100644 --- a/engines/hugo/hugo.cpp +++ b/engines/hugo/hugo.cpp @@ -69,6 +69,7 @@ HugoEngine::HugoEngine(OSystem *syst, const HugoGameDescription *gd) : Engine(sy _backgroundObjectsSize(0), _screenActsSize(0), _usesSize(0) { + _system = syst; DebugMan.addDebugChannel(kDebugSchedule, "Schedule", "Script Schedule debug level"); DebugMan.addDebugChannel(kDebugEngine, "Engine", "Engine debug level"); DebugMan.addDebugChannel(kDebugDisplay, "Display", "Display debug level"); @@ -915,7 +916,6 @@ void HugoEngine::initStatus() { _status.helpFl = false; // Not calling WinHelp() _status.doQuitFl = false; _status.path[0] = 0; // Path to write files - _status.saveSlot = 0; // Slot to save/restore game // Initialize every start of new game _status.tick = 0; // Tick count @@ -934,6 +934,7 @@ void HugoEngine::initStatus() { // _status.mmtime = false; // Multimedia timer support // _status.screenWidth = 0; // Desktop screen width // _status.saveTick = 0; // Time of last save +// _status.saveSlot = 0; // Slot to save/restore game } /** @@ -1256,4 +1257,12 @@ void HugoEngine::endGame() { _status.viewState = V_EXIT; } +bool HugoEngine::canLoadGameStateCurrently() { + return true; +} + +bool HugoEngine::canSaveGameStateCurrently() { + return (_status.viewState == V_PLAY); +} + } // End of namespace Hugo diff --git a/engines/hugo/hugo.h b/engines/hugo/hugo.h index d7dd767b6b..903dd5a7bc 100644 --- a/engines/hugo/hugo.h +++ b/engines/hugo/hugo.h @@ -32,6 +32,7 @@ // This include is here temporarily while the engine is being refactored. #include "hugo/game.h" +#include "hugo/file.h" #define HUGO_DAT_VER_MAJ 0 // 1 byte #define HUGO_DAT_VER_MIN 30 // 1 byte @@ -59,6 +60,8 @@ class RandomSource; */ namespace Hugo { +static const int kSavegameVersion = 2; + enum GameType { kGameTypeNone = 0, kGameTypeHugo1, @@ -114,6 +117,8 @@ public: HugoEngine(OSystem *syst, const HugoGameDescription *gd); ~HugoEngine(); + OSystem *_system; + byte _numVariant; byte _gameVariant; byte _maxInvent; @@ -172,6 +177,7 @@ public: const HugoGameDescription *_gameDescription; uint32 getFeatures() const; + const char *HugoEngine::getGameId() const; GameType getGameType() const; Common::Platform getPlatform() const; @@ -183,17 +189,17 @@ public: return *s_Engine; } - void initGame(const HugoGameDescription *gd); - void initGamePart(const HugoGameDescription *gd); + bool canLoadGameStateCurrently(); + bool canSaveGameStateCurrently(); bool loadHugoDat(); - int getMouseX() const { - return _mouseX; - } - int getMouseY() const { - return _mouseY; - } + char *useBG(char *name); + int deltaX(int x1, int x2, int vx, int y); + int deltaY(int x1, int x2, int vy, int y); + + void initGame(const HugoGameDescription *gd); + void initGamePart(const HugoGameDescription *gd); void boundaryCollision(object_t *obj); void clearBoundary(int x1, int x2, int y); void endGame(); @@ -204,10 +210,12 @@ public: void shutdown(); void storeBoundary(int x1, int x2, int y); - char *useBG(char *name); - - int deltaX(int x1, int x2, int vx, int y); - int deltaY(int x1, int x2, int vy, int y); + int getMouseX() const { + return _mouseX; + } + int getMouseY() const { + return _mouseY; + } overlay_t &getBoundaryOverlay() { return _boundary; @@ -242,6 +250,18 @@ public: byte getIntroSize() { return _introXSize; } + Common::Error saveGameState(int slot, const char *desc) { + + return (_file->saveGame(slot, desc) ? Common::kWritingFailed : Common::kNoError); + } + + Common::Error loadGameState(int slot) { + return (_file->restoreGame(slot) ? Common::kReadingFailed : Common::kNoError); + } + + bool hasFeature(EngineFeature f) const { + return (f == kSupportsRTL) || (f == kSupportsLoadingDuringRuntime) || (f == kSupportsSavingDuringRuntime); + } FileManager *_file; Scheduler *_scheduler; diff --git a/engines/hugo/parser.cpp b/engines/hugo/parser.cpp index f23427e690..19d496dfd4 100644 --- a/engines/hugo/parser.cpp +++ b/engines/hugo/parser.cpp @@ -100,13 +100,11 @@ void Parser::keyHandler(uint16 nChar, uint16 nFlags) { gameStatus.recallFl = true; break; case Common::KEYCODE_F4: // Save game - // TODO: Add a proper screen to select saveslot if (gameStatus.viewState == V_PLAY) - _vm->_file->saveGame(gameStatus.saveSlot, "Current game"); + _vm->_file->saveGame(-1, Common::String()); break; case Common::KEYCODE_F5: // Restore game - // TODO: Add a proper screen to specify saveslot and description - _vm->_file->restoreGame(gameStatus.saveSlot); + _vm->_file->restoreGame(-1); _vm->_scheduler->restoreScreen(*_vm->_screen_p); gameStatus.viewState = V_PLAY; break; diff --git a/engines/hugo/parser_v1d.cpp b/engines/hugo/parser_v1d.cpp index 9514f88178..9aab521a18 100644 --- a/engines/hugo/parser_v1d.cpp +++ b/engines/hugo/parser_v1d.cpp @@ -320,18 +320,12 @@ void Parser_v1d::lineHandler() { if (gameStatus.gameOverFl) Utils::gameOverMsg(); else -// _vm->_file->saveOrRestore(true); - warning("STUB: saveOrRestore()"); - // HACK: Currently use Win code - _vm->_file->saveGame(gameStatus.saveSlot, "Current game"); + _vm->_file->saveGame(-1, Common::String()); return; } if (!strcmp("restore", _line)) { -// _vm->_file->saveOrRestore(false); - warning("STUB: saveOrRestore()"); - // HACK: Currently use Win code - _vm->_file->restoreGame(gameStatus.saveSlot); + _vm->_file->restoreGame(-1); _vm->_scheduler->restoreScreen(*_vm->_screen_p); gameStatus.viewState = V_PLAY; return; diff --git a/engines/hugo/parser_v1w.cpp b/engines/hugo/parser_v1w.cpp index 4bfdd281ba..236d24b61b 100644 --- a/engines/hugo/parser_v1w.cpp +++ b/engines/hugo/parser_v1w.cpp @@ -369,12 +369,12 @@ void Parser_v1w::lineHandler() { // SAVE/RESTORE if (!strcmp("save", _line) && gameStatus.viewState == V_PLAY) { - _vm->_file->saveGame(gameStatus.saveSlot, "Current game"); + _vm->_file->saveGame(-1, Common::String()); return; } if (!strcmp("restore", _line) && (gameStatus.viewState == V_PLAY || gameStatus.viewState == V_IDLE)) { - _vm->_file->restoreGame(gameStatus.saveSlot); + _vm->_file->restoreGame(-1); _vm->_scheduler->restoreScreen(*_vm->_screen_p); gameStatus.viewState = V_PLAY; return; diff --git a/engines/hugo/parser_v2d.cpp b/engines/hugo/parser_v2d.cpp index 802f69056b..a0b0db6234 100644 --- a/engines/hugo/parser_v2d.cpp +++ b/engines/hugo/parser_v2d.cpp @@ -75,19 +75,13 @@ void Parser_v2d::lineHandler() { if (gameStatus.gameOverFl) Utils::gameOverMsg(); else -// _vm->_file->saveOrRestore(true); - warning("STUB: saveOrRestore()"); - // HACK: Currently use Win code - _vm->_file->saveGame(gameStatus.saveSlot, "Current game"); + _vm->_file->saveGame(-1, Common::String()); return; } if (!strcmp("restore", _line)) { _config.soundFl = false; -// _vm->_file->saveOrRestore(false); - warning("STUB: saveOrRestore()"); - // HACK: Currently use Win code - _vm->_file->restoreGame(gameStatus.saveSlot); + _vm->_file->restoreGame(-1); _vm->_scheduler->restoreScreen(*_vm->_screen_p); gameStatus.viewState = V_PLAY; return; diff --git a/engines/hugo/parser_v3d.cpp b/engines/hugo/parser_v3d.cpp index 54c3e830ce..ca78307a44 100644 --- a/engines/hugo/parser_v3d.cpp +++ b/engines/hugo/parser_v3d.cpp @@ -127,19 +127,13 @@ void Parser_v3d::lineHandler() { if (gameStatus.gameOverFl) Utils::gameOverMsg(); else -// _vm->_file->saveOrRestore(true); - warning("STUB: saveOrRestore()"); - // HACK: Currently use Win code - _vm->_file->saveGame(gameStatus.saveSlot, "Current game"); + _vm->_file->saveGame(-1, Common::String()); return; } if (!strcmp("restore", _line)) { _config.soundFl = false; -// _vm->_file->saveOrRestore(false); - warning("STUB: saveOrRestore()"); - // HACK: Currently use Win code - _vm->_file->restoreGame(gameStatus.saveSlot); + _vm->_file->restoreGame(-1); _vm->_scheduler->restoreScreen(*_vm->_screen_p); gameStatus.viewState = V_PLAY; return; |