From cdfcaa4d48fe31afd795da787fa798054b2824e6 Mon Sep 17 00:00:00 2001 From: Julien Templier Date: Wed, 27 Oct 2010 19:20:20 +0000 Subject: LASTEXPRESS: Implement game loading (last save entry only) svn-id: r53883 --- engines/lastexpress/debug.cpp | 6 +- engines/lastexpress/game/menu.cpp | 50 ++++++-------- engines/lastexpress/game/menu.h | 2 +- engines/lastexpress/game/savegame.cpp | 124 ++++++++++++++++++++++++++-------- engines/lastexpress/game/savegame.h | 13 ++-- engines/lastexpress/game/sound.cpp | 3 +- engines/lastexpress/game/state.h | 2 +- 7 files changed, 131 insertions(+), 69 deletions(-) diff --git a/engines/lastexpress/debug.cpp b/engines/lastexpress/debug.cpp index 87daad2760..8e8c3bab2d 100644 --- a/engines/lastexpress/debug.cpp +++ b/engines/lastexpress/debug.cpp @@ -121,7 +121,7 @@ void Debugger::resetCommand() { SAFE_DELETE(_command); if (_commandParams) - for (int i = 0; i < _numParams; i++) + for (int i = 0; i < _numParams; i++) free(_commandParams[i]); free(_commandParams); @@ -1109,9 +1109,7 @@ bool Debugger::cmdLoadGame(int argc, const char **argv) { if (id == 0 || id > 6) goto error; - if (!getSaveLoad()->loadGame((GameId)(id - 1))) - DebugPrintf("Error loading game with id=%d", id); - + getSaveLoad()->loadGame((GameId)(id - 1)); } else { error: DebugPrintf("Syntax: loadgame (id=1-6)\n"); diff --git a/engines/lastexpress/game/menu.cpp b/engines/lastexpress/game/menu.cpp index 1bdb9ac5ac..a6176f9658 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), - _currentTime(kTimeNone), _lowerTime(kTimeNone), _time(kTimeNone), _currentIndex(0), _index(0), _savegameIndex(0), _delta(0), _handleTimeDelta(false) { + _currentTime(kTimeNone), _lowerTime(kTimeNone), _time(kTimeNone), _currentIndex(0), _index(0), _lastIndex(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 (_savegameIndex == _index) { - showFrame(kOverlayTooltip, getSaveLoad()->isGameFinished(_index, _savegameIndex) ? kTooltipViewGameEnding : kTooltipContinueGame, true); + if (_lastIndex == _index) { + showFrame(kOverlayTooltip, getSaveLoad()->isGameFinished(_index, _lastIndex) ? kTooltipViewGameEnding : kTooltipContinueGame, true); } else { showFrame(kOverlayTooltip, kTooltipContinueRewoundGame, true); } @@ -830,7 +830,7 @@ bool Menu::handleEvent(StartMenuAction action, Common::EventType type) { ////////////////////////////////////////////////////////////////////////// case kMenuForwardGame: - if (_savegameIndex <= _index || _currentTime > _time) { + if (_lastIndex <= _index || _currentTime > _time) { hideOverlays(); break; } @@ -1087,11 +1087,11 @@ void Menu::init(bool doSavegame, SavegameType type, uint32 value) { } // Init savegame & menu values - _savegameIndex = getSaveLoad()->init(_gameId, true); - _lowerTime = getSaveLoad()->getTime(_savegameIndex); + _lastIndex = getSaveLoad()->init(_gameId, true); + _lowerTime = getSaveLoad()->getTime(_lastIndex); if (useSameIndex) - _index = _savegameIndex; + _index = _lastIndex; //if (!getGlobalTimer()) // _index3 = 0; @@ -1112,26 +1112,20 @@ void Menu::init(bool doSavegame, SavegameType type, uint32 value) { } } +// Start a game (or load an existing savegame) void Menu::startGame() { - // TODO: Clear train sequences & savegame headers + getSaveLoad()->clear(); - if (0 /* test for temp filename */ ) { - if (_savegameIndex == _index) + if (_lastIndex == _index) { + setGlobalTimer(0); + if (_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); + getLogic()->resetState(); + getEntities()->setup(true, kEntityPlayer); } + } else { + getSaveLoad()->loadGame(_gameId, _index); } } @@ -1152,7 +1146,7 @@ void Menu::switchGame() { _trainLine->clear(); // Clear loaded savegame data - getSaveLoad()->clearHeaders(); + getSaveLoad()->clear(true); init(false, kSavegameTypeIndex, 0); } @@ -1367,7 +1361,7 @@ void Menu::adjustIndex(uint32 time1, uint32 time2, bool searchEntry) { if (searchEntry) { uint32 currentIndex = _index; - if (_savegameIndex >= _index) { + if (_lastIndex >= _index) { do { // Calculate new delta int32 newDelta = (uint32)getSaveLoad()->getTime(currentIndex) - time1; @@ -1378,7 +1372,7 @@ void Menu::adjustIndex(uint32 time1, uint32 time2, bool searchEntry) { } ++currentIndex; - } while (currentIndex <= _savegameIndex); + } while (currentIndex <= _lastIndex); } } else { index = _index + 1; @@ -1409,7 +1403,7 @@ void Menu::goToTime(uint32 time) { } ++index; - } while (_savegameIndex >= index); + } while (_lastIndex >= index); _currentIndex = entryIndex; updateTime(getSaveLoad()->getTime(entryIndex)); @@ -1424,10 +1418,10 @@ void Menu::setTime() { } void Menu::forwardTime() { - if (_savegameIndex <= _index) + if (_lastIndex <= _index) return; - _currentIndex = _savegameIndex; + _currentIndex = _lastIndex; updateTime(getSaveLoad()->getTime(_currentIndex)); } diff --git a/engines/lastexpress/game/menu.h b/engines/lastexpress/game/menu.h index 086e7fd5df..765a611c41 100644 --- a/engines/lastexpress/game/menu.h +++ b/engines/lastexpress/game/menu.h @@ -183,7 +183,7 @@ private: uint32 _currentIndex; // current savegame entry uint32 _index; - uint32 _savegameIndex; + uint32 _lastIndex; uint32 _delta; bool _handleTimeDelta; diff --git a/engines/lastexpress/game/savegame.cpp b/engines/lastexpress/game/savegame.cpp index 7474245557..224d91eddd 100644 --- a/engines/lastexpress/game/savegame.cpp +++ b/engines/lastexpress/game/savegame.cpp @@ -61,9 +61,7 @@ SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine), _savegame(NULL) } SaveLoad::~SaveLoad() { - clearHeaders(); - - SAFE_DELETE(_savegame); + clear(true); //Zero passed pointers _engine = NULL; @@ -115,7 +113,7 @@ uint32 SaveLoad::init(GameId id, bool resetHeaders) { // Reset cached entry headers if needed if (resetHeaders) { - clearHeaders(); + clear(); SavegameEntryHeader *entryHeader = new SavegameEntryHeader(); entryHeader->time = kTimeCityParis; @@ -156,7 +154,7 @@ void SaveLoad::loadStream(GameId id) { error("SaveLoad::loadStream: savegame stream is invalid"); // Load all savegame data - uint8* buf = new uint8[4096]; + uint8* buf = new uint8[8192]; while (!save->eos() && !save->err()) { uint32 count = save->read(buf, sizeof(buf)); if (count) { @@ -175,11 +173,14 @@ void SaveLoad::loadStream(GameId id) { _savegame->seek(0); } -void SaveLoad::clearHeaders() { +void SaveLoad::clear(bool clearStream) { for (uint i = 0; i < _gameHeaders.size(); i++) SAFE_DELETE(_gameHeaders[i]); _gameHeaders.clear(); + + if (clearStream) + SAFE_DELETE(_savegame); } ////////////////////////////////////////////////////////////////////////// @@ -187,19 +188,41 @@ void SaveLoad::clearHeaders() { ////////////////////////////////////////////////////////////////////////// // Load game -bool SaveLoad::loadGame(GameId id) { +void SaveLoad::loadGame(GameId id) { + // Rewind current savegame + _savegame->seek(0); - if (!SaveLoad::isSavegamePresent(id)) - return false; + // 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; + } - //Common::InSaveFile *save = SaveLoad::openForLoading(id); - // Validate header + // Load the last entry + _savegame->seek(header.offsetEntry); - error("SaveLoad::loadgame: not implemented!"); + SavegameType type = kSavegameTypeIndex; + EntityIndex entity = kEntityPlayer; + uint32 val = 0; + readEntry(&type, &entity, &val, header.keepIndex == 1); + + // Setup last loading time + _gameTicksLastSavegame = getState()->timeTicks; + + if (header.keepIndex) { + getSound()->clearQueue(); + + readEntry(&type, &entity, &val, false); + } + + getEntities()->reset(); + getEntities()->setup(false, entity); } -bool SaveLoad::loadGame2(GameId id) { - error("SaveLoad::loadgame2: not implemented!"); +// Load a specific game entry +void SaveLoad::loadGame(GameId id, uint32 index) { + error("SaveLoad::loadGame: not implemented! (only loading the last entry is working for now)"); } // Save game @@ -337,8 +360,6 @@ void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { Common::Serializer ser(NULL, _savegame); header.saveLoadWithSerializer(ser); - computeOffset(); - // Write game data WRITE_ENTRY("entity index", ser.syncAsUint32LE(entity), 4); WRITE_ENTRY("state", getState()->saveLoadWithSerializer(ser), 4 + 4 + 4 + 4 + 1 + 4 + 4); @@ -346,14 +367,14 @@ void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { WRITE_ENTRY("positions", getEntities()->savePositions(ser), 4 * 1000); WRITE_ENTRY("compartments", getEntities()->saveCompartments(ser), 4 * 16 * 2); WRITE_ENTRY("progress", getProgress().saveLoadWithSerializer(ser), 4 * 128); - WRITE_ENTRY("events", getState()->saveEvents(ser), 512); + WRITE_ENTRY("events", getState()->syncEvents(ser), 512); WRITE_ENTRY("inventory", getInventory()->saveLoadWithSerializer(ser), 7 * 32); WRITE_ENTRY("objects", getObjects()->saveLoadWithSerializer(ser), 5 * 128); WRITE_ENTRY("entities", getEntities()->saveLoadWithSerializer(ser), 1262 * 40); WRITE_ENTRY("sound", getSound()->saveLoadWithSerializer(ser), 3 * 4 + getSound()->count() * 64); WRITE_ENTRY("savepoints", getSavePoints()->saveLoadWithSerializer(ser), 128 * 16 + 4 + getSavePoints()->count() * 16); - header.offset = computeOffset(originalPosition); + header.offset = (uint32)_savegame->pos() - (originalPosition + 32); // Add padding if necessary while (header.offset & 0xF) { @@ -376,8 +397,63 @@ void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { _savegame->seek(endPosition); } -void SaveLoad::readEntry(SavegameType type, EntityIndex entity, uint32 value) { - warning("SaveLoad::readEntry: not implemented!"); +void SaveLoad::readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, bool keepIndex) { +#define LOAD_ENTRY(name, func, val) { \ + uint32 _prevPosition = (uint32)_savegame->pos(); \ + func; \ + uint32 _count = (uint32)_savegame->pos() - _prevPosition; \ + debugC(kLastExpressDebugSavegame, "Savegame: Reading " #name ": %d bytes", _count); \ + if (_count != val) \ + error("SaveLoad::readEntry: Number of bytes read (%d) differ from expected count (%d)", _count, val); \ +} + +#define LOAD_ENTRY_ONLY(name, func) { \ + uint32 _prevPosition = (uint32)_savegame->pos(); \ + func; \ + uint32 _count = (uint32)_savegame->pos() - _prevPosition; \ + debugC(kLastExpressDebugSavegame, "Savegame: Reading " #name ": %d bytes", _count); \ +} + + if (!type || !entity || !val) + error("SaveLoad::readEntry: Invalid parameters passed!"); + + // Load entry header + SavegameEntryHeader entry; + Common::Serializer ser(_savegame, NULL); + entry.saveLoadWithSerializer(ser); + + if (!entry.isValid()) + error("SaveLoad::readEntry: entry header is invalid!"); + + // Init type, entity & value + *type = entry.type; + *val = entry.value; + + // Save position + uint32 originalPosition = (uint32)_savegame->pos(); + + // Load game data + LOAD_ENTRY("entity index", ser.syncAsUint32LE(*entity), 4); + LOAD_ENTRY("state", getState()->saveLoadWithSerializer(ser), 4 + 4 + 4 + 4 + 1 + 4 + 4); + LOAD_ENTRY("selected item", getInventory()->saveSelectedItem(ser), 4); + LOAD_ENTRY("positions", getEntities()->savePositions(ser), 4 * 1000); + LOAD_ENTRY("compartments", getEntities()->saveCompartments(ser), 4 * 16 * 2); + LOAD_ENTRY("progress", getProgress().saveLoadWithSerializer(ser), 4 * 128); + LOAD_ENTRY("events", getState()->syncEvents(ser), 512); + LOAD_ENTRY("inventory", getInventory()->saveLoadWithSerializer(ser), 7 * 32); + LOAD_ENTRY("objects", getObjects()->saveLoadWithSerializer(ser), 5 * 128); + LOAD_ENTRY("entities", getEntities()->saveLoadWithSerializer(ser), 1262 * 40); + LOAD_ENTRY_ONLY("sound", getSound()->saveLoadWithSerializer(ser)); + LOAD_ENTRY_ONLY("savepoints", getSavePoints()->saveLoadWithSerializer(ser)); + + // Update chapter + getProgress().chapter = entry.chapter; + + // Skip padding + uint32 offset = _savegame->pos() - originalPosition; + if (offset & 0xF) { + _savegame->seek((~offset & 0xF) + 1, SEEK_SET); + } } SaveLoad::SavegameEntryHeader *SaveLoad::getEntry(uint32 index) { @@ -387,14 +463,6 @@ SaveLoad::SavegameEntryHeader *SaveLoad::getEntry(uint32 index) { return _gameHeaders[index]; } -uint32 SaveLoad::computeOffset(uint32 originalPosition) { - warning("SaveLoad::computePadding: not implemented!"); - if (!_savegame) - error("SaveLoad::computeOffset: savegame stream is invalid"); - - return ((uint32)_savegame->pos() - (originalPosition + 32)); -} - ////////////////////////////////////////////////////////////////////////// // Checks ////////////////////////////////////////////////////////////////////////// diff --git a/engines/lastexpress/game/savegame.h b/engines/lastexpress/game/savegame.h index c6a42fcb3e..d3ba5d13af 100644 --- a/engines/lastexpress/game/savegame.h +++ b/engines/lastexpress/game/savegame.h @@ -94,14 +94,15 @@ public: // Init void create(GameId id); - void clearHeaders(); + void clear(bool clearStream = false); uint32 init(GameId id, bool resetHeaders); // Save & Load - bool loadGame(GameId id); - bool loadGame2(GameId id); + void loadGame(GameId id); + void loadGame(GameId id, uint32 index); void saveGame(SavegameType type, EntityIndex entity, uint32 value); + void loadVolumeBrightness(); void saveVolumeBrightness(); // Getting information @@ -266,10 +267,10 @@ private: static bool loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *header); // Entries - void writeEntry(SavegameType type, EntityIndex entity, uint32 value); - void readEntry(SavegameType type, EntityIndex entity, uint32 value); + void writeEntry(SavegameType type, EntityIndex entity, uint32 val); + void readEntry(SavegameType *type, EntityIndex *entity, uint32 *val, bool keepIndex); + SavegameEntryHeader *getEntry(uint32 index); - uint32 computeOffset(uint32 originalPosition = 0); // Opening save files static Common::String getFilename(GameId id); diff --git a/engines/lastexpress/game/sound.cpp b/engines/lastexpress/game/sound.cpp index 3c87978657..0dfc38b1b0 100644 --- a/engines/lastexpress/game/sound.cpp +++ b/engines/lastexpress/game/sound.cpp @@ -541,7 +541,8 @@ void SoundManager::saveLoadWithSerializer(Common::Serializer &s) { } } } else { - error("Sound::saveLoadWithSerializer: not implemented!"); + warning("Sound::saveLoadWithSerializer: not implemented!"); + s.skip(numEntries * 64); } } diff --git a/engines/lastexpress/game/state.h b/engines/lastexpress/game/state.h index 469f7a0ea2..135ee6bb4e 100644 --- a/engines/lastexpress/game/state.h +++ b/engines/lastexpress/game/state.h @@ -544,7 +544,7 @@ public: s.syncAsUint32LE(sceneBackup2); } - void saveEvents(Common::Serializer &s) { + void syncEvents(Common::Serializer &s) { for (uint i = 0; i < ARRAYSIZE(events); i++) s.syncAsByte(events[i]); } -- cgit v1.2.3