diff options
author | Matthew Stewart | 2018-06-05 04:18:14 -0400 |
---|---|---|
committer | Eugene Sandulenko | 2018-08-09 08:37:30 +0200 |
commit | 84a30e4dce6bdaf3d40656342e53b5dcd12746be (patch) | |
tree | 32476f0e810340b5947ab44ad9b7059a391ecce0 /engines/startrek/saveload.cpp | |
parent | c17bef8285086d438a03fcff6ae5f3d15b5532e8 (diff) | |
download | scummvm-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.cpp | 370 |
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; +} + +} |