From dfe3d8b4ceab85f3831ad4fe7fb0fa587c1e67fa Mon Sep 17 00:00:00 2001 From: Paul Gilbert Date: Wed, 8 Jan 2014 23:31:34 -0500 Subject: VOYEUR: Preliminary savegame functionality --- engines/voyeur/detection.cpp | 14 +- engines/voyeur/events.h | 1 + engines/voyeur/files.h | 4 + engines/voyeur/files_threads.cpp | 11 ++ engines/voyeur/graphics.cpp | 4 + engines/voyeur/graphics.h | 6 + engines/voyeur/voyeur.cpp | 305 ++++++++++++++++++++++++++++----------- engines/voyeur/voyeur.h | 24 +++ engines/voyeur/voyeur_game.cpp | 34 ++--- 9 files changed, 304 insertions(+), 99 deletions(-) (limited to 'engines') diff --git a/engines/voyeur/detection.cpp b/engines/voyeur/detection.cpp index e9a5b8d982..10f96042e0 100644 --- a/engines/voyeur/detection.cpp +++ b/engines/voyeur/detection.cpp @@ -121,6 +121,8 @@ SaveStateList VoyeurMetaEngine::listSaves(const char *target) const { sort(filenames.begin(), filenames.end()); // Sort to get the files in numerical order SaveStateList saveList; + Voyeur::VoyeurSavegameHeader header; + for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { const char *ext = strrchr(file->c_str(), '.'); int slot = ext ? atoi(ext + 1) : -1; @@ -129,6 +131,10 @@ SaveStateList VoyeurMetaEngine::listSaves(const char *target) const { Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file); if (in) { + if (header.read(in)) { + saveList.push_back(SaveStateDescriptor(slot, header._saveName)); + header._thumbnail->free(); + } delete in; } } @@ -151,10 +157,16 @@ SaveStateDescriptor VoyeurMetaEngine::querySaveMetaInfos(const char *target, int Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(filename); if (f) { + Voyeur::VoyeurSavegameHeader header; + header.read(f); delete f; // Create the return descriptor - SaveStateDescriptor desc(slot, ""); + SaveStateDescriptor desc(slot, header._saveName); + desc.setThumbnail(header._thumbnail); + desc.setSaveDate(header._saveYear, header._saveMonth, header._saveDay); + desc.setSaveTime(header._saveHour, header._saveMinutes); + desc.setPlayTime(header._totalFrames * GAME_FRAME_TIME); return desc; } diff --git a/engines/voyeur/events.h b/engines/voyeur/events.h index 3c474aaded..753139bf35 100644 --- a/engines/voyeur/events.h +++ b/engines/voyeur/events.h @@ -141,6 +141,7 @@ public: void showCursor(); void hideCursor(); Common::Point getMousePos() { return _mousePos; } + uint32 getGameCounter() const { return _gameCounter; } void getMouseInfo(); void checkForKey(); void startCursorBlink(); diff --git a/engines/voyeur/files.h b/engines/voyeur/files.h index b1eef580d1..f14b744721 100644 --- a/engines/voyeur/files.h +++ b/engines/voyeur/files.h @@ -537,6 +537,10 @@ public: void checkForMurder(); void checkForIncriminate(); + /** + * Synchronizes the game data + */ + void synchronize(Common::Serializer &s); }; } // End of namespace Voyeur diff --git a/engines/voyeur/files_threads.cpp b/engines/voyeur/files_threads.cpp index 4348a14896..cee4e46c81 100644 --- a/engines/voyeur/files_threads.cpp +++ b/engines/voyeur/files_threads.cpp @@ -1062,6 +1062,13 @@ int ThreadResource::doApt() { PictureResource *pic; do { _vm->_voyeurArea = AREA_APARTMENT; + + if (_vm->_loadGameSlot != -1) { + // Load a savegame + _vm->loadGame(_vm->_loadGameSlot); + _vm->_loadGameSlot = -1; + } + _vm->_eventsManager.getMouseInfo(); if (!_vm->_soundManager.getVOCStatus()) { // Previous sound ended, so start up a new one @@ -1760,4 +1767,8 @@ void ThreadResource::doAptAnim(int mode) { _vm->_bVoy->getBoltGroup(0x100); } +void ThreadResource::synchronize(Common::Serializer &s) { + warning("TODO: ThreadResource::synchronize"); +} + } // End of namespace Voyeur diff --git a/engines/voyeur/graphics.cpp b/engines/voyeur/graphics.cpp index ec26017541..1e21818450 100644 --- a/engines/voyeur/graphics.cpp +++ b/engines/voyeur/graphics.cpp @@ -750,4 +750,8 @@ void GraphicsManager::drawDot() { } } +void GraphicsManager::synchronize(Common::Serializer &s) { + warning("TODO: GraphicsManager::synchronize"); +} + } // End of namespace Voyeur diff --git a/engines/voyeur/graphics.h b/engines/voyeur/graphics.h index 970e814be2..ccc20e5df9 100644 --- a/engines/voyeur/graphics.h +++ b/engines/voyeur/graphics.h @@ -26,6 +26,7 @@ #include "common/scummsys.h" #include "common/array.h" #include "common/rect.h" +#include "common/serializer.h" #include "graphics/surface.h" namespace Voyeur { @@ -117,6 +118,11 @@ public: void fadeUpICF1(int steps); void fadeDownICF(int steps); void drawDot(); + + /** + * Synchronizes the game data + */ + void synchronize(Common::Serializer &s); }; } // End of namespace Voyeur diff --git a/engines/voyeur/voyeur.cpp b/engines/voyeur/voyeur.cpp index d6b95316be..72678fa6f0 100644 --- a/engines/voyeur/voyeur.cpp +++ b/engines/voyeur/voyeur.cpp @@ -28,6 +28,9 @@ #include "common/scummsys.h" #include "common/config-manager.h" #include "common/debug-channels.h" +#include "graphics/palette.h" +#include "graphics/scaler.h" +#include "graphics/thumbnail.h" namespace Voyeur { @@ -37,8 +40,6 @@ VoyeurEngine::VoyeurEngine(OSystem *syst, const VoyeurGameDescription *gameDesc) _gameDescription(gameDesc), _randomSource("Voyeur"), _soundManager(_mixer), _defaultFontInfo(3, 0xff, 0xff, 0, 0, ALIGN_LEFT, 0, Common::Point(), 1, 1, Common::Point(1, 1), 1, 0, 0) { - DebugMan.addDebugChannel(kDebugPath, "Path", "Pathfinding debug level"); - DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts"); _bVoy = NULL; _iForceDeath = -1; _controlPtr = NULL; @@ -54,6 +55,7 @@ VoyeurEngine::VoyeurEngine(OSystem *syst, const VoyeurGameDescription *gameDesc) _timeBarVal = -1; _checkPhoneVal = 0; _voyeurArea = AREA_NONE; + _loadGameSlot = -1; initialiseManagers(); } @@ -62,39 +64,6 @@ VoyeurEngine::~VoyeurEngine() { delete _bVoy; } -Common::String VoyeurEngine::generateSaveName(int slot) { - return Common::String::format("%s.%03d", _targetName.c_str(), slot); -} - -/** - * Returns true if it is currently okay to restore a game - */ -bool VoyeurEngine::canLoadGameStateCurrently() { - return true; -} - -/** - * Returns true if it is currently okay to save the game - */ -bool VoyeurEngine::canSaveGameStateCurrently() { - return true; -} - -/** - * Load the savegame at the specified slot index - */ -Common::Error VoyeurEngine::loadGameState(int slot) { - return Common::kNoError; -} - -/** - * Save the game to the given slot index, and with the given name - */ -Common::Error VoyeurEngine::saveGameState(int slot, const Common::String &desc) { - //TODO - return Common::kNoError; -} - Common::Error VoyeurEngine::run() { ESP_Init(); globalInitBolt(); @@ -130,6 +99,12 @@ void VoyeurEngine::initialiseManagers() { void VoyeurEngine::ESP_Init() { ThreadResource::init(); + + DebugMan.addDebugChannel(kDebugPath, "Path", "Pathfinding debug level"); + DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts"); + + if (ConfMan.hasKey("save_slot")) + _loadGameSlot = ConfMan.getInt("save_slot"); } void VoyeurEngine::globalInitBolt() { @@ -173,58 +148,61 @@ bool VoyeurEngine::doHeadTitle() { // char dest[144]; _eventsManager.startMainClockInt(); + + if (_loadGameSlot == -1) { /* - // Show starting screen - if (_bVoy->getBoltGroup(0x500)) { - showConversionScreen(); - _bVoy->freeBoltGroup(0x500); + // Show starting screen + if (_bVoy->getBoltGroup(0x500)) { + showConversionScreen(); + _bVoy->freeBoltGroup(0x500); - if (shouldQuit()) - return false; - } + if (shouldQuit()) + return false; + } - if (ConfMan.getBool("copy_protection")) { - // Display lock screen - bool result = doLock(); - if (!result || shouldQuit()) - return false; - } + if (ConfMan.getBool("copy_protection")) { + // Display lock screen + bool result = doLock(); + if (!result || shouldQuit()) + return false; + } - // Show the title screen - showTitleScreen(); - if (shouldQuit()) - return false; + // Show the title screen + showTitleScreen(); + if (shouldQuit()) + return false; - // Opening - if (!_eventsManager._mouseClicked) { - doOpening(); - doTransitionCard("Saturday Afternoon", "Player's Apartment"); - _eventsManager.delayClick(90); - } else { - _eventsManager._mouseClicked = false; - } + // Opening + if (!_eventsManager._mouseClicked) { + doOpening(); + doTransitionCard("Saturday Afternoon", "Player's Apartment"); + _eventsManager.delayClick(90); + } else { + _eventsManager._mouseClicked = false; + } */ - if (_voy._field478 & 0x80) { - // Add initial game event set - if (_voy._eventCount <= 1) - _voy.addEvent(18, 1, EVTYPE_VIDEO, 33, 0, 998, -1); - if (_voy._eventCount <= 2) - _voy.addEvent(18, 2, EVTYPE_VIDEO, 41, 0, 998, -1); - if (_voy._eventCount <= 3) - _voy.addEvent(18, 3, EVTYPE_VIDEO, 47, 0, 998, -1); - if (_voy._eventCount <= 4) - _voy.addEvent(18, 4, EVTYPE_VIDEO, 53, 0, 998, -1); - if (_voy._eventCount <= 5) - _voy.addEvent(18, 5, EVTYPE_VIDEO, 46, 0, 998, -1); - if (_voy._eventCount <= 6) - _voy.addEvent(18, 6, EVTYPE_VIDEO, 50, 0, 998, -1); - if (_voy._eventCount <= 7) - _voy.addEvent(18, 7, EVTYPE_VIDEO, 40, 0, 998, -1); - if (_voy._eventCount <= 8) - _voy.addEvent(18, 8, EVTYPE_VIDEO, 43, 0, 998, -1); - if (_voy._eventCount <= 9) - _voy.addEvent(19, 1, EVTYPE_AUDIO, 20, 0, 998, -1); - } + if (_voy._field478 & 0x80) { + // Add initial game event set + if (_voy._eventCount <= 1) + _voy.addEvent(18, 1, EVTYPE_VIDEO, 33, 0, 998, -1); + if (_voy._eventCount <= 2) + _voy.addEvent(18, 2, EVTYPE_VIDEO, 41, 0, 998, -1); + if (_voy._eventCount <= 3) + _voy.addEvent(18, 3, EVTYPE_VIDEO, 47, 0, 998, -1); + if (_voy._eventCount <= 4) + _voy.addEvent(18, 4, EVTYPE_VIDEO, 53, 0, 998, -1); + if (_voy._eventCount <= 5) + _voy.addEvent(18, 5, EVTYPE_VIDEO, 46, 0, 998, -1); + if (_voy._eventCount <= 6) + _voy.addEvent(18, 6, EVTYPE_VIDEO, 50, 0, 998, -1); + if (_voy._eventCount <= 7) + _voy.addEvent(18, 7, EVTYPE_VIDEO, 40, 0, 998, -1); + if (_voy._eventCount <= 8) + _voy.addEvent(18, 8, EVTYPE_VIDEO, 43, 0, 998, -1); + if (_voy._eventCount <= 9) + _voy.addEvent(19, 1, EVTYPE_AUDIO, 20, 0, 998, -1); + } + } _voy._field472 = 140; return true; @@ -674,4 +652,169 @@ void VoyeurEngine::flipPageAndWaitForFade() { _eventsManager.delay(1); } +/*------------------------------------------------------------------------*/ + +Common::String VoyeurEngine::generateSaveName(int slot) { + return Common::String::format("%s.%03d", _targetName.c_str(), slot); +} + +/** + * Returns true if it is currently okay to restore a game + */ +bool VoyeurEngine::canLoadGameStateCurrently() { + return _voyeurArea == AREA_APARTMENT; +} + +/** + * Returns true if it is currently okay to save the game + */ +bool VoyeurEngine::canSaveGameStateCurrently() { + return _voyeurArea == AREA_APARTMENT; +} + +/** + * Load the savegame at the specified slot index + */ +Common::Error VoyeurEngine::loadGameState(int slot) { + _loadGameSlot = slot; + return Common::kNoError; +} + +void VoyeurEngine::loadGame(int slot) { + // Open up the save file + Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(g_vm->generateSaveName(slot)); + if (!saveFile) + return; + + Common::Serializer serializer(saveFile, NULL); + + // Read in the savegame header + VoyeurSavegameHeader header; + if (!header.read(saveFile)) + return; + if (header._thumbnail) + header._thumbnail->free(); + delete header._thumbnail; + + serializer.syncVersion(header._version); + synchronize(serializer); + + delete saveFile; +} + +/** + * Save the game to the given slot index, and with the given name + */ +Common::Error VoyeurEngine::saveGameState(int slot, const Common::String &desc) { + // Open the save file for writing + Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(generateSaveName(slot)); + if (!saveFile) + return Common::kCreatingFileFailed; + + // Write out the header + VoyeurSavegameHeader header; + header.write(saveFile, this, desc); + + // Set up a serializer + Common::Serializer serializer(NULL, saveFile); + + // Synchronise the data + synchronize(serializer); + + saveFile->finalize(); + delete saveFile; + + return Common::kNoError; +} + +void VoyeurEngine::synchronize(Common::Serializer &s) { + s.syncAsSint16LE(_glGoScene); + s.syncAsSint16LE(_glGoStack); + s.syncAsSint16LE(_bob); + s.syncAsSint16LE(_stampFlags); + s.syncAsSint16LE(_playStampGroupId); + s.syncAsSint16LE(_currentVocId); + s.syncAsSint16LE(_videoId); + + s.syncAsSint16LE(_iForceDeath); + s.syncAsSint16LE(_checkTransitionId); + s.syncAsSint16LE(_gameHour); + s.syncAsSint16LE(_gameMinute); + s.syncAsSint16LE(_flashTimeVal); + s.syncAsSint16LE(_flashTimeFlag); + s.syncAsSint16LE(_timeBarVal); + s.syncAsSint16LE(_checkPhoneVal); + + // Sub-systems + _voy.synchronize(s); + _graphicsManager.synchronize(s); + _mainThread->synchronize(s); +} + +/*------------------------------------------------------------------------*/ + +bool VoyeurSavegameHeader::read(Common::InSaveFile *f) { + char id[4]; + _thumbnail = NULL; + + f->read(&id[0], 4); + if (strncmp(id, "VOYR", 4)) { + warning("Invalid savegame"); + return false; + } + + _version = f->readByte(); + if (_version > VOYEUR_SAVEGAME_VERSION) + return false; + + char c; + while ((c = f->readByte()) != 0) + _saveName += c; + + // Get the thumbnail + _thumbnail = Graphics::loadThumbnail(*f); + if (!_thumbnail) + return false; + + // Read in the save datet/ime + _saveYear = f->readSint16LE(); + _saveMonth = f->readSint16LE(); + _saveDay = f->readSint16LE(); + _saveHour = f->readSint16LE(); + _saveMinutes = f->readSint16LE(); + _totalFrames = f->readUint32LE(); + + return true; +} + +void VoyeurSavegameHeader::write(Common::OutSaveFile *f, VoyeurEngine *vm, const Common::String &saveName) { + // Write ident string + f->write("VOYR", 4); + + // Write out savegame version + f->writeByte(VOYEUR_SAVEGAME_VERSION); + + // Write out savegame name + f->write(saveName.c_str(), saveName.size()); + f->writeByte(0); + + // Create a thumbnail and save it + Graphics::Surface *thumb = new Graphics::Surface(); + ::createThumbnail(thumb, (byte *)vm->_graphicsManager._screenSurface.getPixels(), + SCREEN_WIDTH, SCREEN_HEIGHT, vm->_graphicsManager._VGAColors); + Graphics::saveThumbnail(*f, *thumb); + thumb->free(); + delete thumb; + + // Write the save datet/ime + TimeDate td; + g_system->getTimeAndDate(td); + f->writeSint16LE(td.tm_year + 1900); + f->writeSint16LE(td.tm_mon + 1); + f->writeSint16LE(td.tm_mday); + f->writeSint16LE(td.tm_hour); + f->writeSint16LE(td.tm_min); + f->writeUint32LE(vm->_eventsManager.getGameCounter()); +} + } // End of namespace Voyeur diff --git a/engines/voyeur/voyeur.h b/engines/voyeur/voyeur.h index 4ee5f84b02..1622524108 100644 --- a/engines/voyeur/voyeur.h +++ b/engines/voyeur/voyeur.h @@ -33,6 +33,8 @@ #include "common/system.h" #include "common/error.h" #include "common/random.h" +#include "common/savefile.h" +#include "common/serializer.h" #include "common/util.h" #include "engines/engine.h" #include "graphics/surface.h" @@ -134,6 +136,11 @@ private: void playAVideoEvent(int eventIndex); int getChooseButton(); + + /** + * Synchronizes the game data + */ + void synchronize(Common::Serializer &s); protected: // Engine APIs virtual Common::Error run(); @@ -169,7 +176,9 @@ public: int _timeBarVal; int _checkPhoneVal; Common::Point _mansionViewPos; + ThreadResource *_mainThread; VoyeurArea _voyeurArea; + int _loadGameSlot; public: VoyeurEngine(OSystem *syst, const VoyeurGameDescription *gameDesc); virtual ~VoyeurEngine(); @@ -187,6 +196,7 @@ public: virtual bool canSaveGameStateCurrently(); virtual Common::Error loadGameState(int slot); virtual Common::Error saveGameState(int slot, const Common::String &desc); + void loadGame(int slot); void playRL2Video(const Common::String &filename); void doTransitionCard(const Common::String &time, const Common::String &location); @@ -243,6 +253,20 @@ public: Common::String getTimeOfDay(); }; +#define VOYEUR_SAVEGAME_VERSION 1 + +struct VoyeurSavegameHeader { + uint8 _version; + Common::String _saveName; + Graphics::Surface *_thumbnail; + int _saveYear, _saveMonth, _saveDay; + int _saveHour, _saveMinutes; + int _totalFrames; + + bool read(Common::InSaveFile *f); + void write(Common::OutSaveFile *f, VoyeurEngine *vm, const Common::String &saveName); +}; + } // End of namespace Voyeur #endif /* VOYEUR_VOYEUR_H */ diff --git a/engines/voyeur/voyeur_game.cpp b/engines/voyeur/voyeur_game.cpp index 2212453cdf..64af53899d 100644 --- a/engines/voyeur/voyeur_game.cpp +++ b/engines/voyeur/voyeur_game.cpp @@ -35,8 +35,8 @@ void VoyeurEngine::playStamp() { initStamp(); PtrResource *threadsList = _stampLibPtr->boltEntry(3)._ptrResource; - ThreadResource *threadP = threadsList->_entries[0]->_threadResource; - threadP->initThreadStruct(0, 0); + _mainThread = threadsList->_entries[0]->_threadResource; + _mainThread->initThreadStruct(0, 0); _voy._isAM = false; _gameHour = 9; @@ -51,23 +51,23 @@ void VoyeurEngine::playStamp() { _playStampGroupId = _currentVocId = -1; _videoId = -1; - threadP->parsePlayCommands(); + _mainThread->parsePlayCommands(); bool flag = breakFlag = (_voy._field478 & 2) != 0; switch (_voy._field470) { case 5: - buttonId = threadP->doInterface(); + buttonId = _mainThread->doInterface(); if (buttonId == -2) { - switch (threadP->doApt()) { + switch (_mainThread->doApt()) { case 0: _voy._field472 = 140; break; case 1: _voy._field478 &= ~1; _voy._field46E = true; - threadP->chooseSTAMPButton(22); + _mainThread->chooseSTAMPButton(22); _voy._field472 = 143; break; case 2: @@ -78,7 +78,7 @@ void VoyeurEngine::playStamp() { break; case 3: _voy._field478 &= ~1; - threadP->chooseSTAMPButton(21); + _mainThread->chooseSTAMPButton(21); break; case 4: breakFlag = true; @@ -93,24 +93,24 @@ void VoyeurEngine::playStamp() { break; } } else { - threadP->chooseSTAMPButton(buttonId); + _mainThread->chooseSTAMPButton(buttonId); } flag = true; break; case 6: - threadP->doRoom(); + _mainThread->doRoom(); flag = true; break; case 16: _voy._transitionId = 17; - buttonId = threadP->doApt(); + buttonId = _mainThread->doApt(); switch (buttonId) { case 1: - threadP->chooseSTAMPButton(22); + _mainThread->chooseSTAMPButton(22); flag = true; break; case 2: @@ -136,7 +136,7 @@ void VoyeurEngine::playStamp() { _voy._field478 &= ~0x10; } - threadP->chooseSTAMPButton(0); + _mainThread->chooseSTAMPButton(0); flag = true; break; @@ -158,10 +158,10 @@ void VoyeurEngine::playStamp() { if (buttonId == 4) { _voy._field470 = 131; _eventsManager.checkForKey(); - threadP->chooseSTAMPButton(buttonId); + _mainThread->chooseSTAMPButton(buttonId); flag = true; } else { - threadP->chooseSTAMPButton(buttonId); + _mainThread->chooseSTAMPButton(buttonId); _voy._field46E = true; } } @@ -194,12 +194,12 @@ void VoyeurEngine::playStamp() { // Break out of loop flag = false; - } else if (threadP->_field40 & 2) { + } else if (_mainThread->_field40 & 2) { _eventsManager.getMouseInfo(); - threadP->chooseSTAMPButton(0); + _mainThread->chooseSTAMPButton(0); flag = true; } else { - threadP->chooseSTAMPButton(0); + _mainThread->chooseSTAMPButton(0); flag = true; } } while (flag); -- cgit v1.2.3