diff options
-rw-r--r-- | engines/startrek/action.h | 10 | ||||
-rw-r--r-- | engines/startrek/common.cpp | 10 | ||||
-rw-r--r-- | engines/startrek/common.h | 8 | ||||
-rw-r--r-- | engines/startrek/detection.cpp | 132 | ||||
-rw-r--r-- | engines/startrek/menu.cpp | 8 | ||||
-rw-r--r-- | engines/startrek/module.mk | 1 | ||||
-rw-r--r-- | engines/startrek/object.h | 12 | ||||
-rw-r--r-- | engines/startrek/room.cpp | 1 | ||||
-rw-r--r-- | engines/startrek/saveload.cpp | 370 | ||||
-rw-r--r-- | engines/startrek/sound.cpp | 18 | ||||
-rw-r--r-- | engines/startrek/sound.h | 5 | ||||
-rw-r--r-- | engines/startrek/sprite.cpp | 21 | ||||
-rw-r--r-- | engines/startrek/sprite.h | 6 | ||||
-rw-r--r-- | engines/startrek/startrek.cpp | 15 | ||||
-rw-r--r-- | engines/startrek/startrek.h | 56 |
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 |