LASTEXPRESS: Implement game loading (last save entry only)

svn-id: r53883
This commit is contained in:
Julien Templier 2010-10-27 19:20:20 +00:00
parent 5688a393b4
commit cdfcaa4d48
7 changed files with 131 additions and 69 deletions

View file

@ -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> (id=1-6)\n");

View file

@ -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));
}

View file

@ -183,7 +183,7 @@ private:
uint32 _currentIndex; // current savegame entry
uint32 _index;
uint32 _savegameIndex;
uint32 _lastIndex;
uint32 _delta;
bool _handleTimeDelta;

View file

@ -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
//////////////////////////////////////////////////////////////////////////

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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]);
}