diff options
author | Julien Templier | 2010-10-26 00:41:42 +0000 |
---|---|---|
committer | Julien Templier | 2010-10-26 00:41:42 +0000 |
commit | ae2c4b7cd209b310ae9e01edafa9613c06ff421f (patch) | |
tree | e686f651a78fd7997a050e48f8300099ae333229 /engines/lastexpress | |
parent | 8217efc74a9b5066bd158e7fef5edd257aec9f16 (diff) | |
download | scummvm-rg350-ae2c4b7cd209b310ae9e01edafa9613c06ff421f.tar.gz scummvm-rg350-ae2c4b7cd209b310ae9e01edafa9613c06ff421f.tar.bz2 scummvm-rg350-ae2c4b7cd209b310ae9e01edafa9613c06ff421f.zip |
LASTEXPRESS: Savegame support update
- Implement Menu::startGame() properly
- Add stubs functions for game restart
- Made savegame headers serializable and moved validity checks inside struct definition
- Implement create/init savegame functions
- Add SavegameStream to be able to read/write to the same memory stream
- Add stubs for setup, writeEntry & loadEntry functions
svn-id: r53841
Diffstat (limited to 'engines/lastexpress')
-rw-r--r-- | engines/lastexpress/game/logic.cpp | 18 | ||||
-rw-r--r-- | engines/lastexpress/game/logic.h | 1 | ||||
-rw-r--r-- | engines/lastexpress/game/menu.cpp | 135 | ||||
-rw-r--r-- | engines/lastexpress/game/menu.h | 3 | ||||
-rw-r--r-- | engines/lastexpress/game/savegame.cpp | 372 | ||||
-rw-r--r-- | engines/lastexpress/game/savegame.h | 211 | ||||
-rw-r--r-- | engines/lastexpress/shared.h | 2 |
7 files changed, 459 insertions, 283 deletions
diff --git a/engines/lastexpress/game/logic.cpp b/engines/lastexpress/game/logic.cpp index 4aedd04a0c..87cf2b54e3 100644 --- a/engines/lastexpress/game/logic.cpp +++ b/engines/lastexpress/game/logic.cpp @@ -402,7 +402,23 @@ void Logic::eventTick(const Common::Event &) { // Game over, Chapters & credits ////////////////////////////////////////////////////////////////////////// -// Handle game over +/** + * Resets the game state. + */ +void Logic::resetState() { + getState()->scene = kSceneDefault; + + warning("Logic::resetState: not implemented! You need to restart the engine until this is implemented."); +} + +/** + * Handle game over + * + * @param type The savegame type. + * @param value The value (event, time, index, ...) + * @param sceneIndex Index of the scene to show. + * @param showScene true to show a scene, false to return to menu directly + */ void Logic::gameOver(SavegameType type, uint32 value, SceneIndex sceneIndex, bool showScene) const { getSound()->processEntries(); diff --git a/engines/lastexpress/game/logic.h b/engines/lastexpress/game/logic.h index 7e01655b68..fc867d7680 100644 --- a/engines/lastexpress/game/logic.h +++ b/engines/lastexpress/game/logic.h @@ -54,6 +54,7 @@ public: void eventMouse(const Common::Event &ev); void eventTick(const Common::Event &ev); + void resetState(); void gameOver(SavegameType type, uint32 value, SceneIndex sceneIndex, bool showScene) const; void playFinalSequence() const; void updateCursor(bool redraw = true) const; diff --git a/engines/lastexpress/game/menu.cpp b/engines/lastexpress/game/menu.cpp index 6992178d57..ab95fd30c4 100644 --- a/engines/lastexpress/game/menu.cpp +++ b/engines/lastexpress/game/menu.cpp @@ -360,7 +360,7 @@ Menu::Menu(LastExpressEngine *engine) : _engine(engine), _gameId(kGameBlue), _hasShownStartScreen(false), _hasShownIntro(false), _isShowingCredits(false), _isGameStarted(false), _isShowingMenu(false), _creditsSequenceIndex(0), _checkHotspotsTicks(15), _mouseFlags(Common::EVENT_INVALID), _lastHotspot(NULL), - _currentIndex(0), _currentTime(0), _lowerTime(0), _index(0), _index2(0), _time(0), _delta(0), _handleTimeDelta(false) { + _currentIndex(0), _currentTime(0), _lowerTime(0), _index(0), _savegameIndex(0), _time(0), _delta(0), _handleTimeDelta(false) { _clock = new Clock(_engine); _trainLine = new TrainLine(_engine); @@ -661,8 +661,8 @@ bool Menu::handleEvent(StartMenuAction action, Common::EventType type) { if (_isGameStarted) { showFrame(kOverlayEggButtons, kButtonContinue, true); - if (_index2 == _index) { - showFrame(kOverlayTooltip, isGameFinished() ? kTooltipViewGameEnding : kTooltipContinueGame, true); + if (_savegameIndex == _index) { + showFrame(kOverlayTooltip, getSaveLoad()->isGameFinished(_index, _savegameIndex) ? kTooltipViewGameEnding : kTooltipContinueGame, true); } else { showFrame(kOverlayTooltip, kTooltipContinueRewoundGame, true); } @@ -828,7 +828,7 @@ bool Menu::handleEvent(StartMenuAction action, Common::EventType type) { ////////////////////////////////////////////////////////////////////////// case kMenuForwardGame: - if (_index2 <= _index || _currentTime > _time) { + if (_savegameIndex <= _index || _currentTime > _time) { hideOverlays(); break; } @@ -1075,7 +1075,7 @@ void Menu::init(bool doSavegame, SavegameType type, uint32 value) { // Create a new savegame if needed if (!SaveLoad::isSavegamePresent(_gameId)) - SaveLoad::writeMainHeader(_gameId); + getSaveLoad()->create(_gameId); if (doSavegame) getSaveLoad()->saveGame(kSavegameTypeEvent2, kEntityPlayer, kEventNone); @@ -1084,18 +1084,12 @@ void Menu::init(bool doSavegame, SavegameType type, uint32 value) { // TODO: remove existing savegame temp file } - // Init savegame and get the header data - getSaveLoad()->initSavegame(_gameId, true); - SaveLoad::SavegameMainHeader header; - if (!SaveLoad::loadMainHeader(_gameId, &header)) - error("Menu::init: Corrupted savegame - Recovery path not implemented!"); - - // Init Menu values - _index2 = header.index; - _lowerTime = getSaveLoad()->getEntry(_index2)->time; + // Init savegame & menu values + _savegameIndex = getSaveLoad()->init(_gameId, true); + _lowerTime = getSaveLoad()->getTime(_savegameIndex); if (useSameIndex) - _index = _index2; + _index = _savegameIndex; //if (!getGlobalTimer()) // _index3 = 0; @@ -1103,8 +1097,8 @@ void Menu::init(bool doSavegame, SavegameType type, uint32 value) { if (!getProgress().chapter) getProgress().chapter = kChapter1; - getState()->time = getSaveLoad()->getEntry(_index)->time; - getProgress().chapter = getSaveLoad()->getEntry(_index)->chapter; + getState()->time = getSaveLoad()->getTime(_index); + getProgress().chapter = getSaveLoad()->getChapter(_index); if (_lowerTime >= kTimeStartGame) { _currentTime = getState()->time; @@ -1117,11 +1111,26 @@ void Menu::init(bool doSavegame, SavegameType type, uint32 value) { } void Menu::startGame() { - // TODO: we need to reset the current scene - getState()->scene = kSceneDefault; + // TODO: Clear train sequences & savegame headers - getEntities()->setup(true, kEntityPlayer); - warning("Menu::startGame: not implemented!"); + if (0 /* test for temp filename */ ) { + if (_savegameIndex == _index) + getSaveLoad()->loadGame(_gameId); + else + getSaveLoad()->loadGame2(_gameId); + } else { + if (_savegameIndex == _index) { + setGlobalTimer(0); + if (_index) { + getSaveLoad()->loadGame(_gameId); + } else { + getLogic()->resetState(); + getEntities()->setup(true, kEntityPlayer); + } + } else { + getSaveLoad()->loadGame2(_gameId); + } + } } // Switch to the next savegame @@ -1132,7 +1141,7 @@ void Menu::switchGame() { // Initialize savegame if needed if (!SaveLoad::isSavegamePresent(_gameId)) - SaveLoad::writeMainHeader(_gameId); + getSaveLoad()->create(_gameId); getState()->time = 0; @@ -1141,53 +1150,11 @@ void Menu::switchGame() { _trainLine->clear(); // Clear loaded savegame data - getSaveLoad()->clearEntries(); + getSaveLoad()->clear(); init(false, kSavegameTypeIndex, 0); } -bool Menu::isGameFinished() const { - SaveLoad::SavegameEntryHeader *data = getSaveLoad()->getEntry(_index); - - if (_index2 != _index) - return false; - - if (data->type != SaveLoad::kHeaderType2) - return false; - - return (data->event == kEventAnnaKilled - || data->event == kEventKronosHostageAnnaNoFirebird - || data->event == kEventKahinaPunchBaggageCarEntrance - || data->event == kEventKahinaPunchBlue - || data->event == kEventKahinaPunchYellow - || data->event == kEventKahinaPunchSalon - || data->event == kEventKahinaPunchKitchen - || data->event == kEventKahinaPunchBaggageCar - || data->event == kEventKahinaPunchCar - || data->event == kEventKahinaPunchSuite4 - || data->event == kEventKahinaPunchRestaurant - || data->event == kEventKahinaPunch - || data->event == kEventKronosGiveFirebird - || data->event == kEventAugustFindCorpse - || data->event == kEventMertensBloodJacket - || data->event == kEventMertensCorpseFloor - || data->event == kEventMertensCorpseBed - || data->event == kEventCoudertBloodJacket - || data->event == kEventGendarmesArrestation - || data->event == kEventAbbotDrinkGiveDetonator - || data->event == kEventMilosCorpseFloor - || data->event == kEventLocomotiveAnnaStopsTrain - || data->event == kEventTrainStopped - || data->event == kEventCathVesnaRestaurantKilled - || data->event == kEventCathVesnaTrainTopKilled - || data->event == kEventLocomotiveConductorsDiscovered - || data->event == kEventViennaAugustUnloadGuns - || data->event == kEventViennaKronosFirebird - || data->event == kEventVergesAnnaDead - || data->event == kEventTrainExplosionBridge - || data->event == kEventKronosBringNothing); -} - ////////////////////////////////////////////////////////////////////////// // Overlays & elements ////////////////////////////////////////////////////////////////////////// @@ -1307,7 +1274,7 @@ void Menu::initTime(SavegameType type, uint32 value) { // Iterate through existing entries do { - if (getSaveLoad()->getEntry(entryIndex)->time <= value) + if (getSaveLoad()->getTime(entryIndex) <= value) break; entryIndex--; @@ -1320,7 +1287,7 @@ void Menu::initTime(SavegameType type, uint32 value) { break; do { - if (getSaveLoad()->getEntry(entryIndex)->event == (EventIndex)value) + if (getSaveLoad()->getValue(entryIndex) == value) break; entryIndex--; @@ -1332,7 +1299,7 @@ void Menu::initTime(SavegameType type, uint32 value) { if (_index > 1) { uint32 index = _index; do { - if (getSaveLoad()->getEntry(index)->event == (EventIndex)value) + if (getSaveLoad()->getValue(index) == value) break; index--; @@ -1347,7 +1314,7 @@ void Menu::initTime(SavegameType type, uint32 value) { if (entryIndex) { _currentIndex = entryIndex; - updateTime(getSaveLoad()->getEntry(entryIndex)->time); + updateTime(getSaveLoad()->getTime(entryIndex)); } } @@ -1381,7 +1348,7 @@ void Menu::adjustIndex(uint32 time1, uint32 time2, bool searchEntry) { if ((int32)_index >= 0) { do { // Calculate new delta - int32 newDelta = time1 - getSaveLoad()->getEntry(currentIndex)->time; + int32 newDelta = time1 - getSaveLoad()->getTime(currentIndex); if (newDelta >= 0 && timeDelta >= newDelta) { timeDelta = newDelta; @@ -1398,10 +1365,10 @@ void Menu::adjustIndex(uint32 time1, uint32 time2, bool searchEntry) { if (searchEntry) { uint32 currentIndex = _index; - if (_index2 >= _index) { + if (_savegameIndex >= _index) { do { // Calculate new delta - int32 newDelta = getSaveLoad()->getEntry(currentIndex)->time - time1; + int32 newDelta = getSaveLoad()->getTime(currentIndex) - time1; if (newDelta >= 0 && timeDelta > newDelta) { timeDelta = newDelta; @@ -1409,7 +1376,7 @@ void Menu::adjustIndex(uint32 time1, uint32 time2, bool searchEntry) { } ++currentIndex; - } while (currentIndex >= _index2); + } while (currentIndex >= _savegameIndex); } } else { index = _index + 1; @@ -1421,45 +1388,45 @@ void Menu::adjustIndex(uint32 time1, uint32 time2, bool searchEntry) { } if (_index == _currentIndex) { - if (getProgress().chapter != getSaveLoad()->getEntry(index)->chapter) - getProgress().chapter = getSaveLoad()->getEntry(_index)->chapter; + if (getProgress().chapter != getSaveLoad()->getChapter(index)) + getProgress().chapter = getSaveLoad()->getChapter(_index); } } void Menu::goToTime(uint32 time) { uint32 entryIndex = 0; - uint32 deltaTime = (uint32)ABS((int32)(getSaveLoad()->getEntry(0)->time - time)); + uint32 deltaTime = (uint32)ABS((int32)(getSaveLoad()->getTime(0) - time)); uint32 index = 0; do { - uint32 deltaTime2 = (uint32)ABS((int32)(getSaveLoad()->getEntry(index)->time - time)); + uint32 deltaTime2 = (uint32)ABS((int32)(getSaveLoad()->getTime(index) - time)); if (deltaTime2 < deltaTime) { deltaTime = deltaTime2; entryIndex = index; } ++index; - } while (_index2 >= index); + } while (_savegameIndex >= index); _currentIndex = entryIndex; - updateTime(getSaveLoad()->getEntry(entryIndex)->time); + updateTime(getSaveLoad()->getTime(entryIndex)); } void Menu::setTime() { _currentIndex = _index; - _currentTime = getSaveLoad()->getEntry(_currentIndex)->time; + _currentTime = getSaveLoad()->getTime(_currentIndex); if (_time == _currentTime) adjustTime(); } void Menu::forwardTime() { - if (_index2 <= _index) + if (_savegameIndex <= _index) return; - _currentIndex = _index2; - updateTime(getSaveLoad()->getEntry(_currentIndex)->time); + _currentIndex = _savegameIndex; + updateTime(getSaveLoad()->getTime(_currentIndex)); } void Menu::rewindTime() { @@ -1467,7 +1434,7 @@ void Menu::rewindTime() { return; _currentIndex = 0; - updateTime(getSaveLoad()->getEntry(_currentIndex)->time); + updateTime(getSaveLoad()->getTime(_currentIndex)); } void Menu::adjustTime() { diff --git a/engines/lastexpress/game/menu.h b/engines/lastexpress/game/menu.h index 006aecc5ef..f6bcb05959 100644 --- a/engines/lastexpress/game/menu.h +++ b/engines/lastexpress/game/menu.h @@ -145,7 +145,6 @@ private: // Game-related void startGame(); void switchGame(); - bool isGameFinished() const; ////////////////////////////////////////////////////////////////////////// // Overlays & elements @@ -183,7 +182,7 @@ private: uint32 _lowerTime; // lower time value uint32 _index; - uint32 _index2; + uint32 _savegameIndex; uint32 _time; uint32 _delta; bool _handleTimeDelta; diff --git a/engines/lastexpress/game/savegame.cpp b/engines/lastexpress/game/savegame.cpp index 6ba54fbe45..57b551af85 100644 --- a/engines/lastexpress/game/savegame.cpp +++ b/engines/lastexpress/game/savegame.cpp @@ -23,8 +23,10 @@ * */ -#include "lastexpress/game/logic.h" #include "lastexpress/game/savegame.h" + +#include "lastexpress/game/logic.h" +#include "lastexpress/game/menu.h" #include "lastexpress/game/state.h" #include "lastexpress/debug.h" @@ -36,10 +38,6 @@ namespace LastExpress { -// Savegame signatures -#define SAVEGAME_SIGNATURE 0x12001200 -#define SAVEGAME_HEADER 0xE660E660 - // Names of savegames static const struct { const char *saveFile; @@ -56,15 +54,91 @@ static const struct { // Constructors ////////////////////////////////////////////////////////////////////////// -SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine) { - _gameTicksLastSavegame = 0; +SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine), _savegame(NULL), _gameTicksLastSavegame(0) { } SaveLoad::~SaveLoad() { + clear(); + + SAFE_DELETE(_savegame); + //Zero passed pointers _engine = NULL; +} + +void SaveLoad::initStream() { + SAFE_DELETE(_savegame); + + _savegame = new SavegameStream(); +} + +void SaveLoad::flushStream(GameId id) { + Common::OutSaveFile *save = openForSaving(id); + if (!save) + error("SaveLoad::initSave: Cannot init savegame (%s)!", getFilename(id).c_str()); + + save->write(_savegame->getData(), _savegame->size()); + + delete save; +} + +////////////////////////////////////////////////////////////////////////// +// Init +////////////////////////////////////////////////////////////////////////// +void SaveLoad::create(GameId id) { + initStream(); + + Common::Serializer ser(NULL, _savegame); + SavegameMainHeader header; + header.saveLoadWithSerializer(ser); + + flushStream(id); +} + +uint32 SaveLoad::init(GameId id, bool resetHeaders) { + initStream(); + + // Open savegame + Common::InSaveFile *save = openForLoading(id); + if (save->size() < 32) + error("SaveLoad::init - Savegame seems to be corrupted (not enough data: %i bytes)", save->size()); + + // Load all savegame data + while (!save->eos() && !save->err()) + _savegame->writeByte(save->readByte()); + _savegame->seek(0); + + delete save; + + // Load the main header + Common::Serializer ser(_savegame, NULL); + SavegameMainHeader header; + header.saveLoadWithSerializer(ser); + if (!header.isValid()) + error("SaveLoad::init - Savegame seems to be corrupted (invalid header)"); + + // Reset cached entry headers if needed + if (resetHeaders) { + clear(); - clearEntries(); + SavegameEntryHeader *header = new SavegameEntryHeader(); + header->time = kTimeCityParis; + header->chapter = kChapter1; + + _gameHeaders.push_back(header); + } + + warning("SaveLoad::initSavegame: not implemented!"); + + // return the index to the current save game entry (we store count + 1 entries, so we're good) + return 0; //header.count; +} + +void SaveLoad::clear() { + for (uint i = 0; i < _gameHeaders.size(); i++) + SAFE_DELETE(_gameHeaders[i]); + + _gameHeaders.clear(); } ////////////////////////////////////////////////////////////////////////// @@ -80,21 +154,80 @@ bool SaveLoad::loadGame(GameId id) { //Common::InSaveFile *save = SaveLoad::openForLoading(id); // Validate header - - - error("SaveLoad::loadgame: not implemented!"); return false; } +bool SaveLoad::loadGame2(GameId id) { + error("SaveLoad::loadgame2: not implemented!"); +} + // Save game void SaveLoad::saveGame(SavegameType type, EntityIndex entity, uint32 value) { + if (getState()->scene <= kSceneIntro) + return; + + // Validate main header + SavegameMainHeader header; + if (!loadMainHeader(_savegame, &header)) { + debugC(2, kLastExpressDebugSavegame, "SaveLoad::saveGame - Cannot load main header: %s", getFilename(getMenu()->getGameId()).c_str()); + return; + } + + // Validate the current entry if it exists + if (header.count > 0) { + _savegame->seek(header.offsetEntry); + + // Load entry header + SavegameEntryHeader entry; + Common::Serializer ser(_savegame, NULL); + entry.saveLoadWithSerializer(ser); + + if (!entry.isValid() || getState()->time < entry.time || (type == kSavegameTypeTickInterval && getState()->time == entry.time)) + return; + + if ((type == kSavegameTypeTime || type == kSavegameTypeEvent) + && (entry.type == kSavegameTypeTickInterval && (getState()->time - entry.time) < 450)) { + _savegame->seek(header.offsetEntry); + --header.count; + } else { + _savegame->seek(header.offset); + } + } else { + // Seek to the next savegame entry + _savegame->seek(header.offset); + } + + if (type != kSavegameTypeEvent2 && type != kSavegameTypeAuto) + header.offsetEntry = _savegame->pos(); + + // Write the savegame entry + writeEntry(type, entity, value); + + if (!header.keepIndex) + ++header.count; + + if (type == kSavegameTypeEvent2 || type == kSavegameTypeAuto) { + header.keepIndex = 1; + } else { + header.keepIndex = 0; + header.offset = _savegame->pos(); + + // Save ticks + _gameTicksLastSavegame = getState()->timeTicks; + } + + // Validate the main header + if (!header.isValid()) + error("SaveLoad::saveGame: main game header is invalid!"); - // Save ticks - _gameTicksLastSavegame = getState()->timeTicks; + // Write the main header + _savegame->seek(0); + Common::Serializer ser(NULL, _savegame); + header.saveLoadWithSerializer(ser); - warning("SaveLoad::savegame: not implemented!"); + flushStream(getMenu()->getGameId()); } void SaveLoad::saveVolumeBrightness() { @@ -107,7 +240,7 @@ void SaveLoad::saveVolumeBrightness() { // Check if a specific savegame exists bool SaveLoad::isSavegamePresent(GameId id) { - if (g_system->getSavefileManager()->listSavefiles(getSavegameName(id)).size() == 0) + if (g_system->getSavefileManager()->listSavefiles(getFilename(id)).size() == 0) return false; return true; @@ -116,114 +249,57 @@ bool SaveLoad::isSavegamePresent(GameId id) { // Check if the game has been started in the specific savegame bool SaveLoad::isSavegameValid(GameId id) { if (!isSavegamePresent(id)) { - debugC(2, kLastExpressDebugSavegame, "SaveLoad::isSavegameValid - Savegame does not exist: %s", getSavegameName(id).c_str()); + debugC(2, kLastExpressDebugSavegame, "SaveLoad::isSavegameValid - Savegame does not exist: %s", getFilename(id).c_str()); return false; } SavegameMainHeader header; - if (!loadMainHeader(id, &header)) - return false; - return validateMainHeader(header); + Common::InSaveFile *save = openForLoading(id); + return loadMainHeader(save, &header); } - ////////////////////////////////////////////////////////////////////////// // Headers ////////////////////////////////////////////////////////////////////////// -bool SaveLoad::loadMainHeader(GameId id, SavegameMainHeader *header) { - // Read first 32 bytes of savegame - Common::InSaveFile *save = openForLoading(id); - if (!save) { - debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Cannot open savegame for reading: %s", getSavegameName(id).c_str()); +bool SaveLoad::loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *header) { + if (!stream) return false; - } - // Check there is enough data - if (save->size() < 32) { - debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Savegame seems to be corrupted (not enough data: %i bytes): %s", save->size(), getSavegameName(id).c_str()); - delete save; + // Check there is enough data (32 bytes) + if (stream->size() < 32) { + debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Savegame seems to be corrupted (not enough data: %i bytes)!", stream->size()); return false; } - header->signature = save->readUint32LE(); - header->index = save->readUint32LE(); - header->time = save->readUint32LE(); - header->field_C = save->readUint32LE(); - header->field_10 = save->readUint32LE(); - header->brightness = save->readSint32LE(); - header->volume = save->readSint32LE(); - header->field_1C = save->readUint32LE(); + // Rewind stream + stream->seek(0); - delete save; + Common::Serializer ser(stream, NULL); + header->saveLoadWithSerializer(ser); - // Valide the header - if (!validateMainHeader(*header)) { - debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Cannot validate main header for savegame %s.", getSavegameName(id).c_str()); + // Validate the header + if (!header->isValid()) { + debugC(2, kLastExpressDebugSavegame, "SaveLoad::loadMainHeader - Cannot validate main header!"); return false; } return true; } -void SaveLoad::loadEntryHeader(Common::InSaveFile *save, SavegameEntryHeader *header) { - header->signature = save->readUint32LE(); - header->type = (HeaderType)save->readUint32LE(); - header->time = save->readUint32LE(); - header->field_C = save->readUint32LE(); - header->chapter = (ChapterIndex)save->readUint32LE(); - header->event = (EventIndex)save->readUint32LE(); - header->field_18 = save->readUint32LE(); - header->field_1C = save->readUint32LE(); +void SaveLoad::loadEntryHeader(SavegameEntryHeader *header) { + error("SaveLoad::loadEntryHeader: not implemented!"); } -bool SaveLoad::validateMainHeader(const SavegameMainHeader &header) { - if (header.signature != SAVEGAME_SIGNATURE) - return false; - - /* Check not needed as it can never be < 0 - if (header.chapter < 0) - return false;*/ - - if (header.time < 32) - return false; - - if (header.field_C < 32) - return false; - - if (header.field_10 != 1 && header.field_10) - return false; - - if (header.brightness < 0 || header.brightness > 6) - return false; - - if (header.volume < 0 || header.volume > 7) - return false; - - if (header.field_1C != 9) - return false; - - return true; +////////////////////////////////////////////////////////////////////////// +// Entries +////////////////////////////////////////////////////////////////////////// +void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { + warning("SaveLoad::writeEntry: not implemented!"); } -bool SaveLoad::validateEntryHeader(const SavegameEntryHeader &header) { - if (header.signature != SAVEGAME_HEADER) - return false; - - if (header.type < kHeaderType1 || header.type > kHeaderType5) - return false; - - if (header.time < kTimeStartGame || header.time > kTimeCityConstantinople) - return false; - - if (header.field_C <= 0 || header.field_C >= 15) - return false; - - /* No check for < 0, as it cannot happen normaly */ - if (header.chapter == 0) - return false; - - return true; +void SaveLoad::readEntry(SavegameType type, EntityIndex entity, uint32 value) { + warning("SaveLoad::readEntry: not implemented!"); } SaveLoad::SavegameEntryHeader *SaveLoad::getEntry(uint32 index) { @@ -233,78 +309,80 @@ SaveLoad::SavegameEntryHeader *SaveLoad::getEntry(uint32 index) { return _gameHeaders[index]; } -void SaveLoad::clearEntries() { - for (uint i = 0; i < _gameHeaders.size(); i++) - SAFE_DELETE(_gameHeaders[i]); - - _gameHeaders.clear(); -} - ////////////////////////////////////////////////////////////////////////// -// Init +// Checks ////////////////////////////////////////////////////////////////////////// -void SaveLoad::writeMainHeader(GameId id) { - Common::OutSaveFile *save = openForSaving(id); - if (!save) { - debugC(2, kLastExpressDebugSavegame, "SaveLoad::initSavegame - Cannot open savegame for writing: %s", getSavegameName(id).c_str()); - return; - } - - // Write default values to savegame - save->writeUint32LE(SAVEGAME_SIGNATURE); - save->writeUint32LE(0); - save->writeUint32LE(32); - save->writeUint32LE(32); - save->writeUint32LE(0); - save->writeUint32LE(3); - save->writeUint32LE(7); - save->writeUint32LE(9); - - delete save; -} +bool SaveLoad::isGameFinished(uint32 menuIndex, uint32 savegameIndex) { + SavegameEntryHeader *data = getEntry(menuIndex); -void SaveLoad::initSavegame(GameId id, bool resetHeaders) { - //Common::OutSaveFile *save = openForSaving(id); - //if (!save) { - // debugC(2, kLastExpressDebugSavegame, "SaveLoad::initSavegame - Cannot open savegame for writing: %s", getSavegameName(id).c_str()); - // return; - //} - - if (resetHeaders) { - clearEntries(); - - SavegameEntryHeader *header = new SavegameEntryHeader(); - header->time = kTimeCityParis; - header->chapter = kChapter1; - - _gameHeaders.push_back(header); - } - - // Open the savegame and read all game headers + if (savegameIndex != menuIndex) + return false; - warning("SaveLoad::initSavegame: not implemented!"); + if (data->type != kSavegameTypeEvent) + return false; - //delete save; + return (data->value == kEventAnnaKilled + || data->value == kEventKronosHostageAnnaNoFirebird + || data->value == kEventKahinaPunchBaggageCarEntrance + || data->value == kEventKahinaPunchBlue + || data->value == kEventKahinaPunchYellow + || data->value == kEventKahinaPunchSalon + || data->value == kEventKahinaPunchKitchen + || data->value == kEventKahinaPunchBaggageCar + || data->value == kEventKahinaPunchCar + || data->value == kEventKahinaPunchSuite4 + || data->value == kEventKahinaPunchRestaurant + || data->value == kEventKahinaPunch + || data->value == kEventKronosGiveFirebird + || data->value == kEventAugustFindCorpse + || data->value == kEventMertensBloodJacket + || data->value == kEventMertensCorpseFloor + || data->value == kEventMertensCorpseBed + || data->value == kEventCoudertBloodJacket + || data->value == kEventGendarmesArrestation + || data->value == kEventAbbotDrinkGiveDetonator + || data->value == kEventMilosCorpseFloor + || data->value == kEventLocomotiveAnnaStopsTrain + || data->value == kEventTrainStopped + || data->value == kEventCathVesnaRestaurantKilled + || data->value == kEventCathVesnaTrainTopKilled + || data->value == kEventLocomotiveConductorsDiscovered + || data->value == kEventViennaAugustUnloadGuns + || data->value == kEventViennaKronosFirebird + || data->value == kEventVergesAnnaDead + || data->value == kEventTrainExplosionBridge + || data->value == kEventKronosBringNothing); } + ////////////////////////////////////////////////////////////////////////// // Private methods ////////////////////////////////////////////////////////////////////////// // Get the file name from the savegame ID -Common::String SaveLoad::getSavegameName(GameId id) { +Common::String SaveLoad::getFilename(GameId id) { if (id >= 6) - error("SaveLoad::getSavegameName - attempting to use an invalid game id. Valid values: 0 - 5, was %d", id); + error("SaveLoad::getName - attempting to use an invalid game id. Valid values: 0 - 5, was %d", id); return gameInfo[id].saveFile; } Common::InSaveFile *SaveLoad::openForLoading(GameId id) { - return g_system->getSavefileManager()->openForLoading(getSavegameName(id)); + Common::InSaveFile *load = g_system->getSavefileManager()->openForLoading(getFilename(id)); + + if (!load) + debugC(2, kLastExpressDebugSavegame, "SaveLoad::openForLoading - Cannot open savegame for loading: %s", getFilename(id).c_str()); + + return load; } Common::OutSaveFile *SaveLoad::openForSaving(GameId id) { - return g_system->getSavefileManager()->openForSaving(getSavegameName(id)); + Common::OutSaveFile *save = g_system->getSavefileManager()->openForSaving(getFilename(id)); + + if (!save) + debugC(2, kLastExpressDebugSavegame, "SaveLoad::openForSaving - Cannot open savegame for writing: %s", getFilename(id).c_str()); + + return save; } } // End of namespace LastExpress diff --git a/engines/lastexpress/game/savegame.h b/engines/lastexpress/game/savegame.h index a5af760846..ecb976b38c 100644 --- a/engines/lastexpress/game/savegame.h +++ b/engines/lastexpress/game/savegame.h @@ -77,95 +77,208 @@ #include "lastexpress/shared.h" #include "common/savefile.h" +#include "common/serializer.h" namespace LastExpress { +// Savegame signatures +#define SAVEGAME_SIGNATURE 0x12001200 +#define SAVEGAME_HEADER 0xE660E660 + class LastExpressEngine; class SaveLoad { public: - enum HeaderType { - kHeaderTypeNone = 0, - kHeaderType1 = 1, - kHeaderType2 = 2, - kHeaderType3 = 3, - kHeaderType4 = 4, - kHeaderType5 = 5 + SaveLoad(LastExpressEngine *engine); + ~SaveLoad(); + + // Init + void create(GameId id); + void clear(); + uint32 init(GameId id, bool resetHeaders); + + // Save & Load + bool loadGame(GameId id); + bool loadGame2(GameId id); + void saveGame(SavegameType type, EntityIndex entity, uint32 value); + + void saveVolumeBrightness(); + + // Getting information + static bool isSavegamePresent(GameId id); + static bool isSavegameValid(GameId id); + + bool isGameFinished(uint32 menuIndex, uint32 savegameIndex); + + // Accessors + TimeValue getTime(uint32 index) { return getEntry(index)->time; } + ChapterIndex getChapter(uint32 index) { return getEntry(index)->chapter; } + uint32 getValue(uint32 index) { return getEntry(index)->value; } + uint32 getLastSavegameTicks() const { return _gameTicksLastSavegame; } + +private: + class SavegameStream : public Common::MemoryWriteStreamDynamic, public Common::SeekableReadStream { + public: + SavegameStream() : MemoryWriteStreamDynamic(DisposeAfterUse::YES), + _eos(false) {} + + int32 pos() const { return MemoryWriteStreamDynamic::pos(); } + int32 size() const { return MemoryWriteStreamDynamic::size(); } + bool seek(int32 offset, int whence = SEEK_SET) { return MemoryWriteStreamDynamic::seek(offset, whence); } + bool eos() const { return _eos; } + uint32 read(void *dataPtr, uint32 dataSize) { + if ((int32)dataSize > size() - pos()) { + dataSize = size() - pos(); + _eos = true; + } + memcpy(dataPtr, getData() + pos(), dataSize); + + seek(dataSize, SEEK_CUR); + + return dataSize; + } + private: + bool _eos; }; - struct SavegameMainHeader { + LastExpressEngine *_engine; + + struct SavegameMainHeader : Common::Serializable { uint32 signature; - uint32 index; - uint32 time; - uint32 field_C; - uint32 field_10; + uint32 count; + uint32 offset; + uint32 offsetEntry; + uint32 keepIndex; int32 brightness; int32 volume; uint32 field_1C; + + SavegameMainHeader() { + signature = SAVEGAME_SIGNATURE; + count = 0; + offset = 32; + offsetEntry = 32; + keepIndex = 0; + brightness = 3; + volume = 7; + field_1C = 9; + } + + void saveLoadWithSerializer(Common::Serializer &s) { + s.syncAsUint32LE(signature); + s.syncAsUint32LE(count); + s.syncAsUint32LE(offset); + s.syncAsUint32LE(offsetEntry); + s.syncAsUint32LE(keepIndex); + s.syncAsUint32LE(brightness); + s.syncAsUint32LE(volume); + s.syncAsUint32LE(field_1C); + } + + bool isValid() { + if (signature != SAVEGAME_SIGNATURE) + return false; + + /* Check not needed as it can never be < 0 + if (header.chapter < 0) + return false;*/ + + if (offset < 32) + return false; + + if (offsetEntry < 32) + return false; + + if (keepIndex != 1 && keepIndex != 0) + return false; + + if (brightness < 0 || brightness > 6) + return false; + + if (volume < 0 || volume > 7) + return false; + + if (field_1C != 9) + return false; + + return true; + } }; - struct SavegameEntryHeader { + struct SavegameEntryHeader : Common::Serializable { uint32 signature; - HeaderType type; - uint32 time; + SavegameType type; + TimeValue time; int field_C; ChapterIndex chapter; - EventIndex event; + uint32 value; int field_18; int field_1C; SavegameEntryHeader() { signature = 0; - type = kHeaderTypeNone; - time = 0; + type = kSavegameTypeIndex; + time = kTimeNone; field_C = 0; chapter = kChapterAll; - event = kEventNone; + value = 0; field_18 = 0; field_1C = 0; } - }; - - SaveLoad(LastExpressEngine *engine); - ~SaveLoad(); - - // Save & Load - bool loadGame(GameId id); - void saveGame(SavegameType type, EntityIndex entity, uint32 value); - void saveVolumeBrightness(); + void saveLoadWithSerializer(Common::Serializer &s) { + s.syncAsUint32LE(signature); + s.syncAsUint32LE(type); + s.syncAsUint32LE(time); + s.syncAsUint32LE(field_C); + s.syncAsUint32LE(chapter); + s.syncAsUint32LE(value); + s.syncAsUint32LE(field_18); + s.syncAsUint32LE(field_1C); + } - // Init - void initSavegame(GameId id, bool resetHeaders); - static void writeMainHeader(GameId id); + bool isValid() { + if (signature != SAVEGAME_HEADER) + return false; - // Getting information - static bool isSavegamePresent(GameId id); - static bool isSavegameValid(GameId id); + if (type < kSavegameTypeTime || type > kSavegameTypeTickInterval) + return false; - // Opening save files - static Common::InSaveFile *openForLoading(GameId id); - static Common::OutSaveFile *openForSaving(GameId id); + if (time < kTimeStartGame || time > kTimeCityConstantinople) + return false; - // Headers - static bool loadMainHeader(GameId id, SavegameMainHeader *header); - SavegameEntryHeader *getEntry(uint32 index); - void clearEntries(); + if (field_C <= 0 || field_C >= 15) + return false; - uint32 getLastSavegameTicks() const { return _gameTicksLastSavegame; } + /* No check for < 0, as it cannot happen normaly */ + if (chapter == 0) + return false; -private: - LastExpressEngine *_engine; + return true; + } + }; - uint32 _gameTicksLastSavegame; + SavegameStream *_savegame; Common::Array<SavegameEntryHeader *> _gameHeaders; + uint32 _gameTicksLastSavegame; + + // Headers + static bool loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *header); + void loadEntryHeader(SavegameEntryHeader *header); - static Common::String getSavegameName(GameId id); + // Entries + void writeEntry(SavegameType type, EntityIndex entity, uint32 value); + void readEntry(SavegameType type, EntityIndex entity, uint32 value); + SavegameEntryHeader *getEntry(uint32 index); - static void loadEntryHeader(Common::InSaveFile *save, SavegameEntryHeader *header); + // Opening save files + static Common::String getFilename(GameId id); + static Common::InSaveFile *openForLoading(GameId id); + static Common::OutSaveFile *openForSaving(GameId id); - static bool validateMainHeader(const SavegameMainHeader &header); - static bool validateEntryHeader(const SavegameEntryHeader &header); + // Savegame stream + void initStream(); + void flushStream(GameId id); }; } // End of namespace LastExpress diff --git a/engines/lastexpress/shared.h b/engines/lastexpress/shared.h index 8284ddb9d6..80e227e6a7 100644 --- a/engines/lastexpress/shared.h +++ b/engines/lastexpress/shared.h @@ -342,6 +342,8 @@ enum SceneIndex { kSceneNone = 0, kSceneMenu = 1, + kSceneIntro = 30, + // Inventory kSceneMatchbox = 31, kSceneTelegram = 32, |