diff --git a/engines/toltecs/detection.cpp b/engines/toltecs/detection.cpp index 39915a55a2a..9128ce53a8c 100644 --- a/engines/toltecs/detection.cpp +++ b/engines/toltecs/detection.cpp @@ -56,7 +56,7 @@ namespace Toltecs { static const ToltecsGameDescription gameDescriptions[] = { { - // Toltecs English version + // 3 Skulls of the Toltecs English version { "toltecs", 0, @@ -67,8 +67,8 @@ static const ToltecsGameDescription gameDescriptions[] = { }, }, - { + // 3 Skulls of the Toltecs Russian version { "toltecs", 0, @@ -115,9 +115,30 @@ public: return "Toltecs Engine (C) 1996"; } + virtual bool hasFeature(MetaEngineFeature f) const; virtual bool createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const; + SaveStateList listSaves(const char *target) const; + virtual int getMaximumSaveSlot() const; + void removeSaveState(const char *target, int slot) const; + SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; }; +bool ToltecsMetaEngine::hasFeature(MetaEngineFeature f) const { + return + (f == kSupportsListSaves) || +// (f == kSupportsLoadingDuringStartup) || +// (f == kSupportsDeleteSave) || + (f == kSavesSupportMetaInfo) || + (f == kSavesSupportThumbnail); +} + +bool Toltecs::ToltecsEngine::hasFeature(EngineFeature f) const { + return +// (f == kSupportsRTL) || + (f == kSupportsLoadingDuringRuntime) || + (f == kSupportsSavingDuringRuntime); +} + bool ToltecsMetaEngine::createInstance(OSystem *syst, Engine **engine, const Common::ADGameDescription *desc) const { const Toltecs::ToltecsGameDescription *gd = (const Toltecs::ToltecsGameDescription *)desc; if (gd) { @@ -126,6 +147,100 @@ bool ToltecsMetaEngine::createInstance(OSystem *syst, Engine **engine, const Com return gd != 0; } +SaveStateList ToltecsMetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Toltecs::ToltecsEngine::SaveHeader header; + Common::String pattern = target; + pattern += ".???"; + + Common::StringList filenames; + filenames = saveFileMan->listSavefiles(pattern.c_str()); + Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) + + SaveStateList saveList; + for (Common::StringList::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 slotNum = atoi(file->c_str() + file->size() - 3); + + if (slotNum >= 0 && slotNum <= 999) { + Common::InSaveFile *in = saveFileMan->openForLoading(file->c_str()); + if (in) { + if (Toltecs::ToltecsEngine::readSaveHeader(in, false, header) == Toltecs::ToltecsEngine::kRSHENoError) { + saveList.push_back(SaveStateDescriptor(slotNum, header.description)); + } + delete in; + } + } + } + + return saveList; +} + +int ToltecsMetaEngine::getMaximumSaveSlot() const { + return 999; +} + +void ToltecsMetaEngine::removeSaveState(const char *target, int slot) const { + // Slot 0 can't be deleted, it's for restarting the game(s) + if (slot == 0) + return; + + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::String filename = Toltecs::ToltecsEngine::getSavegameFilename(target, slot); + + saveFileMan->removeSavefile(filename.c_str()); + + Common::StringList filenames; + Common::String pattern = target; + pattern += ".???"; + filenames = saveFileMan->listSavefiles(pattern.c_str()); + Common::sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) + + for (Common::StringList::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 slotNum = atoi(file->c_str() + file->size() - 3); + + // Rename every slot greater than the deleted slot, + // Also do not rename quicksaves. + if (slotNum > slot && slotNum < 990) { + // FIXME: Our savefile renaming done here is inconsitent with what we do in + // GUI_v2::deleteMenu. While here we rename every slot with a greater equal + // number of the deleted slot to deleted slot, deleted slot + 1 etc., + // we only rename the following slots in GUI_v2::deleteMenu until a slot + // is missing. + saveFileMan->renameSavefile(file->c_str(), filename.c_str()); + + filename = Toltecs::ToltecsEngine::getSavegameFilename(target, ++slot); + } + } + +} + +SaveStateDescriptor ToltecsMetaEngine::querySaveMetaInfos(const char *target, int slot) const { + Common::String filename = Toltecs::ToltecsEngine::getSavegameFilename(target, slot); + Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(filename.c_str()); + + if (in) { + Toltecs::ToltecsEngine::SaveHeader header; + Toltecs::ToltecsEngine::kReadSaveHeaderError error; + + error = Toltecs::ToltecsEngine::readSaveHeader(in, true, header); + delete in; + + if (error == Toltecs::ToltecsEngine::kRSHENoError) { + SaveStateDescriptor desc(slot, header.description); + + desc.setDeletableFlag(false); + desc.setWriteProtectedFlag(false); + desc.setThumbnail(header.thumbnail); + + return desc; + } + } + + return SaveStateDescriptor(); +} + #if PLUGIN_ENABLED_DYNAMIC(TOLTECS) REGISTER_PLUGIN_DYNAMIC(TOLTECS, PLUGIN_TYPE_ENGINE, ToltecsMetaEngine); #else diff --git a/engines/toltecs/movie.cpp b/engines/toltecs/movie.cpp index 2813c0ea698..9cbcbb2ed6c 100644 --- a/engines/toltecs/movie.cpp +++ b/engines/toltecs/movie.cpp @@ -52,6 +52,8 @@ void MoviePlayer::playMovie(uint resIndex) { uint32 subtitleSlot; byte moviePalette[768]; + _vm->_isSaveAllowed = false; + memset(moviePalette, 0, sizeof(moviePalette)); _vm->_screen->finishTalkTextItems(); @@ -176,6 +178,8 @@ void MoviePlayer::playMovie(uint resIndex) { debug(0, "playMovie() done"); + _vm->_isSaveAllowed = true; + } void MoviePlayer::fetchAudioChunks() { diff --git a/engines/toltecs/saveload.cpp b/engines/toltecs/saveload.cpp index b631b1d529f..da62da40f4c 100644 --- a/engines/toltecs/saveload.cpp +++ b/engines/toltecs/saveload.cpp @@ -30,6 +30,8 @@ #include "base/plugins.h" #include "base/version.h" +#include "graphics/thumbnail.h" + #include "sound/mixer.h" #include "toltecs/toltecs.h" @@ -44,14 +46,43 @@ namespace Toltecs { /* TODO: - Saveload is working so far but only one slot is supported until the game menu is implemented - - Save with F6; Load with F9 + - Save with F7; Load with F9 - Saving during an animation (AnimationPlayer) is not working correctly yet - Maybe switch to SCUMM/Tinsel serialization approach? */ #define SAVEGAME_VERSION 0 // 0 is dev version until in official SVN -void ToltecsEngine::savegame(const char *filename) { +ToltecsEngine::kReadSaveHeaderError ToltecsEngine::readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header) { + + header.version = in->readUint32LE(); + if (header.version != SAVEGAME_VERSION) + return kRSHEInvalidVersion; + + byte descriptionLen = in->readByte(); + header.description = ""; + while (descriptionLen--) + header.description += (char)in->readByte(); + + if (loadThumbnail) { + header.thumbnail = new Graphics::Surface(); + assert(header.thumbnail); + if (!Graphics::loadThumbnail(*in, *header.thumbnail)) { + delete header.thumbnail; + header.thumbnail = 0; + } + } else { + Graphics::skipThumbnailHeader(*in); + } + + // Not used yet, reserved for future usage + header.gameID = in->readByte(); + header.flags = in->readUint32LE(); + + return (in->ioFailed() ? kRSHEIoError : kRSHENoError); +} + +void ToltecsEngine::savegame(const char *filename, const char *description) { Common::OutSaveFile *out; if (!(out = g_system->getSavefileManager()->openForSaving(filename))) { @@ -61,6 +92,16 @@ void ToltecsEngine::savegame(const char *filename) { out->writeUint32LE(SAVEGAME_VERSION); + byte descriptionLen = strlen(description); + out->writeByte(descriptionLen); + out->write(description, descriptionLen); + + Graphics::saveThumbnail(*out); + + // Not used yet, reserved for future usage + out->writeByte(0); + out->writeUint32LE(0); + out->writeUint16LE(_cameraX); out->writeUint16LE(_cameraY); out->writeUint16LE(_cameraHeight); @@ -99,10 +140,14 @@ void ToltecsEngine::loadgame(const char *filename) { warning("Can't open file '%s', game not loaded", filename); return; } + + SaveHeader header; + + kReadSaveHeaderError errorCode = readSaveHeader(in, false, header); - uint32 version = in->readUint32LE(); - if (version != SAVEGAME_VERSION) { - warning("Savegame '%s' too old, game not loaded (got v%d, need v%d)", filename, version, SAVEGAME_VERSION); + if (errorCode != kRSHENoError) { + warning("Error loading savegame '%s'", filename); + delete in; return; } @@ -145,4 +190,29 @@ void ToltecsEngine::loadgame(const char *filename) { } +Common::Error ToltecsEngine::loadGameState(int slot) { + const char *fileName = getSavegameFilename(slot); + loadgame(fileName); +} + +Common::Error ToltecsEngine::saveGameState(int slot, const char *description) { + const char *fileName = getSavegameFilename(slot); + savegame(fileName, description); +} + +const char *ToltecsEngine::getSavegameFilename(int num) { + static Common::String filename; + filename = getSavegameFilename(_targetName, num); + return filename.c_str(); +} + +Common::String ToltecsEngine::getSavegameFilename(const Common::String &target, int num) { + assert(num >= 0 && num <= 999); + + char extension[5]; + sprintf(extension, "%03d", num); + + return target + "." + extension; +} + } // End of namespace Toltecs diff --git a/engines/toltecs/toltecs.cpp b/engines/toltecs/toltecs.cpp index 4691d23cb23..6e659997379 100644 --- a/engines/toltecs/toltecs.cpp +++ b/engines/toltecs/toltecs.cpp @@ -79,16 +79,28 @@ ToltecsEngine::~ToltecsEngine() { delete _rnd; } -int ToltecsEngine::init() { +Common::Error ToltecsEngine::init() { // Initialize backend _system->beginGFXTransaction(); initCommonGFX(true); _system->initSize(640, 400); _system->endGFXTransaction(); - return 0; + return Common::kNoError; } -int ToltecsEngine::go() { +void ToltecsEngine::syncSoundSettings() { + /* + _music->setVolume(ConfMan.getInt("music_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, ConfMan.getInt("sfx_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); + */ +} + +Common::Error ToltecsEngine::go() { + + _isSaveAllowed = true; _quitGame = false; _counter01 = 0; @@ -140,16 +152,6 @@ int ToltecsEngine::go() { _system->showMouse(true); -//#define TEST_MOVIE -#ifdef TEST_MOVIE - _screen->registerFont(0, 0x0D); - _screen->registerFont(1, 0x0E); - //_moviePlayer->playMovie(0x000012D8); - //_moviePlayer->playMovie(0x000012D7); - //_moviePlayer->playMovie(0x); - _moviePlayer->playMovie(0x000012E0); -#endif - //#define TEST_MENU #ifdef TEST_MENU _screen->registerFont(0, 0x0D); @@ -183,7 +185,7 @@ int ToltecsEngine::go() { delete _sound; - return 0; + return Common::kNoError; } void ToltecsEngine::loadScene(uint resIndex) { @@ -259,7 +261,7 @@ void ToltecsEngine::updateInput() { // FIXME: This is just for debugging switch (event.kbd.keycode) { case Common::KEYCODE_F7: - savegame("toltecs.001"); + savegame("toltecs.001", "Quicksave"); break; case Common::KEYCODE_F9: loadgame("toltecs.001"); diff --git a/engines/toltecs/toltecs.h b/engines/toltecs/toltecs.h index b9d08c3d59b..0794a26b9dd 100644 --- a/engines/toltecs/toltecs.h +++ b/engines/toltecs/toltecs.h @@ -43,10 +43,6 @@ namespace Toltecs { -enum ToltecsGameFeatures { - GF_PACKED = (1 << 0) -}; - struct ToltecsGameDescription; class AnimationPlayer; @@ -65,14 +61,17 @@ class ToltecsEngine : public ::Engine { Common::KeyState _keyPressed; protected: - int init(); - int go(); + Common::Error init(); + Common::Error go(); // void shutdown(); public: ToltecsEngine(OSystem *syst, const ToltecsGameDescription *gameDesc); virtual ~ToltecsEngine(); + virtual bool hasFeature(EngineFeature f) const; + virtual void syncSoundSettings(); + Common::RandomSource *_rnd; const ToltecsGameDescription *_gameDescription; uint32 getFeatures() const; @@ -99,10 +98,8 @@ public: int16 findRectAtPoint(byte *rectData, int16 x, int16 y, int16 index, int16 itemSize); - void savegame(const char *filename); - void loadgame(const char *filename); - public: + AnimationPlayer *_anim; ArchiveReader *_arc; Input *_input; @@ -140,6 +137,37 @@ public: int16 _mouseDisabled; bool _leftButtonDown, _rightButtonDown; + /* Save/load */ + + enum kReadSaveHeaderError { + kRSHENoError = 0, + kRSHEInvalidType = 1, + kRSHEInvalidVersion = 2, + kRSHEIoError = 3 + }; + + struct SaveHeader { + Common::String description; + uint32 version; + byte gameID; + uint32 flags; + Graphics::Surface *thumbnail; + }; + + bool _isSaveAllowed; + + bool canLoadGameStateCurrently() { return _isSaveAllowed; } + bool canSaveGameStateCurrently() { return _isSaveAllowed; } + Common::Error loadGameState(int slot); + Common::Error saveGameState(int slot, const char *description); + void savegame(const char *filename, const char *description); + void loadgame(const char *filename); + + const char *getSavegameFilename(int num); + static Common::String getSavegameFilename(const Common::String &target, int num); + + static kReadSaveHeaderError readSaveHeader(Common::SeekableReadStream *in, bool loadThumbnail, SaveHeader &header); + }; } // End of namespace Toltecs