diff options
-rw-r--r-- | engines/prince/detection.cpp | 100 | ||||
-rw-r--r-- | engines/prince/detection.h | 101 | ||||
-rw-r--r-- | engines/prince/flags.h | 2 | ||||
-rw-r--r-- | engines/prince/hero.cpp | 4 | ||||
-rw-r--r-- | engines/prince/hero.h | 8 | ||||
-rw-r--r-- | engines/prince/module.mk | 3 | ||||
-rw-r--r-- | engines/prince/prince.cpp | 9 | ||||
-rw-r--r-- | engines/prince/prince.h | 15 | ||||
-rw-r--r-- | engines/prince/saveload.cpp | 508 | ||||
-rw-r--r-- | engines/prince/script.cpp | 96 | ||||
-rw-r--r-- | engines/prince/script.h | 31 |
11 files changed, 729 insertions, 148 deletions
diff --git a/engines/prince/detection.cpp b/engines/prince/detection.cpp index fa9df38c90..285d7d1ce3 100644 --- a/engines/prince/detection.cpp +++ b/engines/prince/detection.cpp @@ -20,19 +20,10 @@ * */ -#include "base/plugins.h" -#include "engines/advancedDetector.h" - -#include "prince/prince.h" +#include "prince/detection.h" namespace Prince { -struct PrinceGameDescription { - ADGameDescription desc; - - int gameType; -}; - int PrinceEngine::getGameType() const { return _gameDescription->gameType; } @@ -49,78 +40,6 @@ Common::Language PrinceEngine::getLanguage() const { return _gameDescription->desc.language; } -} - -static const PlainGameDescriptor princeGames[] = { - {"prince", "Prince Game"}, - {0, 0} -}; - -namespace Prince { - -static const PrinceGameDescription gameDescriptions[] = { - - // German - { - { - "prince", - "Galador", - AD_ENTRY1s("databank.ptc", "5fa03833177331214ec1354761b1d2ee", 3565031), - Common::DE_DEU, - Common::kPlatformWindows, - ADGF_NO_FLAGS, - GUIO1(GUIO_NONE) - }, - 0 - }, - // Polish - { - { - "prince", - "Ksiaze i Tchorz", - AD_ENTRY1s("databank.ptc", "48ec9806bda9d152acbea8ce31c93c49", 3435298), - Common::PL_POL, - Common::kPlatformWindows, - ADGF_NO_FLAGS, - GUIO1(GUIO_NONE) - }, - 1 - }, - - - { AD_TABLE_END_MARKER, 0 } -}; - -} // End of namespace Prince - -using namespace Prince; - -// we match from data too, to stop detection from a non-top-level directory -const static char *directoryGlobs[] = { - "all", - 0 -}; - -class PrinceMetaEngine : public AdvancedMetaEngine { -public: - PrinceMetaEngine() : AdvancedMetaEngine(Prince::gameDescriptions, sizeof(Prince::PrinceGameDescription), princeGames) { - _singleid = "prince"; - _maxScanDepth = 2; - _directoryGlobs = directoryGlobs; - } - - virtual const char *getName() const { - return "Prince Engine"; - } - - virtual const char *getOriginalCopyright() const { - return "Copyright (C)"; - } - - virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; - virtual bool hasFeature(MetaEngineFeature f) const; -}; - bool PrinceMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const { using namespace Prince; const PrinceGameDescription *gd = (const PrinceGameDescription *)desc; @@ -131,17 +50,26 @@ bool PrinceMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGa } bool PrinceMetaEngine::hasFeature(MetaEngineFeature f) const { - return false; + return + (f == kSupportsDeleteSave) || + (f == kSavesSupportMetaInfo) || + (f == kSavesSupportThumbnail) || + (f == kSavesSupportCreationDate) || + (f == kSupportsListSaves); } bool Prince::PrinceEngine::hasFeature(EngineFeature f) const { - return false;//(f == kSupportsRTL); + return + (f == kSupportsLoadingDuringRuntime) || + (f == kSupportsSavingDuringRuntime); } +} // End of namespace Prince + #if PLUGIN_ENABLED_DYNAMIC(PRINCE) -REGISTER_PLUGIN_DYNAMIC(PRINCE, PLUGIN_TYPE_ENGINE, PrinceMetaEngine); +REGISTER_PLUGIN_DYNAMIC(PRINCE, PLUGIN_TYPE_ENGINE, Prince::PrinceMetaEngine); #else -REGISTER_PLUGIN_STATIC(PRINCE, PLUGIN_TYPE_ENGINE, PrinceMetaEngine); +REGISTER_PLUGIN_STATIC(PRINCE, PLUGIN_TYPE_ENGINE, Prince::PrinceMetaEngine); #endif /* vim: set tabstop=4 noexpandtab: */ diff --git a/engines/prince/detection.h b/engines/prince/detection.h new file mode 100644 index 0000000000..6137730bd0 --- /dev/null +++ b/engines/prince/detection.h @@ -0,0 +1,101 @@ +/* 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. + * + */ + +#ifndef PRINCE_DETECTION_H +#define PRINCE_DETECTION_H + +#include "prince/prince.h" +#include "engines/advancedDetector.h" + +namespace Prince { + +struct PrinceGameDescription { + ADGameDescription desc; + int gameType; +}; + +static const PlainGameDescriptor princeGames[] = { + {"prince", "Prince Game"}, + {0, 0} +}; + +static const PrinceGameDescription gameDescriptions[] = { + { + { + "prince", + "Galador", + AD_ENTRY1s("databank.ptc", "5fa03833177331214ec1354761b1d2ee", 3565031), + Common::DE_DEU, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + 0 + }, + { + { + "prince", + "Ksiaze i Tchorz", + AD_ENTRY1s("databank.ptc", "48ec9806bda9d152acbea8ce31c93c49", 3435298), + Common::PL_POL, + Common::kPlatformWindows, + ADGF_NO_FLAGS, + GUIO1(GUIO_NONE) + }, + 1 + }, + { AD_TABLE_END_MARKER, 0 } +}; + +// we match from data too, to stop detection from a non-top-level directory +const static char *directoryGlobs[] = { + "all", + 0 +}; + +class PrinceMetaEngine : public AdvancedMetaEngine { +public: + PrinceMetaEngine() : AdvancedMetaEngine(Prince::gameDescriptions, sizeof(Prince::PrinceGameDescription), princeGames) { + _singleid = "prince"; + _maxScanDepth = 2; + _directoryGlobs = directoryGlobs; + } + + virtual const char *getName() const { + return "Prince Engine"; + } + + virtual const char *getOriginalCopyright() const { + return "Copyright (C)"; + } + + virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const; + virtual bool hasFeature(MetaEngineFeature f) const; + virtual int getMaximumSaveSlot() const; + virtual SaveStateList listSaves(const char *target) const; + SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const; + virtual void removeSaveState(const char *target, int slot) const; +}; + +} // End of namespace Prince + +#endif diff --git a/engines/prince/flags.h b/engines/prince/flags.h index aa607a01fe..5af6bffa00 100644 --- a/engines/prince/flags.h +++ b/engines/prince/flags.h @@ -31,7 +31,7 @@ struct Flags { // TODO: Remove from release build // useful just for debugging - static const char * getFlagName(uint16 flagId); + static const char *getFlagName(uint16 flagId); enum Id { FLAGA1 = 0x8000, diff --git a/engines/prince/hero.cpp b/engines/prince/hero.cpp index 2cc5789781..6b4cafa968 100644 --- a/engines/prince/hero.cpp +++ b/engines/prince/hero.cpp @@ -41,7 +41,7 @@ Hero::Hero(PrinceEngine *vm, GraphicsMan *graph) : _vm(vm), _graph(graph) , _shadZoomFactor(0), _shadScaleValue(0), _shadLineLen(0), _shadDrawX(0), _shadDrawY(0) , _frameXSize(0), _frameYSize(0), _scaledFrameXSize(0), _scaledFrameYSize(0), _color(0) , _coords(nullptr), _dirTab(nullptr), _currCoords(nullptr), _currDirTab(nullptr), _step(0) - , _maxBoredom(200), _turnAnim(0), _leftRightMainDir(0), _upDownMainDir(0) + , _maxBoredom(200), _turnAnim(0), _leftRightMainDir(0), _upDownMainDir(0), _animSetNr(0) { _zoomBitmap = (byte *)malloc(kZoomBitmapLen); _shadowBitmap = (byte *)malloc(2 * kShadowBitmapSize); @@ -57,6 +57,8 @@ Hero::~Hero() { } bool Hero::loadAnimSet(uint32 animSetNr) { + _animSetNr = animSetNr; + if (animSetNr > sizeof(heroSetTable)) { return false; } diff --git a/engines/prince/hero.h b/engines/prince/hero.h index d2ad0d9c95..236818c3a1 100644 --- a/engines/prince/hero.h +++ b/engines/prince/hero.h @@ -182,11 +182,11 @@ public: uint16 _currHeight; // height of current anim phase - Common::Array<int> _inventory; // Inventory array of items - Common::Array<int> _inventory2; // Inventory2 array of items + Common::Array<byte> _inventory; // Inventory array of items + Common::Array<byte> _inventory2; // Inventory2 array of items // Font subtitiles font - int _color; // Color Subtitles color - // AnimSet number of animation set + int _color; // subtitles color + uint32 _animSetNr; // number of animation set Common::Array<Animation *> _moveSet; // MoveAnims MoveSet int16 _turnAnim; byte *_zoomBitmap; diff --git a/engines/prince/module.mk b/engines/prince/module.mk index ad5b20aeb1..584fb99a97 100644 --- a/engines/prince/module.mk +++ b/engines/prince/module.mk @@ -19,7 +19,8 @@ MODULE_OBJS = \ hero.o \ hero_set.o \ cursor.o \ - pscr.o + pscr.o \ + saveload.o # This module can be built as a plugin ifeq ($(ENABLE_PRINCE), DYNAMIC_PLUGIN) diff --git a/engines/prince/prince.cpp b/engines/prince/prince.cpp index 89fb9e334e..fe2f398b85 100644 --- a/engines/prince/prince.cpp +++ b/engines/prince/prince.cpp @@ -323,7 +323,7 @@ void PrinceEngine::init() { _objSlot = new int[kMaxObjects]; for (int i = 0; i < kMaxObjects; i++) { - _objSlot[i] = -1; + _objSlot[i] = 0xFF; } } @@ -458,7 +458,7 @@ bool PrinceEngine::loadLocation(uint16 locationNr) { _mainHero->setShadowScale(_script->getShadowScale(_locationNr)); for (uint i = 0; i < _mobList.size(); i++) { - _mobList[i]._visible = _script->getMobVisible(i); + _mobList[i]._visible = _script->getMobVisible(_room->_mobs, i); } freeDrawNodes(); @@ -992,7 +992,7 @@ int PrinceEngine::checkMob(Graphics::Surface *screen, Common::Array<Mob> &mobLis //mob_obj if (mob->_mask < kMaxObjects) { int nr = _objSlot[mob->_mask]; - if (nr != -1) { + if (nr != 0xFF) { Object &obj = *_objList[nr]; Common::Rect objectRect(obj._x, obj._y, obj._x + obj._width, obj._y + obj._height); if (objectRect.contains(mousePosCamera)) { @@ -1682,7 +1682,7 @@ void PrinceEngine::freeZoomObject(int slot) { void PrinceEngine::showObjects() { for (int i = 0; i < kMaxObjects; i++) { int nr = _objSlot[i]; - if (nr != -1) { + if (nr != 0xFF) { Graphics::Surface *objSurface = nullptr; if ((_objList[nr]->_flags & 0x8000)) { _objList[nr]->_zoomTime--; @@ -4508,7 +4508,6 @@ void PrinceEngine::mainLoop() { } } } - if (_debugger->_locationNr != _locationNr) loadLocation(_debugger->_locationNr); if (_debugger->_cursorNr != _cursorNr) diff --git a/engines/prince/prince.h b/engines/prince/prince.h index fab49889bb..64f981cdb6 100644 --- a/engines/prince/prince.h +++ b/engines/prince/prince.h @@ -31,6 +31,8 @@ #include "common/rect.h" #include "common/events.h" #include "common/endian.h" +#include "common/savefile.h" +#include "common/serializer.h" #include "image/bmp.h" @@ -51,6 +53,7 @@ namespace Prince { struct PrinceGameDescription; +struct SavegameHeader; class PrinceEngine; class GraphicsMan; @@ -252,6 +255,17 @@ public: virtual ~PrinceEngine(); virtual bool hasFeature(EngineFeature f) const; + virtual bool canSaveGameStateCurrently(); + virtual bool canLoadGameStateCurrently(); + virtual Common::Error saveGameState(int slot, const Common::String &desc); + virtual Common::Error loadGameState(int slot); + + static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header); + Common::String generateSaveName(int slot); + void writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header); + void syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream); + bool loadGame(int slotNumber); + void resetGame(); int getGameType() const; const char *getGameId() const; @@ -313,6 +327,7 @@ public: static const int kMaxNormAnims = 64; static const int kMaxBackAnims = 64; static const int kMaxObjects = 64; + static const int kMaxMobs = 64; Common::Array<AnimListItem> _animList; Common::Array<BackgroundAnim> _backAnimList; diff --git a/engines/prince/saveload.cpp b/engines/prince/saveload.cpp new file mode 100644 index 0000000000..83d45a4bd7 --- /dev/null +++ b/engines/prince/saveload.cpp @@ -0,0 +1,508 @@ +/* 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 "prince/prince.h" +#include "prince/graphics.h" +#include "prince/detection.h" +#include "prince/flags.h" +#include "prince/script.h" +#include "prince/hero.h" +#include "common/savefile.h" +#include "common/system.h" +#include "common/config-manager.h" +#include "common/memstream.h" +#include "graphics/thumbnail.h" +#include "graphics/surface.h" +#include "graphics/palette.h" +#include "graphics/scaler.h" + +namespace Prince { + +#define kBadSVG 99 +#define kSavegameVersion 1 +#define kSavegameStrSize 14 +#define kSavegameStr "SCUMMVM_PRINCE" + +class InterpreterFlags; +class Interpreter; + +struct SavegameHeader { + uint8 version; + Common::String saveName; + Graphics::Surface *thumbnail; + int saveYear, saveMonth, saveDay; + int saveHour, saveMinutes; +}; + +int PrinceMetaEngine::getMaximumSaveSlot() const { + return 99; +} + +SaveStateList PrinceMetaEngine::listSaves(const char *target) const { + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::StringArray filenames; + Common::String pattern = target; + pattern += ".???"; + + filenames = saveFileMan->listSavefiles(pattern); + sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..) + + SaveStateList saveList; + for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); filename++) { + // Obtain the last 3 digits of the filename, since they correspond to the save slot + int slotNum = atoi(filename->c_str() + filename->size() - 3); + + if (slotNum >= 0 && slotNum <= 99) { + + Common::InSaveFile *file = saveFileMan->openForLoading(*filename); + if (file) { + Prince::SavegameHeader header; + + // Check to see if it's a ScummVM savegame or not + char buffer[kSavegameStrSize + 1]; + file->read(buffer, kSavegameStrSize + 1); + + if (!strncmp(buffer, kSavegameStr, kSavegameStrSize + 1)) { + // Valid savegame + if (Prince::PrinceEngine::readSavegameHeader(file, header)) { + saveList.push_back(SaveStateDescriptor(slotNum, header.saveName)); + if (header.thumbnail) { + header.thumbnail->free(); + delete header.thumbnail; + } + } + } else { + // Must be an original format savegame + saveList.push_back(SaveStateDescriptor(slotNum, "Unknown")); + } + + delete file; + } + } + } + + return saveList; +} + +SaveStateDescriptor PrinceMetaEngine::querySaveMetaInfos(const char *target, int slot) const { + Common::String fileName = Common::String::format("%s.%03d", target, slot); + Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName); + + if (f) { + Prince::SavegameHeader header; + + // Check to see if it's a ScummVM savegame or not + char buffer[kSavegameStrSize + 1]; + f->read(buffer, kSavegameStrSize + 1); + + bool hasHeader = !strncmp(buffer, kSavegameStr, kSavegameStrSize + 1) && + Prince::PrinceEngine::readSavegameHeader(f, header); + delete f; + + if (!hasHeader) { + // Original savegame perhaps? + SaveStateDescriptor desc(slot, "Unknown"); + return desc; + } else { + // Create the return descriptor + SaveStateDescriptor desc(slot, header.saveName); + desc.setThumbnail(header.thumbnail); + desc.setSaveDate(header.saveYear, header.saveMonth, header.saveDay); + desc.setSaveTime(header.saveHour, header.saveMinutes); + + return desc; + } + } + + return SaveStateDescriptor(); +} + +bool PrinceEngine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header) { + header.thumbnail = nullptr; + + // Get the savegame version + header.version = in->readByte(); + if (header.version > kSavegameVersion) + return false; + + // Read in the string + header.saveName.clear(); + char ch; + while ((ch = (char)in->readByte()) != '\0') + header.saveName += ch; + + // Get the thumbnail + header.thumbnail = Graphics::loadThumbnail(*in); + if (!header.thumbnail) + return false; + + // Read in save date/time + header.saveYear = in->readSint16LE(); + header.saveMonth = in->readSint16LE(); + header.saveDay = in->readSint16LE(); + header.saveHour = in->readSint16LE(); + header.saveMinutes = in->readSint16LE(); + + return true; +} + +void PrinceMetaEngine::removeSaveState(const char *target, int slot) const { + Common::String fileName = Common::String::format("%s.%03d", target, slot); + g_system->getSavefileManager()->removeSavefile(fileName); +} + +// TODO +bool PrinceEngine::canSaveGameStateCurrently() { + return true; +} + +// TODO +bool PrinceEngine::canLoadGameStateCurrently() { + return true; +} + +Common::Error PrinceEngine::saveGameState(int slot, const Common::String &desc) { + // Set up the serializer + Common::String slotName = generateSaveName(slot); + Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(slotName); + + // Write out the ScummVM savegame header + SavegameHeader header; + header.saveName = desc; + header.version = kSavegameVersion; + writeSavegameHeader(saveFile, header); + + // Write out the data of the savegame + syncGame(nullptr, saveFile); + + // Finish writing out game data + saveFile->finalize(); + delete saveFile; + + return Common::kNoError; +} + +Common::String PrinceEngine::generateSaveName(int slot) { + return Common::String::format("%s.%03d", _targetName.c_str(), slot); +} + +void PrinceEngine::writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header) { + // Write out a savegame header + out->write(kSavegameStr, kSavegameStrSize + 1); + + out->writeByte(kSavegameVersion); + + // Write savegame name + out->write(header.saveName.c_str(), header.saveName.size() + 1); + + // Get the active palette + uint8 thumbPalette[256 * 3]; + _system->getPaletteManager()->grabPalette(thumbPalette, 0, 256); + + // Create a thumbnail and save it + Graphics::Surface *thumb = new Graphics::Surface(); + Graphics::Surface *s = _graph->_frontScreen; // check inventory / map etc.. + ::createThumbnail(thumb, (const byte *)s->getPixels(), s->w, s->h, thumbPalette); + Graphics::saveThumbnail(*out, *thumb); + thumb->free(); + delete thumb; + + // Write out the save date/time + TimeDate td; + g_system->getTimeAndDate(td); + out->writeSint16LE(td.tm_year + 1900); + out->writeSint16LE(td.tm_mon + 1); + out->writeSint16LE(td.tm_mday); + out->writeSint16LE(td.tm_hour); + out->writeSint16LE(td.tm_min); +} + +void PrinceEngine::syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream) { + int emptyRoom = 0x00; + int normRoom = 0xFF; + byte endInv = 0xFF; + + Common::Serializer s(readStream, writeStream); + + if (s.isSaving()) { + // Flag values + for (int i = 0; i < _flags->kMaxFlags; i++) { + uint32 value = _flags->getFlagValue((Flags::Id)(_flags->kFlagMask + i)); + s.syncAsUint32LE(value); + } + + // Dialog data + for (uint32 i = 0; i < _dialogDatSize; i++) { + byte value = _dialogDat[i]; + s.syncAsByte(value); + } + + // Location number + s.syncAsUint16LE(_locationNr); + + // Rooms + for (int roomId = 0; roomId < _script->kMaxRooms; roomId++) { + Room *room = new Room(); + room->loadRoom(_script->getRoomOffset(roomId)); + + if (room->_mobs) { + s.syncAsByte(normRoom); + } else { + s.syncAsByte(emptyRoom); + delete room; + continue; + } + + // Mobs + for (int mobId = 0; mobId < kMaxMobs; mobId++) { + byte value = _script->getMobVisible(room->_mobs, mobId); + s.syncAsByte(value); + } + + // Background animations + for (int backAnimSlot = 0; backAnimSlot < kMaxBackAnims; backAnimSlot++) { + uint32 value = _script->getBackAnimId(_room->_backAnim, backAnimSlot); + s.syncAsUint32LE(value); + } + + // Objects + for (int objectSlot = 0; objectSlot < kMaxObjects; objectSlot++) { + byte value = _script->getObjId(room->_obj, objectSlot); + s.syncAsByte(value); + } + + delete room; + } + + // Main hero + s.syncAsUint16LE(_mainHero->_visible); + s.syncAsUint16LE(_mainHero->_middleX); + s.syncAsUint16LE(_mainHero->_middleY); + s.syncAsUint16LE(_mainHero->_lastDirection); + s.syncAsUint32LE(_mainHero->_color); + s.syncAsUint16LE(_mainHero->_maxBoredom); + s.syncAsUint32LE(_mainHero->_animSetNr); + + for (uint inv1Slot = 0; inv1Slot < _mainHero->_inventory.size(); inv1Slot++) { + s.syncAsByte(_mainHero->_inventory[inv1Slot]); + } + s.syncAsByte(endInv); + + for (uint inv2Slot = 0; inv2Slot < _mainHero->_inventory2.size(); inv2Slot++) { + s.syncAsByte(_mainHero->_inventory2[inv2Slot]); + } + s.syncAsByte(endInv); + + // Second hero + s.syncAsUint16LE(_secondHero->_visible); + s.syncAsUint16LE(_secondHero->_middleX); + s.syncAsUint16LE(_secondHero->_middleY); + s.syncAsUint16LE(_secondHero->_lastDirection); + s.syncAsUint32LE(_secondHero->_color); + s.syncAsUint16LE(_secondHero->_maxBoredom); + s.syncAsUint32LE(_secondHero->_animSetNr); + + for (uint inv1Slot = 0; inv1Slot < _secondHero->_inventory.size(); inv1Slot++) { + s.syncAsByte(_secondHero->_inventory[inv1Slot]); + } + s.syncAsByte(endInv); + + for (uint inv2Slot = 0; inv2Slot < _secondHero->_inventory2.size(); inv2Slot++) { + s.syncAsByte(_secondHero->_inventory2[inv2Slot]); + } + s.syncAsByte(endInv); + + } else { + // Flag values + for (int i = 0; i < _flags->kMaxFlags; i++) { + uint32 value = 0; + s.syncAsUint32LE(value); + _flags->setFlagValue((Flags::Id)(_flags->kFlagMask + i), value); + } + + // Dialog data + for (uint32 i = 0; i < _dialogDatSize; i++) { + byte value = 0; + s.syncAsByte(value); + _dialogDat[i] = value; + } + + // Location number + int restoreRoom = 0; + s.syncAsUint16LE(restoreRoom); + _flags->setFlagValue(Flags::RESTOREROOM, restoreRoom); + + // Rooms + for (int roomId = 0; roomId < _script->kMaxRooms; roomId++) { + Room *room = new Room(); + room->loadRoom(_script->getRoomOffset(roomId)); + + byte roomType = emptyRoom; + s.syncAsByte(roomType); + if (roomType == emptyRoom) { + delete room; + continue; + } + + // Mobs + for (int mobId = 0; mobId < kMaxMobs; mobId++) { + byte value = 0; + s.syncAsByte(value); + _script->setMobVisible(room->_mobs, mobId, value); + } + + // Background animations + for (int backAnimSlot = 0; backAnimSlot < kMaxBackAnims; backAnimSlot++) { + uint32 value = 0; + s.syncAsUint32LE(value); + _script->setBackAnimId(_room->_backAnim, backAnimSlot, value); + } + + // Objects + for (int objectSlot = 0; objectSlot < kMaxObjects; objectSlot++) { + byte value = 0; + s.syncAsByte(value); + _script->setObjId(room->_obj, objectSlot, value); + } + + delete room; + } + + // Main hero + s.syncAsUint16LE(_mainHero->_visible); + s.syncAsUint16LE(_mainHero->_middleX); + s.syncAsUint16LE(_mainHero->_middleY); + s.syncAsUint16LE(_mainHero->_lastDirection); + s.syncAsUint32LE(_mainHero->_color); + s.syncAsUint16LE(_mainHero->_maxBoredom); + s.syncAsUint32LE(_mainHero->_animSetNr); + _mainHero->loadAnimSet(_mainHero->_animSetNr); + + _mainHero->_inventory.clear(); + byte invId = endInv; + while (1) { + s.syncAsByte(invId); + if (invId == endInv) { + break; + } + _mainHero->_inventory.push_back(invId); + } + + _mainHero->_inventory2.clear(); + invId = endInv; + while (1) { + s.syncAsByte(invId); + if (invId == endInv) { + break; + } + _mainHero->_inventory.push_back(invId); + } + + // Second hero + s.syncAsUint16LE(_secondHero->_visible); + s.syncAsUint16LE(_secondHero->_middleX); + s.syncAsUint16LE(_secondHero->_middleY); + s.syncAsUint16LE(_secondHero->_lastDirection); + s.syncAsUint32LE(_secondHero->_color); + s.syncAsUint16LE(_secondHero->_maxBoredom); + s.syncAsUint32LE(_secondHero->_animSetNr); + _secondHero->loadAnimSet(_secondHero->_animSetNr); + + _secondHero->_inventory.clear(); + invId = endInv; + while (1) { + s.syncAsByte(invId); + if (invId == endInv) { + break; + } + _secondHero->_inventory.push_back(invId); + } + + _secondHero->_inventory2.clear(); + invId = endInv; + while (1) { + s.syncAsByte(invId); + if (invId == endInv) { + break; + } + _secondHero->_inventory.push_back(invId); + } + + // Script + _interpreter->setBgOpcodePC(0); + _interpreter->setFgOpcodePC(_script->_scriptInfo.restoreGame); + + } +} + +Common::Error PrinceEngine::loadGameState(int slot) { + if (!loadGame(slot)) { + return Common::kReadingFailed; + } + return Common::kNoError; +} + +bool PrinceEngine::loadGame(int slotNumber) { + Common::MemoryReadStream *readStream; + + // Open up the savegame file + Common::String slotName = generateSaveName(slotNumber); + Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName); + + // Read the data into a data buffer + int size = saveFile->size(); + byte *dataBuffer = (byte *)malloc(size); + saveFile->read(dataBuffer, size); + readStream = new Common::MemoryReadStream(dataBuffer, size, DisposeAfterUse::YES); + delete saveFile; + + // Check to see if it's a ScummVM savegame or not + char buffer[kSavegameStrSize + 1]; + readStream->read(buffer, kSavegameStrSize + 1); + + if (strncmp(buffer, kSavegameStr, kSavegameStrSize + 1) != 0) { + delete readStream; + return false; + } else { + SavegameHeader saveHeader; + + if (!readSavegameHeader(readStream, saveHeader)) { + delete readStream; + return false; + } + + // Delete the thumbnail + saveHeader.thumbnail->free(); + delete saveHeader.thumbnail; + } + + // Get in the savegame + syncGame(readStream, nullptr); + delete readStream; + + // TODO + //syncSpeechSettings(); + + return true; +} + +} // End of namespace Prince diff --git a/engines/prince/script.cpp b/engines/prince/script.cpp index e2c323b415..57d0b8a770 100644 --- a/engines/prince/script.cpp +++ b/engines/prince/script.cpp @@ -148,12 +148,16 @@ uint32 Script::getStartGameOffset() { return _scriptInfo.startGame; } -bool Script::getMobVisible(int mob) { - return _data[_vm->_room->_mobs + mob]; +uint32 Script::getLocationInitScript(int initRoomTableOffset, int roomNr) { + return (uint32)READ_UINT32(&_data[initRoomTableOffset + roomNr * 4]); } -void Script::setMobVisible(int mob, int value) { - _data[_vm->_room->_mobs + mob] = value; +byte Script::getMobVisible(int roomMobOffset, uint16 mob) { + return _data[roomMobOffset + mob]; +} + +void Script::setMobVisible(int roomMobOffset, uint16 mob, byte value) { + _data[roomMobOffset + mob] = value; } uint8 *Script::getRoomOffset(int locationNr) { @@ -185,12 +189,21 @@ uint8 *Script::getHeroAnimName(int offset) { return &_data[offset]; } -void Script::setBackAnimId(int offset, int animId) { - WRITE_UINT32(&_data[offset], animId); +uint32 Script::getBackAnimId(int roomBackAnimOffset, int slot) { + uint32 animId = READ_UINT32(&_data[roomBackAnimOffset + slot * 4]); + return animId; +} + +void Script::setBackAnimId(int roomBackAnimOffset, int slot, int animId) { + WRITE_UINT32(&_data[roomBackAnimOffset + slot * 4], animId); } -void Script::setObjId(int offset, int objId) { - _data[offset] = objId; +byte Script::getObjId(int roomObjOffset, int slot) { + return _data[roomObjOffset + slot]; +} + +void Script::setObjId(int roomObjOffset, int slot, byte objectId) { + _data[roomObjOffset + slot] = objectId; } int Script::scanMobEvents(int mobMask, int dataEventOffset) { @@ -232,7 +245,9 @@ int Script::scanMobEventsWithItem(int mobMask, int dataEventOffset, int itemMask return -1; } -void Script::installSingleBackAnim(Common::Array<BackgroundAnim> &backAnimList, int slot, int offset) { +void Script::installSingleBackAnim(Common::Array<BackgroundAnim> &backAnimList, int slot, int roomBackAnimOffset) { + + int offset = roomBackAnimOffset + slot * 4; BackgroundAnim newBackgroundAnim; @@ -311,20 +326,15 @@ void Script::installSingleBackAnim(Common::Array<BackgroundAnim> &backAnimList, } } -void Script::installBackAnims(Common::Array<BackgroundAnim> &backAnimList, int offset) { +void Script::installBackAnims(Common::Array<BackgroundAnim> &backAnimList, int roomBackAnimOffset) { for (int i = 0; i < _vm->kMaxBackAnims; i++) { - installSingleBackAnim(backAnimList, i, offset); - offset += 4; + installSingleBackAnim(backAnimList, i, roomBackAnimOffset); } } void Script::installObjects(int offset) { for (int i = 0; i < _vm->kMaxObjects; i++) { - if (_data[offset] != 0xFF) { - _vm->_objSlot[i] = i; - } else { - _vm->_objSlot[i] = -1; - } + _vm->_objSlot[i] = _data[offset]; offset++; } } @@ -382,11 +392,11 @@ void InterpreterFlags::resetAllFlags() { } void InterpreterFlags::setFlagValue(Flags::Id flagId, uint32 value) { - _flags[(uint32)flagId - FLAG_MASK] = value; + _flags[(uint32)flagId - kFlagMask] = value; } uint32 InterpreterFlags::getFlagValue(Flags::Id flagId) { - return _flags[(uint32)flagId - FLAG_MASK]; + return _flags[(uint32)flagId - kFlagMask]; } Interpreter::Interpreter(PrinceEngine *vm, Script *script, InterpreterFlags *flags) : @@ -497,6 +507,14 @@ void Interpreter::setResult(byte value) { _result = value; } +void Interpreter::setBgOpcodePC(uint32 value) { + _bgOpcodePC = value; +} + +void Interpreter::setFgOpcodePC(uint32 value) { + _fgOpcodePC = value; +} + template <typename T> T Interpreter::readScript() { T data = _script->read<T>(_currentInstruction); @@ -506,7 +524,7 @@ T Interpreter::readScript() { uint16 Interpreter::readScriptFlagValue() { uint16 value = readScript<uint16>(); - if (value & InterpreterFlags::FLAG_MASK) { + if (value & InterpreterFlags::kFlagMask) { return _flags->getFlagValue((Flags::Id)value); } return value; @@ -567,8 +585,7 @@ void Interpreter::O_PUTOBJECT() { uint16 objectId = readScriptFlagValue(); Room *room = new Room(); room->loadRoom(_script->getRoomOffset(roomId)); - int offset = room->_obj + slot; - _vm->_script->setObjId(offset, objectId); + _vm->_script->setObjId(room->_obj, slot, objectId); if (_vm->_locationNr == roomId) { _vm->_objSlot[slot] = objectId; } @@ -581,10 +598,9 @@ void Interpreter::O_REMOBJECT() { uint16 slot = readScriptFlagValue(); Room *room = new Room(); room->loadRoom(_script->getRoomOffset(roomId)); - int offset = room->_obj + slot; - _vm->_script->setObjId(offset, 0xFF); + _vm->_script->setObjId(room->_obj, slot, 0xFF); if (_vm->_locationNr == roomId) { - _vm->_objSlot[slot] = -1; + _vm->_objSlot[slot] = 0xFF; } delete room; debugInterpreter("O_REMOBJECT roomId %d slot %d", roomId, slot); @@ -653,10 +669,9 @@ void Interpreter::O_PUTBACKANIM() { int32 animId = readScript<uint32>(); Room *room = new Room(); room->loadRoom(_script->getRoomOffset(roomId)); - int offset = room->_backAnim + slot * 4; - _vm->_script->setBackAnimId(offset, animId); + _vm->_script->setBackAnimId(room->_backAnim, slot, animId); if (_vm->_locationNr == roomId) { - _vm->_script->installSingleBackAnim(_vm->_backAnimList, slot, offset); + _vm->_script->installSingleBackAnim(_vm->_backAnimList, slot, room->_backAnim); } delete room; debugInterpreter("O_PUTBACKANIM roomId %d, slot %d, animId %d", roomId, slot, animId); @@ -670,8 +685,7 @@ void Interpreter::O_REMBACKANIM() { } Room *room = new Room(); room->loadRoom(_script->getRoomOffset(roomId)); - int offset = room->_backAnim + slot * 4; - _vm->_script->setBackAnimId(offset, 0); + _vm->_script->setBackAnimId(room->_backAnim, slot, 0); delete room; debugInterpreter("O_REMBACKANIM roomId %d, slot %d", roomId, slot); } @@ -1019,22 +1033,24 @@ void Interpreter::O_CLSTEXT() { debugInterpreter("O_CLSTEXT slot %d", slot); } -// TODO - check if need this for saving void Interpreter::O_CALLTABLE() { - uint16 flag = readScript<uint16>(); - int32 table = readScript<uint32>(); - - debugInterpreter("O_CALLTABLE flag %d, table %d", flag, table); - // makes a call from script function table - // must read table pointer from _code and - // use table entry as next opcode + Flags::Id flagId = readScriptFlagId(); + int roomNr = _flags->getFlagValue(flagId); + int32 tableOffset = readScript<uint32>(); + int initLocationScript = _script->getLocationInitScript(tableOffset, roomNr); + if (initLocationScript) { + _stack[_stacktop] = _currentInstruction; + _stacktop++; + _currentInstruction = initLocationScript; + } + debugInterpreter("O_CALLTABLE loc %d", roomNr); } void Interpreter::O_CHANGEMOB() { uint16 mob = readScriptFlagValue(); uint16 value = readScriptFlagValue(); value ^= 1; - _vm->_script->setMobVisible(mob, value); + _vm->_script->setMobVisible(_vm->_room->_mobs, mob, value); _vm->_mobList[mob]._visible = value; debugInterpreter("O_CHANGEMOB mob %d, value %d", mob, value); } @@ -1527,7 +1543,7 @@ void Interpreter::O_BACKANIMRANGE() { uint16 low = readScriptFlagValue(); uint16 high = readScriptFlagValue(); if (animId != 0xFFFF) { - if (animId & InterpreterFlags::FLAG_MASK) { + if (animId & InterpreterFlags::kFlagMask) { animId = _flags->getFlagValue((Flags::Id)animId); } } diff --git a/engines/prince/script.h b/engines/prince/script.h index 04f125a8ea..6ebef3d94b 100644 --- a/engines/prince/script.h +++ b/engines/prince/script.h @@ -96,6 +96,8 @@ private: class Script { public: + static const int16 kMaxRooms = 60; + Script(PrinceEngine *vm); ~Script(); @@ -133,23 +135,30 @@ public: } uint32 getStartGameOffset(); + uint32 getLocationInitScript(int initRoomTableOffset, int roomNr); int16 getLightX(int locationNr); int16 getLightY(int locationNr); int32 getShadowScale(int locationNr); uint8 *getRoomOffset(int locationNr); int32 getOptionStandardOffset(int option); uint8 *getHeroAnimName(int offset); - void setBackAnimId(int offset, int animId); - void setObjId(int offset, int objId); - void installBackAnims(Common::Array<BackgroundAnim> &backAnimList, int offset); - void installSingleBackAnim(Common::Array<BackgroundAnim> &backAnimList, int slot, int offset); + + void installBackAnims(Common::Array<BackgroundAnim> &backAnimList, int roomBackAnimOffset); + void installSingleBackAnim(Common::Array<BackgroundAnim> &backAnimList, int slot, int roomBackAnimOffset); void installObjects(int offset); bool loadAllMasks(Common::Array<Mask> &maskList, int offset); int scanMobEvents(int mobMask, int dataEventOffset); int scanMobEventsWithItem(int mobMask, int dataEventOffset, int itemMask); - bool getMobVisible(int mob); - void setMobVisible(int mob, int value); + + byte getMobVisible(int roomMobOffset, uint16 mob); + void setMobVisible(int roomMobOffset, uint16 mob, byte value); + + uint32 getBackAnimId(int roomBackAnimOffset, int slot); + void setBackAnimId(int roomBackAnimOffset, int slot, int animId); + + byte getObjId(int roomObjOffset, int slot); + void setObjId(int roomObjOffset, int slot, byte objectId); const char *getString(uint32 offset) { return (const char *)(&_data[offset]); @@ -171,11 +180,10 @@ public: void resetAllFlags(); - static const uint16 FLAG_MASK = 0x8000; - + static const uint16 kFlagMask = 0x8000; + static const uint16 kMaxFlags = 2000; private: - static const uint16 MAX_FLAGS = 2000; - int32 _flags[MAX_FLAGS]; + int32 _flags[kMaxFlags]; }; class Interpreter { @@ -189,6 +197,9 @@ public: int getLastOPCode(); int getFgOpcodePC(); + void setBgOpcodePC(uint32 value); + void setFgOpcodePC(uint32 value); + uint32 getCurrentString(); void setCurrentString(uint32 value); |