diff options
-rw-r--r-- | engines/supernova2/screen.cpp | 4 | ||||
-rw-r--r-- | engines/supernova2/screen.h | 2 | ||||
-rw-r--r-- | engines/supernova2/state.cpp | 66 | ||||
-rw-r--r-- | engines/supernova2/state.h | 2 | ||||
-rw-r--r-- | engines/supernova2/supernova2.cpp | 181 | ||||
-rw-r--r-- | engines/supernova2/supernova2.h | 6 |
6 files changed, 253 insertions, 8 deletions
diff --git a/engines/supernova2/screen.cpp b/engines/supernova2/screen.cpp index 5e27569785..9f04defb00 100644 --- a/engines/supernova2/screen.cpp +++ b/engines/supernova2/screen.cpp @@ -402,9 +402,9 @@ void Screen::saveScreen(int x, int y, int width, int height) { _screenBuffer.push(x, y, width, height); } -/*void Screen::saveScreen(const GuiElement &guiElement) { +void Screen::saveScreen(const GuiElement &guiElement) { saveScreen(guiElement.left, guiElement.top, guiElement.width(), guiElement.height()); -}*/ +} void Screen::restoreScreen() { _screenBuffer.restore(); diff --git a/engines/supernova2/screen.h b/engines/supernova2/screen.h index a35223c7f3..e952006beb 100644 --- a/engines/supernova2/screen.h +++ b/engines/supernova2/screen.h @@ -149,7 +149,7 @@ public: void renderImage(int section); bool setCurrentImage(int filenumber); void saveScreen(int x, int y, int width, int height); - //void saveScreen(const GuiElement &guiElement); + void saveScreen(const GuiElement &guiElement); void restoreScreen(); void renderRoom(Room &room); void renderMessage(const char *text, MessagePosition position = kMessageNormal); diff --git a/engines/supernova2/state.cpp b/engines/supernova2/state.cpp index 97c5f2a6dd..a0c1ef78b1 100644 --- a/engines/supernova2/state.cpp +++ b/engines/supernova2/state.cpp @@ -31,6 +31,69 @@ namespace Supernova2 { +bool GameManager::serialize(Common::WriteStream *out) { + if (out->err()) + return false; + + // GameState + out->writeSint16LE(_state._money); + out->writeByte(_state._addressKnown); + + // Inventory + out->writeSint32LE(_inventory.getSize()); + out->writeSint32LE(_inventoryScroll); + for (int i = 0; i < _inventory.getSize(); ++i) { + Object *objectStateBegin = _rooms[_inventory.get(i)->_roomId]->getObject(0); + byte objectIndex = _inventory.get(i) - objectStateBegin; + out->writeSint32LE(_inventory.get(i)->_roomId); + out->writeSint32LE(objectIndex); + } + + // Rooms + out->writeByte(_currentRoom->getId()); + for (int i = 0; i < 3; ++i) { + _rooms[i]->serialize(out); + } + + return !out->err(); +} + + +bool GameManager::deserialize(Common::ReadStream *in, int version) { + if (in->err()) + return false; + + // GameState + _state._money = in->readSint16LE(); + _state._addressKnown = in->readByte(); + _vm->setGameString(kStringMoney, Common::String::format("%d Xa", _state._money)); + + _oldTime = g_system->getMillis(); + + // Inventory + int inventorySize = in->readSint32LE(); + _inventoryScroll = in->readSint32LE(); + _inventory.clear(); + for (int i = 0; i < inventorySize; ++i) { + RoomId objectRoom = static_cast<RoomId>(in->readSint32LE()); + int objectIndex = in->readSint32LE(); + _inventory.add(*_rooms[objectRoom]->getObject(objectIndex)); + } + + // Rooms + RoomId curRoomId = static_cast<RoomId>(in->readByte()); + for (int i = 0; i < 3; ++i) { + _rooms[i]->deserialize(in, version); + } + changeRoom(curRoomId); + + // Some additional variables + _state._previousRoom = _rooms[INTRO]; + _guiEnabled = true; + _animationEnabled = true; + + return !in->err(); +} StringId GameManager::guiCommands[] = { kStringCommandGo, kStringCommandLook, kStringCommandTake, kStringCommandOpen, kStringCommandClose, @@ -1138,6 +1201,7 @@ void GameManager::taxiPayment(int price, int destination) { } void GameManager::taxi() { + _vm->_allowSaveGame = false; static StringId dest[] = { kStringAirport, kStringDowntown, @@ -1148,7 +1212,6 @@ void GameManager::taxi() { }; Common::String input; int possibility = _taxi_possibility; - bool paid = false; _state._previousRoom = _currentRoom; _currentRoom = _rooms[INTRO]; @@ -1220,6 +1283,7 @@ void GameManager::taxi() { } _rooms[INTRO]->addAllSentences(1); } while(answer == 3 && !_vm->shouldQuit()); + _vm->_allowSaveGame = true; } } diff --git a/engines/supernova2/state.h b/engines/supernova2/state.h index 195c53616f..d26e8228d7 100644 --- a/engines/supernova2/state.h +++ b/engines/supernova2/state.h @@ -110,6 +110,8 @@ public: void processInput(Common::KeyState &state); void processInput(); void executeRoom(); + bool serialize(Common::WriteStream *out); + bool deserialize(Common::ReadStream *in, int version); static StringId guiCommands[]; static StringId guiStatusCommands[]; diff --git a/engines/supernova2/supernova2.cpp b/engines/supernova2/supernova2.cpp index 10c87ecc9e..5572ac2d56 100644 --- a/engines/supernova2/supernova2.cpp +++ b/engines/supernova2/supernova2.cpp @@ -133,7 +133,14 @@ void Supernova2Engine::init() { _gm = new GameManager(this); _screen = new Screen(this, _resMan); _console = new Console(this, _gm); + setTotalPlayTime(0); + + int saveSlot = ConfMan.getInt("save_slot"); + if (saveSlot >= 0) { + if (loadGameState(saveSlot).getCode() != Common::kNoError) + error("Failed to load save game from slot %i", saveSlot); + } } bool Supernova2Engine::hasFeature(EngineFeature f) const { @@ -236,6 +243,14 @@ bool Supernova2Engine::setCurrentImage(int filenumber) { return _screen->setCurrentImage(filenumber); } +void Supernova2Engine::saveScreen(int x, int y, int width, int height) { + _screen->saveScreen(x, y, width, height); +} + +void Supernova2Engine::saveScreen(const GuiElement &guiElement) { + _screen->saveScreen(guiElement); +} + void Supernova2Engine::restoreScreen() { _screen->restoreScreen(); } @@ -299,6 +314,13 @@ void Supernova2Engine::renderText(StringId stringId, int x, int y, byte color) { _screen->renderText(stringId, x, y, color); } +void Supernova2Engine::renderBox(int x, int y, int width, int height, byte color) { + _screen->renderBox(x, y, width, height, color); +} + +void Supernova2Engine::renderBox(const GuiElement &guiElement) { + _screen->renderBox(guiElement); +} void Supernova2Engine::paletteBrightness() { _screen->paletteBrightness(); @@ -430,12 +452,163 @@ void Supernova2Engine::setColor63(byte value) { return quit; }*/ -void Supernova2Engine::renderBox(int x, int y, int width, int height, byte color) { - _screen->renderBox(x, y, width, height, color); +bool Supernova2Engine::canLoadGameStateCurrently() { + return _allowLoadGame; } -void Supernova2Engine::renderBox(const GuiElement &guiElement) { - _screen->renderBox(guiElement); +Common::Error Supernova2Engine::loadGameState(int slot) { + return (loadGame(slot) ? Common::kNoError : Common::kReadingFailed); +} + +bool Supernova2Engine::canSaveGameStateCurrently() { + // Do not allow saving when either _allowSaveGame, _animationEnabled or _guiEnabled is false + return _allowSaveGame && _gm->_animationEnabled && _gm->_guiEnabled; +} + +Common::Error Supernova2Engine::saveGameState(int slot, const Common::String &desc) { + return (saveGame(slot, desc) ? Common::kNoError : Common::kWritingFailed); +} + +bool Supernova2Engine::serialize(Common::WriteStream *out) { + if (!_gm->serialize(out)) + return false; + out->writeByte(_screen->getGuiBrightness()); + out->writeByte(_screen->getViewportBrightness()); + return true; +} + +bool Supernova2Engine::deserialize(Common::ReadStream *in, int version) { + if (!_gm->deserialize(in, version)) + return false; + if (version >= 5) { + _screen->setGuiBrightness(in->readByte()); + _screen->setViewportBrightness(in->readByte()); + } else { + _screen->setGuiBrightness(255); + _screen->setViewportBrightness(255); + } + return true; +} + +bool Supernova2Engine::loadGame(int slot) { + if (slot < 0) + return false; + + // Make sure no message is displayed as this would otherwise delay the + // switch to the new location until a mouse click. + removeMessage(); + + if (slot == kSleepAutosaveSlot) { + if (_sleepAutoSave != nullptr && deserialize(_sleepAutoSave, _sleepAuoSaveVersion)) { + // We no longer need the sleep autosave + delete _sleepAutoSave; + _sleepAutoSave = nullptr; + return true; + } + // Old version used to save it literally in the kSleepAutosaveSlot, so + // continue to try to load it from there. + } + + Common::String filename = Common::String::format("ms2_save.%03d", slot); + Common::InSaveFile *savefile = _saveFileMan->openForLoading(filename); + if (!savefile) + return false; + + uint saveHeader = savefile->readUint32LE(); + if (saveHeader != SAVEGAME_HEADER) { + warning("No header found in '%s'", filename.c_str()); + delete savefile; + return false; //Common::kUnknownError + } + + byte saveVersion = savefile->readByte(); + if (saveVersion > SAVEGAME_VERSION) { + warning("Save game version %i not supported", saveVersion); + delete savefile; + return false; //Common::kUnknownError; + } + + int descriptionSize = savefile->readSint16LE(); + savefile->skip(descriptionSize); + savefile->skip(6); + setTotalPlayTime(savefile->readUint32LE() * 1000); + Graphics::skipThumbnail(*savefile); + if (!deserialize(savefile, saveVersion)) { + delete savefile; + return false; + }; + + // With version 9 onward the sleep auto-save is save at the end of a normal save. + delete _sleepAutoSave; + _sleepAutoSave = nullptr; + if (saveVersion >= 9) { + _sleepAuoSaveVersion = saveVersion; + byte hasAutoSave = savefile->readByte(); + if (hasAutoSave) { + _sleepAutoSave = new Common::MemoryReadWriteStream(DisposeAfterUse::YES); + uint nb; + char buf[4096]; + while ((nb = savefile->read(buf, 4096)) > 0) + _sleepAutoSave->write(buf, nb); + } + } + + delete savefile; + + return true; +} + +bool Supernova2Engine::saveGame(int slot, const Common::String &description) { + if (slot < 0) + return false; + + if (slot == kSleepAutosaveSlot) { + delete _sleepAutoSave; + _sleepAutoSave = new Common::MemoryReadWriteStream(DisposeAfterUse::YES); + _sleepAuoSaveVersion = SAVEGAME_VERSION; + serialize(_sleepAutoSave); + return true; + } + + Common::String filename = Common::String::format("ms2_save.%03d", slot); + Common::OutSaveFile *savefile = _saveFileMan->openForSaving(filename); + if (!savefile) + return false; + + savefile->writeUint32LE(SAVEGAME_HEADER); + savefile->writeByte(SAVEGAME_VERSION); + + TimeDate currentDate; + _system->getTimeAndDate(currentDate); + uint32 saveDate = (currentDate.tm_mday & 0xFF) << 24 | ((currentDate.tm_mon + 1) & 0xFF) << 16 | ((currentDate.tm_year + 1900) & 0xFFFF); + uint16 saveTime = (currentDate.tm_hour & 0xFF) << 8 | ((currentDate.tm_min) & 0xFF); + + savefile->writeSint16LE(description.size() + 1); + savefile->write(description.c_str(), description.size() + 1); + savefile->writeUint32LE(saveDate); + savefile->writeUint16LE(saveTime); + savefile->writeUint32LE(getTotalPlayTime() / 1000); + Graphics::saveThumbnail(*savefile); + serialize(savefile); + + if (_sleepAutoSave == nullptr) + savefile->writeByte(0); + else { + savefile->writeByte(1); + savefile->write(_sleepAutoSave->getData(), _sleepAutoSave->size()); + } + + savefile->finalize(); + delete savefile; + + return true; +} + +void Supernova2Engine::errorTempSave(bool saving) { + GUIErrorMessage(saving + ? "Failed to save temporary game state. Make sure your save game directory is set in ScummVM and that you can write to it." + : "Failed to load temporary game state."); + error("Unrecoverable error"); } } diff --git a/engines/supernova2/supernova2.h b/engines/supernova2/supernova2.h index 26673920fd..4fa80b5067 100644 --- a/engines/supernova2/supernova2.h +++ b/engines/supernova2/supernova2.h @@ -60,6 +60,10 @@ public: ~Supernova2Engine(); virtual Common::Error run(); + virtual Common::Error loadGameState(int slot); + virtual bool canLoadGameStateCurrently(); + virtual Common::Error saveGameState(int slot, const Common::String &desc); + virtual bool canSaveGameStateCurrently(); virtual bool hasFeature(EngineFeature f) const; GameManager *_gm; @@ -95,6 +99,8 @@ public: void renderImage(int section); void renderImage(ImageId id, bool removeImage = false); bool setCurrentImage(int filenumber); + void saveScreen(int x, int y, int width, int height); + void saveScreen(const GuiElement &guiElement); void restoreScreen(); void renderRoom(Room &room); void renderMessage(const char *text, MessagePosition position = kMessageNormal); |