aboutsummaryrefslogtreecommitdiff
path: root/engines/lastexpress
diff options
context:
space:
mode:
authorJulien Templier2010-10-26 00:41:42 +0000
committerJulien Templier2010-10-26 00:41:42 +0000
commitae2c4b7cd209b310ae9e01edafa9613c06ff421f (patch)
treee686f651a78fd7997a050e48f8300099ae333229 /engines/lastexpress
parent8217efc74a9b5066bd158e7fef5edd257aec9f16 (diff)
downloadscummvm-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.cpp18
-rw-r--r--engines/lastexpress/game/logic.h1
-rw-r--r--engines/lastexpress/game/menu.cpp135
-rw-r--r--engines/lastexpress/game/menu.h3
-rw-r--r--engines/lastexpress/game/savegame.cpp372
-rw-r--r--engines/lastexpress/game/savegame.h211
-rw-r--r--engines/lastexpress/shared.h2
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,