LASTEXPRESS: Implement game loading (last save entry only)
svn-id: r53883
This commit is contained in:
parent
5688a393b4
commit
cdfcaa4d48
7 changed files with 131 additions and 69 deletions
|
@ -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");
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -183,7 +183,7 @@ private:
|
|||
|
||||
uint32 _currentIndex; // current savegame entry
|
||||
uint32 _index;
|
||||
uint32 _savegameIndex;
|
||||
uint32 _lastIndex;
|
||||
uint32 _delta;
|
||||
bool _handleTimeDelta;
|
||||
|
||||
|
|
|
@ -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
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue