aboutsummaryrefslogtreecommitdiff
path: root/engines/startrek/saveload.cpp
diff options
context:
space:
mode:
authorMatthew Stewart2018-06-05 04:18:14 -0400
committerEugene Sandulenko2018-08-09 08:37:30 +0200
commit84a30e4dce6bdaf3d40656342e53b5dcd12746be (patch)
tree32476f0e810340b5947ab44ad9b7059a391ecce0 /engines/startrek/saveload.cpp
parentc17bef8285086d438a03fcff6ae5f3d15b5532e8 (diff)
downloadscummvm-rg350-84a30e4dce6bdaf3d40656342e53b5dcd12746be.tar.gz
scummvm-rg350-84a30e4dce6bdaf3d40656342e53b5dcd12746be.tar.bz2
scummvm-rg350-84a30e4dce6bdaf3d40656342e53b5dcd12746be.zip
STARTREK: Preliminary saving/loading
Diffstat (limited to 'engines/startrek/saveload.cpp')
-rw-r--r--engines/startrek/saveload.cpp370
1 files changed, 370 insertions, 0 deletions
diff --git a/engines/startrek/saveload.cpp b/engines/startrek/saveload.cpp
new file mode 100644
index 0000000000..443d4a5446
--- /dev/null
+++ b/engines/startrek/saveload.cpp
@@ -0,0 +1,370 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "gui/saveload.h"
+
+#include "graphics/thumbnail.h"
+
+#include "common/file.h"
+#include "common/savefile.h"
+#include "common/serializer.h"
+#include "common/translation.h"
+
+#include "startrek/startrek.h"
+
+namespace StarTrek {
+
+bool StarTrekEngine::showSaveMenu() {
+ GUI::SaveLoadChooser *dialog;
+ Common::String desc;
+ int slot;
+
+ dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
+
+ slot = dialog->runModalWithCurrentTarget();
+ desc = dialog->getResultString();
+
+ if (desc.empty()) {
+ // create our own description for the saved game, the user didnt enter it
+ desc = dialog->createDefaultSaveDescription(slot);
+ }
+
+ if (desc.size() > 28)
+ desc = Common::String(desc.c_str(), 28);
+
+ /*
+ dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
+ slot = dialog->runModalWithCurrentTarget();
+ */
+
+ delete dialog;
+
+ if (slot < 0)
+ return true;
+
+ return saveGame(slot, desc);
+}
+
+bool StarTrekEngine::showLoadMenu() {
+ GUI::SaveLoadChooser *dialog;
+ int slot;
+
+ dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
+ slot = dialog->runModalWithCurrentTarget();
+
+ delete dialog;
+
+ if (slot < 0)
+ return true;
+
+ return loadGame(slot);
+}
+
+const uint32 CURRENT_SAVEGAME_VERSION = 0;
+
+bool StarTrekEngine::saveGame(int slot, Common::String desc) {
+ Common::String filename = getSavegameFilename(slot);
+ Common::OutSaveFile *out;
+
+ if (!(out = _saveFileMan->openForSaving(filename))) {
+ warning("Can't create file '%s', game not saved", filename.c_str());
+ return false;
+ } else {
+ debug(3, "Successfully opened %s for writing", filename.c_str());
+ }
+
+ SavegameMetadata meta;
+ meta.version = CURRENT_SAVEGAME_VERSION;
+ memset(meta.description, 0, sizeof(meta.description));
+ strncpy(meta.description, desc.c_str(), SAVEGAME_DESCRIPTION_LEN);
+
+ TimeDate curTime;
+ _system->getTimeAndDate(curTime);
+ meta.setSaveTimeAndDate(curTime);
+ meta.playTime = g_engine->getTotalPlayTime();
+
+ if (!saveOrLoadMetadata(nullptr, out, &meta)) {
+ delete out;
+ return false;
+ }
+ if (!saveOrLoadGameData(nullptr, out, &meta)) {
+ delete out;
+ return false;
+ }
+
+ out->finalize();
+ delete out;
+ return true;
+}
+
+bool StarTrekEngine::loadGame(int slot) {
+ Common::String filename = getSavegameFilename(slot);
+ Common::InSaveFile *in;
+
+ if (!(in = _saveFileMan->openForLoading(filename))) {
+ warning("Can't open file '%s', game not loaded", filename.c_str());
+ return false;
+ } else {
+ debug(3, "Successfully opened %s for loading", filename.c_str());
+ }
+
+ SavegameMetadata meta;
+ if (!saveOrLoadMetadata(in, nullptr, &meta)) {
+ delete in;
+ return false;
+ }
+
+ if (meta.version > CURRENT_SAVEGAME_VERSION) {
+ delete in;
+ error("Savegame version (%d) is newer than current version (%d). A newer version of ScummVM is needed", meta.version, CURRENT_SAVEGAME_VERSION);
+ }
+
+ if (!saveOrLoadGameData(in, nullptr, &meta)) {
+ delete in;
+ return false;
+ }
+
+ delete in;
+
+ _lastGameMode = _gameMode;
+
+ if (_gameMode == GAMEMODE_AWAYMISSION) {
+ for (int i = 0; i < NUM_ACTORS; i++) {
+ Actor *a = &_actorList[i];
+ if (a->spriteDrawn) {
+ if (a->animType != 1)
+ a->animFile = loadFile(Common::String(a->animFilename) + ".anm");
+ _gfx->addSprite(&a->sprite);
+ a->sprite.setBitmap(loadAnimationFrame(a->bitmapFilename, a->scale));
+ }
+ }
+ }
+ else if (_gameMode == -1) {
+ initBridge(true);
+ _lastGameMode = GAMEMODE_BRIDGE;
+ // TODO: mode change
+ }
+ else {
+ _txtFilename = _missionToLoad;
+ initBridge(false);
+ // TODO: mode change
+ }
+
+ return true;
+}
+
+/**
+ * Call this after loading "saveOrLoadMetadata" to load all the data pertaining to game
+ * execution.
+ */
+bool StarTrekEngine::saveOrLoadGameData(Common::SeekableReadStream *in, Common::WriteStream *out, SavegameMetadata *meta) {
+ Common::Serializer ser(in, out);
+
+ if (ser.isLoading()) {
+ if (_lastGameMode == GAMEMODE_BRIDGE)
+ cleanupBridge();
+ else // Assume GAMEMODE_AWAYMISSION
+ unloadRoom();
+ }
+
+ ser.syncAsUint16LE(_gameMode);
+ // TODO: sub_1d8eb (save) / sub_1d958 (load) (probably bridge / space combat state)
+
+ ser.syncString(_sound->_loadedMidiFilename);
+ ser.syncAsSint16LE(_sound->_loopingMidiTrack);
+
+ if (ser.isLoading()) {
+ if (_sound->_loadedMidiFilename.empty())
+ _sound->clearAllMidiSlots();
+ else {
+ _sound->loadMusicFile(_sound->_loadedMidiFilename);
+ _sound->playMidiMusicTracks(_sound->_loopingMidiTrack, _sound->_loopingMidiTrack);
+ }
+ }
+
+ ser.syncAsUint16LE(_frameIndex);
+ ser.syncAsUint16LE(_mouseControllingShip);
+ // TODO: word_45aa8
+ // TODO: word_45aaa
+ // TODO: word_45aac
+ // TODO: word_5082e
+ // TODO: dword_519b0
+ // TODO: word_45ab2
+ // TODO: word_45ab4
+ // TODO: word_45ab8
+
+ ser.syncString(_missionToLoad);
+ // TODO: word_4b032
+ // TODO: word_519bc
+ // TODO: word_45c5c
+ // TODO: unk_52afe
+ ser.syncString(_sound->_loopingAudioName);
+
+ if (ser.isLoading()) {
+ if (!_sound->_loopingAudioName.empty())
+ _sound->playVoc(_sound->_loopingAudioName);
+ }
+
+ // TODO: word_45a50
+
+ for (int i = 0; i < NUM_OBJECTS; i++) {
+ ser.syncAsByte(_itemList[i].have);
+ }
+
+ if (_gameMode == GAMEMODE_AWAYMISSION) {
+ ser.syncString(_missionName);
+ ser.syncAsSint16LE(_roomIndex);
+
+ if (ser.isLoading()) {
+ _gfx->fadeoutScreen();
+ _txtFilename = "ground";
+
+ // This must be done before loading the actor variables, since this clears
+ // them.
+ loadRoom(_missionName, _roomIndex);
+ }
+
+ ser.syncAsUint32LE(_roomFrameCounter);
+ ser.syncAsUint32LE(_frameIndex); // FIXME: redundant
+
+ // Serialize the "actor" class
+ for (int i = 0; i < NUM_ACTORS; i++) {
+ Actor *a = &_actorList[i];
+ ser.syncAsUint16LE(a->spriteDrawn);
+ ser.syncBytes((byte *)a->animFilename, 16);
+ ser.syncAsUint16LE(a->animType);
+
+ a->sprite.saveLoadWithSerializer(ser);
+
+ ser.syncBytes((byte *)a->bitmapFilename, 10);
+ ser.syncAsUint16LE(a->scale);
+ // Can't save "animFile" (will be reloaded)
+ ser.syncAsUint16LE(a->numAnimFrames);
+ ser.syncAsUint16LE(a->animFrame);
+ ser.syncAsUint32LE(a->frameToStartNextAnim);
+ ser.syncAsSint16LE(a->pos.x);
+ ser.syncAsSint16LE(a->pos.y);
+ ser.syncAsUint16LE(a->field60);
+ ser.syncAsUint16LE(a->field62);
+ ser.syncAsUint16LE(a->triggerActionWhenAnimFinished);
+ ser.syncAsUint16LE(a->finishedAnimActionParam);
+ ser.syncBytes((byte *)a->animationString2, 8);
+ ser.syncAsUint16LE(a->field70);
+ ser.syncAsUint16LE(a->field72);
+ ser.syncAsUint16LE(a->field74);
+ ser.syncAsUint16LE(a->field76);
+ ser.syncAsSint16LE(a->iwSrcPosition);
+ ser.syncAsSint16LE(a->iwDestPosition);
+ ser.syncAsSint32LE(a->granularPosX);
+ ser.syncAsSint32LE(a->granularPosY);
+ ser.syncAsSint32LE(a->speedX);
+ ser.syncAsSint32LE(a->speedY);
+ ser.syncAsSint16LE(a->dest.x);
+ ser.syncAsSint16LE(a->dest.y);
+ ser.syncAsUint16LE(a->field90);
+ ser.syncAsByte(a->field92);
+ ser.syncAsByte(a->direction);
+ ser.syncAsUint16LE(a->field94);
+ ser.syncAsUint16LE(a->field96);
+ ser.syncBytes((byte *)a->animationString, 10);
+ ser.syncAsUint16LE(a->fielda2);
+ ser.syncAsUint16LE(a->fielda4);
+ ser.syncAsUint16LE(a->fielda6);
+ }
+
+ ser.syncString(_mapFilename);
+ // TODO: awayMissionStruct
+
+ // The action queue
+ if (ser.isLoading()) {
+ _actionQueue = Common::Queue<Action>();
+ int16 n;
+ ser.syncAsSint16LE(n);
+ for (int i = 0; i < n; i++) {
+ Action a;
+ a.saveLoadWithSerializer(ser);
+ _actionQueue.push(a);
+ }
+ }
+ else { // Saving
+ int16 n = _actionQueue.size();
+ ser.syncAsSint16LE(n);
+ for (int i = 0; i < n; i++) {
+ Action a = _actionQueue.pop();
+ a.saveLoadWithSerializer(ser);
+ _actionQueue.push(a);
+ }
+ }
+
+ // Original game located changes in RDF files and saved them. Since RDF files
+ // aren't modified directly here, that's skipped.
+
+ ser.syncAsSint16LE(_objectHasWalkPosition);
+ ser.syncAsSint16LE(_objectWalkPosition.x);
+ ser.syncAsSint16LE(_objectWalkPosition.y);
+
+ for (int i = 0; i < MAX_BUFFERED_WALK_ACTIONS; i++) {
+ _actionOnWalkCompletion[i].saveLoadWithSerializer(ser);
+ ser.syncAsByte(_actionOnWalkCompletionInUse[i]);
+ }
+
+ ser.syncAsSint16LE(_warpHotspotsActive);
+ }
+
+ return true;
+}
+
+Common::String StarTrekEngine::getSavegameFilename(int slotId) const {
+ Common::String saveLoadSlot = _targetName;
+ saveLoadSlot += Common::String::format(".%.3d", slotId);
+ return saveLoadSlot;
+}
+
+
+// Static function (reused in detection.cpp)
+bool saveOrLoadMetadata(Common::SeekableReadStream *in, Common::WriteStream *out, SavegameMetadata *meta) {
+ Common::Serializer ser(in, out);
+
+ ser.syncAsUint32LE(meta->version);
+ ser.syncBytes((byte *)meta->description, SAVEGAME_DESCRIPTION_LEN + 1);
+
+ // Thumbnail
+ if (ser.isLoading()) {
+ if (!::Graphics::loadThumbnail(*in, meta->thumbnail))
+ meta->thumbnail = nullptr;
+ }
+ else
+ ::Graphics::saveThumbnail(*out);
+
+ // Creation date/time
+ ser.syncAsUint32LE(meta->saveDate);
+ debugC(5, kDebugSavegame, "Save date: %d", meta->saveDate);
+ ser.syncAsUint16LE(meta->saveTime);
+ debugC(5, kDebugSavegame, "Save time: %d", meta->saveTime);
+ ser.syncAsByte(meta->saveTimeSecs); // write seconds of save time as well
+ ser.syncAsUint32LE(meta->playTime);
+ debugC(5, kDebugSavegame, "Play time: %d", meta->playTime);
+
+ return true;
+}
+
+}