aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/supernova2/screen.cpp4
-rw-r--r--engines/supernova2/screen.h2
-rw-r--r--engines/supernova2/state.cpp66
-rw-r--r--engines/supernova2/state.h2
-rw-r--r--engines/supernova2/supernova2.cpp181
-rw-r--r--engines/supernova2/supernova2.h6
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);