aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engines/startrek/action.h10
-rw-r--r--engines/startrek/common.cpp10
-rw-r--r--engines/startrek/common.h8
-rw-r--r--engines/startrek/detection.cpp132
-rw-r--r--engines/startrek/menu.cpp8
-rw-r--r--engines/startrek/module.mk1
-rw-r--r--engines/startrek/object.h12
-rw-r--r--engines/startrek/room.cpp1
-rw-r--r--engines/startrek/saveload.cpp370
-rw-r--r--engines/startrek/sound.cpp18
-rw-r--r--engines/startrek/sound.h5
-rw-r--r--engines/startrek/sprite.cpp21
-rw-r--r--engines/startrek/sprite.h6
-rw-r--r--engines/startrek/startrek.cpp15
-rw-r--r--engines/startrek/startrek.h56
15 files changed, 639 insertions, 34 deletions
diff --git a/engines/startrek/action.h b/engines/startrek/action.h
index 232dc22b35..15d3ea11f7 100644
--- a/engines/startrek/action.h
+++ b/engines/startrek/action.h
@@ -23,6 +23,7 @@
#ifndef STARTREK_ACTION_H
#define STARTREK_ACTION_H
+#include "common/serializer.h"
enum Acton {
ACTION_TICK = 0,
@@ -41,7 +42,7 @@ enum Acton {
ACTION_OPTIONS = 13 // Not really an action, but selectable from action menu
};
-struct Action {
+struct Action : Common::Serializable {
byte type;
byte b1;
byte b2;
@@ -79,6 +80,13 @@ struct Action {
uint32 toUint32() const {
return (type << 24) | (b1 << 16) | (b2 << 8) | (b3 << 0);
}
+
+ virtual void saveLoadWithSerializer(Common::Serializer &ser) {
+ ser.syncAsByte(type);
+ ser.syncAsByte(b1);
+ ser.syncAsByte(b2);
+ ser.syncAsByte(b3);
+ }
};
#endif
diff --git a/engines/startrek/common.cpp b/engines/startrek/common.cpp
index cef01c5fae..a742860c32 100644
--- a/engines/startrek/common.cpp
+++ b/engines/startrek/common.cpp
@@ -19,6 +19,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "common/rect.h"
+#include "common/serializer.h"
+
#include "startrek/common.h"
namespace StarTrek {
@@ -32,4 +35,11 @@ Common::Rect getRectEncompassing(Common::Rect r1, Common::Rect r2) {
return Common::Rect(l,t,r,b);
}
+void serializeRect(Common::Rect rect, Common::Serializer &ser) {
+ ser.syncAsSint16LE(rect.left);
+ ser.syncAsSint16LE(rect.top);
+ ser.syncAsSint16LE(rect.right);
+ ser.syncAsSint16LE(rect.bottom);
+}
+
}
diff --git a/engines/startrek/common.h b/engines/startrek/common.h
index c5e3efea04..e4930b6f05 100644
--- a/engines/startrek/common.h
+++ b/engines/startrek/common.h
@@ -22,7 +22,12 @@
#ifndef STARTREK_COMMON_H
#define STARTREK_COMMON_H
-#include "common/rect.h"
+#include "common/scummsys.h"
+
+namespace Common {
+class Rect;
+class Serializer;
+}
namespace StarTrek {
@@ -33,6 +38,7 @@ template<class T>
T max(T a, T b) { return a > b ? a : b; }
Common::Rect getRectEncompassing(Common::Rect r1, Common::Rect r2);
+void serializeRect(Common::Rect rect, Common::Serializer &ser);
// Fixed-point (16.16) number
diff --git a/engines/startrek/detection.cpp b/engines/startrek/detection.cpp
index d6e449e98b..3440bb4758 100644
--- a/engines/startrek/detection.cpp
+++ b/engines/startrek/detection.cpp
@@ -26,8 +26,12 @@
#include "base/plugins.h"
#include "engines/advancedDetector.h"
+
+#include "graphics/thumbnail.h"
+
#include "common/config-manager.h"
#include "common/file.h"
+#include "common/savefile.h"
#include "startrek/startrek.h"
@@ -213,9 +217,27 @@ public:
return "Star Trek: 25th Anniversary, Star Trek: Judgment Rites (C) Interplay";
}
+ virtual bool hasFeature(MetaEngineFeature f) const;
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+
+ virtual SaveStateList listSaves(const char *target) const;
+ virtual int getMaximumSaveSlot() const;
+ virtual void removeSaveState(const char *target, int slot) const;
+ SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
};
+bool StarTrekMetaEngine::hasFeature(MetaEngineFeature f) const {
+ return
+ (f == kSupportsListSaves) ||
+ (f == kSupportsLoadingDuringStartup) ||
+ (f == kSupportsDeleteSave) ||
+ (f == kSavesSupportMetaInfo) ||
+ (f == kSavesSupportThumbnail) ||
+ (f == kSavesSupportCreationDate) ||
+ (f == kSavesSupportPlayTime) ||
+ (f == kSimpleSavesNames);
+}
+
bool StarTrekMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
const StarTrek::StarTrekGameDescription *gd = (const StarTrek::StarTrekGameDescription *)desc;
@@ -224,6 +246,116 @@ bool StarTrekMetaEngine::createInstance(OSystem *syst, Engine **engine, const AD
return (gd != 0);
}
+SaveStateList StarTrekMetaEngine::listSaves(const char *target) const {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray filenames;
+ Common::String pattern = target;
+ pattern += ".###";
+
+ filenames = saveFileMan->listSavefiles(pattern);
+
+ SaveStateList saveList;
+ for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
+ // Obtain the last 3 digits of the filename, since they correspond to the save slot
+ int slotNr = atoi(file->c_str() + file->size() - 3);
+
+ if (slotNr >= 0 && slotNr <= getMaximumSaveSlot()) {
+ Common::InSaveFile *in = saveFileMan->openForLoading(*file);
+ if (in) {
+ StarTrek::SavegameMetadata meta;
+ StarTrek::saveOrLoadMetadata(in, nullptr, &meta);
+ delete in;
+
+ uint16 descriptionPos = 0;
+
+ // Security-check, if saveDescription has a terminating NUL
+ while (meta.description[descriptionPos]) {
+ descriptionPos++;
+ if (descriptionPos >= sizeof(meta.description))
+ break;
+ }
+ if (descriptionPos >= sizeof(meta.description)) {
+ strcpy(meta.description, "[broken saved game]");
+ }
+
+ saveList.push_back(SaveStateDescriptor(slotNr, meta.description));
+ }
+ }
+ }
+
+ // Sort saves based on slot number.
+ Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
+ return saveList;
+}
+
+
+int StarTrekMetaEngine::getMaximumSaveSlot() const { return 999; }
+
+void StarTrekMetaEngine::removeSaveState(const char *target, int slot) const {
+ Common::String fileName = Common::String::format("%s.%03d", target, slot);
+ g_system->getSavefileManager()->removeSavefile(fileName);
+}
+
+SaveStateDescriptor StarTrekMetaEngine::querySaveMetaInfos(const char *target, int slotNr) const {
+ Common::String fileName = Common::String::format("%s.%03d", target, slotNr);
+
+ Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(fileName);
+
+ if (in) {
+ StarTrek::SavegameMetadata meta;
+ StarTrek::saveOrLoadMetadata(in, nullptr, &meta);
+ delete in;
+
+ uint16 descriptionPos = 0;
+
+ while (meta.description[descriptionPos]) {
+ descriptionPos++;
+ if (descriptionPos >= sizeof(meta.description))
+ break;
+ }
+ if (descriptionPos >= sizeof(meta.description)) {
+ // broken meta.description, ignore it
+ SaveStateDescriptor descriptor(slotNr, "[broken saved game]");
+ return descriptor;
+ }
+
+ SaveStateDescriptor descriptor(slotNr, meta.description);
+
+ // Do not allow save slot 0 (used for auto-saving) to be deleted or
+ // overwritten.
+ if (slotNr == 0) {
+ descriptor.setWriteProtectedFlag(true);
+ descriptor.setDeletableFlag(false);
+ } else {
+ descriptor.setWriteProtectedFlag(false);
+ descriptor.setDeletableFlag(true);
+ }
+
+ if (meta.thumbnail == nullptr) {
+ return SaveStateDescriptor();
+ }
+
+ descriptor.setThumbnail(meta.thumbnail);
+ descriptor.setPlayTime(meta.playTime);
+ descriptor.setSaveDate(meta.getYear(), meta.getMonth(), meta.getDay());
+ descriptor.setSaveTime(meta.getHour(), meta.getMinute());
+
+ return descriptor;
+
+ } else {
+ SaveStateDescriptor emptySave;
+ // Do not allow save slot 0 (used for auto-saving) to be overwritten.
+ if (slotNr == 0) {
+ emptySave.setWriteProtectedFlag(true);
+ } else {
+ emptySave.setWriteProtectedFlag(false);
+ }
+ return emptySave;
+ }
+}
+
+
+
#if PLUGIN_ENABLED_DYNAMIC(STARTREK)
REGISTER_PLUGIN_DYNAMIC(STARTREK, PLUGIN_TYPE_ENGINE, StarTrekMetaEngine);
#else
diff --git a/engines/startrek/menu.cpp b/engines/startrek/menu.cpp
index 1f67fb288c..84f6f28169 100644
--- a/engines/startrek/menu.cpp
+++ b/engines/startrek/menu.cpp
@@ -818,14 +818,6 @@ void StarTrekEngine::chooseMouseBitmapForAction(int action, bool withRedOutline)
_gfx->setMouseBitmap(_gfx->loadBitmap(bitmapName));
}
-void StarTrekEngine::showSaveMenu() {
- // TODO
-}
-
-void StarTrekEngine::showLoadMenu() {
- // TODO
-}
-
void StarTrekEngine::showQuitGamePrompt(int x, int y) {
const char *options[] = {
"Quit Game",
diff --git a/engines/startrek/module.mk b/engines/startrek/module.mk
index 040091ddc2..f061ac3d29 100644
--- a/engines/startrek/module.mk
+++ b/engines/startrek/module.mk
@@ -14,6 +14,7 @@ MODULE_OBJS = \
menu.o \
object.o \
room.o \
+ saveload.o \
sound.o \
sprite.o \
startrek.o \
diff --git a/engines/startrek/object.h b/engines/startrek/object.h
index 888289249d..6a7911636f 100644
--- a/engines/startrek/object.h
+++ b/engines/startrek/object.h
@@ -74,11 +74,11 @@ enum Objects {
struct Actor {
- uint16 spriteDrawn;
- char animationString3[16];
+ bool spriteDrawn;
+ char animFilename[16];
uint16 animType;
Sprite sprite;
- char animationString4[10];
+ char bitmapFilename[10];
Fixed16 scale;
SharedPtr<FileStream> animFile;
uint16 numAnimFrames;
@@ -115,8 +115,10 @@ struct Actor {
uint16 field94;
uint16 field96;
- char animationString[9];
- uint8 fielda1;
+
+ char animationString[10];
+
+ // These might be part of "animationString"?
uint16 fielda2;
uint16 fielda4;
uint16 fielda6;
diff --git a/engines/startrek/room.cpp b/engines/startrek/room.cpp
index 2a8f5e6c6f..a2cc0d27b5 100644
--- a/engines/startrek/room.cpp
+++ b/engines/startrek/room.cpp
@@ -319,6 +319,7 @@ void Room::endMission(int16 score, int16 arg1, int16 arg2) {
void Room::showGameOverMenu() {
_vm->showGameOverMenu();
+ // TODO: shouldn't do this within a room
}
void Room::playVoc(Common::String filename) {
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;
+}
+
+}
diff --git a/engines/startrek/sound.cpp b/engines/startrek/sound.cpp
index f3447462b7..46867961ae 100644
--- a/engines/startrek/sound.cpp
+++ b/engines/startrek/sound.cpp
@@ -79,6 +79,12 @@ Sound::~Sound() {
}
+void Sound::clearAllMidiSlots() {
+ for (int i=0; i<8; i++) {
+ clearMidiSlot(i);
+ }
+}
+
/**
* Plays a midi track as a sound effect (one of midi slots 1-7)
*/
@@ -124,6 +130,12 @@ void Sound::playMidiTrackInSlot(int slot, int track) {
void Sound::loadMusicFile(const Common::String &baseSoundName) {
clearAllMidiSlots();
+
+ if (baseSoundName == _loadedMidiFilename)
+ return;
+
+ _loadedMidiFilename = baseSoundName;
+
/*
if (_vm->getPlatform() == Common::kPlatformAmiga)
playAmigaSound(baseSoundName);
@@ -339,12 +351,6 @@ void Sound::clearMidiSlot(int slot) {
_midiSlots[slot].track = -1;
}
-void Sound::clearAllMidiSlots() {
- for (int i=0; i<8; i++) {
- clearMidiSlot(i);
- }
-}
-
// Static callback method
void Sound::midiDriverCallback(void *data) {
Sound *s = (Sound*)data;
diff --git a/engines/startrek/sound.h b/engines/startrek/sound.h
index 7030805d08..3885d8102a 100644
--- a/engines/startrek/sound.h
+++ b/engines/startrek/sound.h
@@ -53,6 +53,7 @@ public:
Sound(StarTrekEngine *vm);
~Sound();
+ void clearAllMidiSlots();
void playMidiTrack(int track);
void playMidiTrackInSlot(int slot, int track);
void loadMusicFile(const Common::String &baseSoundName);
@@ -71,13 +72,11 @@ private:
void loadPCMusicFile(const Common::String &baseSoundName);
void clearMidiSlot(int slot);
- void clearAllMidiSlots();
// MIDI-Related Variables
MidiDriver *_midiDriver;
MidiPlaybackSlot _midiSlots[8]; // 0 is for music; 1-7 are for sfx
Common::List<MidiPlaybackSlot*> _midiSlotList; // Sorts midi slots by most recently used
- int _loopingMidiTrack;
byte *loadedSoundData;
uint32 _midiDevice;
@@ -89,6 +88,8 @@ private:
public:
Common::String _loopingAudioName;
+ Common::String _loadedMidiFilename;
+ int _loopingMidiTrack;
private:
// Driver callback
diff --git a/engines/startrek/sprite.cpp b/engines/startrek/sprite.cpp
index f6ba48de9d..8961f9c5e4 100644
--- a/engines/startrek/sprite.cpp
+++ b/engines/startrek/sprite.cpp
@@ -20,6 +20,7 @@
*
*/
+#include "startrek/common.h"
#include "startrek/sprite.h"
namespace StarTrek {
@@ -68,4 +69,24 @@ Common::Rect Sprite::getRect() {
return rect;
}
+void Sprite::saveLoadWithSerializer(Common::Serializer &ser) {
+ ser.syncAsSint16LE(pos.x);
+ ser.syncAsSint16LE(pos.y);
+ ser.syncAsUint16LE(drawPriority);
+ ser.syncAsUint16LE(drawPriority2);
+ ser.syncAsUint16LE(field8);
+ // Note: bitmap must be reloaded
+ ser.syncAsUint16LE(drawMode);
+ ser.syncAsUint16LE(textColor);
+ ser.syncAsUint16LE(bitmapChanged);
+ ser.syncAsUint16LE(rect2Valid);
+ ser.syncAsUint16LE(isOnScreen);
+ ser.syncAsUint16LE(field16);
+ serializeRect(lastDrawRect, ser);
+ serializeRect(drawRect, ser);
+ serializeRect(rectangle2, ser);
+ ser.syncAsSint16LE(drawX);
+ ser.syncAsSint16LE(drawY);
+}
+
}
diff --git a/engines/startrek/sprite.h b/engines/startrek/sprite.h
index e182f927d8..9b052a135b 100644
--- a/engines/startrek/sprite.h
+++ b/engines/startrek/sprite.h
@@ -30,6 +30,7 @@
#include "common/ptr.h"
#include "common/rect.h"
+#include "common/serializer.h"
#include "common/stream.h"
using Common::SharedPtr;
@@ -41,7 +42,7 @@ namespace StarTrek {
// the rectangle, but ScummVM rects are not. Functions from Trek have been adapted to use
// ScummVM's rect format. Be wary of off-by-1 errors...
-struct Sprite {
+struct Sprite : Common::Serializable {
Common::Point pos;
uint16 drawPriority;
uint16 drawPriority2; // If two sprites' drawPriorities are equal, this is checked.
@@ -65,6 +66,9 @@ struct Sprite {
void dontDrawNextFrame();
Common::Rect getRect();
+
+ /// NOTE: even after calling this, "bitmap" must be reloaded by the caller.
+ virtual void saveLoadWithSerializer(Common::Serializer &ser);
};
}
diff --git a/engines/startrek/startrek.cpp b/engines/startrek/startrek.cpp
index 96e74bf1b2..f3f14bd2bf 100644
--- a/engines/startrek/startrek.cpp
+++ b/engines/startrek/startrek.cpp
@@ -52,6 +52,7 @@ StarTrekEngine::StarTrekEngine(OSystem *syst, const StarTrekGameDescription *gam
DebugMan.addDebugChannel(kDebugSound, "sound", "Sound");
DebugMan.addDebugChannel(kDebugGraphics, "graphics", "Graphics");
+ DebugMan.addDebugChannel(kDebugSavegame, "savegame", "Savegames");
_gfx = nullptr;
_sound = nullptr;
@@ -474,8 +475,8 @@ void StarTrekEngine::updateActorAnimations() {
actor->animFile->read(animFrameFilename, 16);
sprite->setBitmap(loadAnimationFrame(animFrameFilename, actor->scale));
- memset(actor->animationString4, 0, 10);
- strncpy(actor->animationString4, animFrameFilename, 9);
+ memset(actor->bitmapFilename, 0, 10);
+ strncpy(actor->bitmapFilename, animFrameFilename, 9);
actor->animFile->seek(10 + actor->animFrame * 22, SEEK_SET);
uint16 xOffset = actor->animFile->readUint16();
@@ -588,7 +589,7 @@ void StarTrekEngine::drawActorToScreen(Actor *actor, const Common::String &_anim
Common::String animFilename = _animName;
if (_animName.hasPrefixIgnoreCase("stnd") /* && word_45d20 == -1 */) // TODO
animFilename += 'j';
- memcpy(actor->animationString3, _animName.c_str(), sizeof(actor->animationString3));
+ memcpy(actor->animFilename, _animName.c_str(), sizeof(actor->animFilename));
actor->animType = 2;
actor->animFile = loadFile(animFilename + ".anm");
@@ -611,8 +612,8 @@ void StarTrekEngine::drawActorToScreen(Actor *actor, const Common::String &_anim
_gfx->addSprite(sprite);
sprite->setBitmap(loadAnimationFrame(firstFrameFilename, scale));
- memset(actor->animationString4, 0, sizeof(char) * 10);
- strncpy(actor->animationString4, firstFrameFilename, sizeof(char) * 9);
+ memset(actor->bitmapFilename, 0, sizeof(char) * 10);
+ strncpy(actor->bitmapFilename, firstFrameFilename, sizeof(char) * 9);
actor->scale = scale;
@@ -683,8 +684,8 @@ void StarTrekEngine::updateActorPositionWhileWalking(Actor *actor, int16 x, int1
Common::String animName = Common::String::format("%s%02d", actor->animationString2, actor->field92 & 7);
actor->sprite.setBitmap(loadAnimationFrame(animName, actor->scale));
- memset(actor->animationString4, 0, 10);
- strncpy(actor->animationString4, animName.c_str(), 9);
+ memset(actor->bitmapFilename, 0, 10);
+ strncpy(actor->bitmapFilename, animName.c_str(), 9);
Sprite *sprite = &actor->sprite;
sprite->drawPriority = _gfx->getPriValue(0, y);
diff --git a/engines/startrek/startrek.h b/engines/startrek/startrek.h
index 52d23c5677..cf678ebd83 100644
--- a/engines/startrek/startrek.h
+++ b/engines/startrek/startrek.h
@@ -29,11 +29,14 @@
#include "common/random.h"
#include "common/rect.h"
#include "common/scummsys.h"
+#include "common/serializer.h"
#include "common/str.h"
#include "common/stream.h"
#include "common/system.h"
#include "common/util.h"
+#include "gui/saveload-dialog.h"
+
#include "engines/engine.h"
#include "startrek/action.h"
@@ -60,6 +63,32 @@ class Room;
typedef String (StarTrekEngine::*TextGetterFunc)(int, uintptr, String *);
+const int SAVEGAME_DESCRIPTION_LEN = 30;
+
+struct SavegameMetadata {
+ uint32 version;
+ char description[SAVEGAME_DESCRIPTION_LEN + 1];
+
+ uint32 saveDate;
+ uint16 saveTime;
+ byte saveTimeSecs;
+ uint32 playTime;
+
+ ::Graphics::Surface *thumbnail;
+
+ void setSaveTimeAndDate(TimeDate time) {
+ saveDate = ((time.tm_mday & 0xFF) << 24) | (((time.tm_mon + 1) & 0xFF) << 16) | ((time.tm_year + 1900) & 0xFFFF);
+ saveTime = ((time.tm_hour & 0xFF) << 8) | ((time.tm_min) & 0xFF);
+ saveTimeSecs = time.tm_sec & 0xFF;
+ }
+
+ int getDay() { return (saveDate >> 24) & 0xFF; }
+ int getMonth() { return (saveDate >> 16) & 0xFF; }
+ int getYear() { return saveDate & 0xFFFF; }
+ int getHour() { return (saveTime >> 8) & 0xFF; }
+ int getMinute() { return saveTime & 0xFF; }
+};
+
const int MAX_MENUBUTTONS = 32;
const int TEXTBOX_WIDTH = 26;
@@ -79,7 +108,8 @@ enum StarTrekGameFeatures {
enum kDebugLevels {
kDebugSound = 1 << 0,
- kDebugGraphics = 1 << 1
+ kDebugGraphics = 1 << 1,
+ kDebugSavegame = 2 << 1
};
enum GameMode {
@@ -209,6 +239,10 @@ private:
// Transporter room
void runTransportSequence(const Common::String &name);
+ // Bridge
+ void initBridge(bool b) {}; // TODO
+ void cleanupBridge() {}; // TODO
+
public:
StarTrekEngine(OSystem *syst, const StarTrekGameDescription *gamedesc);
virtual ~StarTrekEngine();
@@ -314,8 +348,6 @@ public:
void unloadMenuButtons();
void chooseMouseBitmapForAction(int action, bool withRedOutline);
- void showSaveMenu();
- void showLoadMenu();
void showQuitGamePrompt(int x, int y);
void showGameOverMenu();
void showTextConfigurationMenu(bool fromOptionMenu);
@@ -333,6 +365,18 @@ private:
// Saved value of StarTrekEngine::_keyboardControlsMouse when menus are up
bool _keyboardControlsMouseOutsideMenu;
+ // saveload.cpp
+public:
+ bool showSaveMenu();
+ bool showLoadMenu();
+
+ bool saveGame(int slot, Common::String desc);
+ bool loadGame(int slot);
+
+ bool saveOrLoadGameData(Common::SeekableReadStream *in, Common::WriteStream *out, SavegameMetadata *meta);
+
+ Common::String getSavegameFilename(int slotId) const;
+
// Detection related functions
public:
const StarTrekGameDescription *_gameDescription;
@@ -358,6 +402,9 @@ public:
int _gameMode;
int _lastGameMode;
+ // NOTE: this has a different meaning than the original game. When non-empty, a new
+ // room load is triggered, as opposed to original behaviour where this was only read
+ // when "loadRoom" was called.
Common::String _missionToLoad;
int _roomIndexToLoad;
int _spawnIndexToLoad;
@@ -438,6 +485,9 @@ private:
SharedPtr<Room> _room;
};
+// Static function
+bool saveOrLoadMetadata(Common::SeekableReadStream *in, Common::WriteStream *out, SavegameMetadata *meta);
+
} // End of namespace StarTrek
#endif