LASTEXPRESS: Cleanup game saving

- Move loading of game stream to separate function
 - Add checks for NULL in functions using _savegame
 - Add expected saved size for sound & savepoints

svn-id: r53881
This commit is contained in:
Julien Templier 2010-10-27 19:19:38 +00:00
parent dba75791d9
commit d5e4ab6b20
6 changed files with 100 additions and 78 deletions

View File

@ -1150,7 +1150,7 @@ void Menu::switchGame() {
_trainLine->clear();
// Clear loaded savegame data
getSaveLoad()->clear();
getSaveLoad()->clearHeaders();
init(false, kSavegameTypeIndex, 0);
}

View File

@ -61,7 +61,7 @@ SaveLoad::SaveLoad(LastExpressEngine *engine) : _engine(engine), _savegame(NULL)
}
SaveLoad::~SaveLoad() {
clear();
clearHeaders();
SAFE_DELETE(_savegame);
@ -70,17 +70,19 @@ SaveLoad::~SaveLoad() {
}
void SaveLoad::initStream() {
SAFE_DELETE(_savegame);
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());
error("SaveLoad::flushStream: cannot open savegame (%s)!", getFilename(id).c_str());
save->write(_savegame->getData(), _savegame->size());
if (!_savegame)
error("SaveLoad::flushStream: savegame stream is invalid");
save->write(_savegame->getData(), (uint32)_savegame->size());
delete save;
}
@ -101,28 +103,10 @@ void SaveLoad::create(GameId id) {
uint32 SaveLoad::init(GameId id, bool resetHeaders) {
initStream();
// Open savegame and check size
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 game data
loadStream(id);
// Load all savegame data
uint8* buf = new uint8[4096];
while (!save->eos() && !save->err()) {
uint32 count = save->read(buf, sizeof(buf));
if (count) {
uint32 w = _savegame->write(buf, count);
assert (w == count);
}
}
if (save->err())
error("SaveLoad::init - Error reading savegame");
delete[] buf;
_savegame->seek(0);
SAFE_DELETE(save);
// Load the main header
// Get the main header
Common::Serializer ser(_savegame, NULL);
SavegameMainHeader mainHeader;
mainHeader.saveLoadWithSerializer(ser);
@ -131,7 +115,7 @@ uint32 SaveLoad::init(GameId id, bool resetHeaders) {
// Reset cached entry headers if needed
if (resetHeaders) {
clear();
clearHeaders();
SavegameEntryHeader *entryHeader = new SavegameEntryHeader();
entryHeader->time = kTimeCityParis;
@ -163,7 +147,35 @@ uint32 SaveLoad::init(GameId id, bool resetHeaders) {
return mainHeader.count;
}
void SaveLoad::clear() {
void SaveLoad::loadStream(GameId id) {
Common::InSaveFile *save = openForLoading(id);
if (save->size() < 32)
error("SaveLoad::init - Savegame seems to be corrupted (not enough data: %i bytes)", save->size());
if (!_savegame)
error("SaveLoad::loadStream: savegame stream is invalid");
// Load all savegame data
uint8* buf = new uint8[4096];
while (!save->eos() && !save->err()) {
uint32 count = save->read(buf, sizeof(buf));
if (count) {
uint32 w = _savegame->write(buf, count);
assert (w == count);
}
}
if (save->err())
error("SaveLoad::init - Error reading savegame");
delete[] buf;
delete save;
// Move back to the beginning of the stream
_savegame->seek(0);
}
void SaveLoad::clearHeaders() {
for (uint i = 0; i < _gameHeaders.size(); i++)
SAFE_DELETE(_gameHeaders[i]);
@ -184,8 +196,6 @@ bool SaveLoad::loadGame(GameId id) {
// Validate header
error("SaveLoad::loadgame: not implemented!");
return false;
}
bool SaveLoad::loadGame2(GameId id) {
@ -204,6 +214,9 @@ void SaveLoad::saveGame(SavegameType type, EntityIndex entity, uint32 value) {
return;
}
if (!_savegame)
error("SaveLoad::saveGame: savegame stream is invalid");
// Validate the current entry if it exists
if (header.count > 0) {
_savegame->seek(header.offsetEntry);
@ -232,7 +245,7 @@ void SaveLoad::saveGame(SavegameType type, EntityIndex entity, uint32 value) {
}
if (type != kSavegameTypeEvent2 && type != kSavegameTypeAuto)
header.offsetEntry = _savegame->pos();
header.offsetEntry = (uint32)_savegame->pos();
// Write the savegame entry
writeEntry(type, entity, value);
@ -244,7 +257,7 @@ void SaveLoad::saveGame(SavegameType type, EntityIndex entity, uint32 value) {
header.keepIndex = 1;
} else {
header.keepIndex = 0;
header.offset = _savegame->pos();
header.offset = (uint32)_savegame->pos();
// Save ticks
_gameTicksLastSavegame = getState()->timeTicks;
@ -266,31 +279,6 @@ void SaveLoad::saveVolumeBrightness() {
warning("SaveLoad::saveVolumeBrightness: not implemented!");
}
//////////////////////////////////////////////////////////////////////////
// Static Members
//////////////////////////////////////////////////////////////////////////
// Check if a specific savegame exists
bool SaveLoad::isSavegamePresent(GameId id) {
if (g_system->getSavefileManager()->listSavefiles(getFilename(id)).size() == 0)
return false;
return true;
}
// 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", getFilename(id).c_str());
return false;
}
SavegameMainHeader header;
Common::InSaveFile *save = openForLoading(id);
return loadMainHeader(save, &header);
}
//////////////////////////////////////////////////////////////////////////
// Headers
//////////////////////////////////////////////////////////////////////////
@ -323,24 +311,27 @@ bool SaveLoad::loadMainHeader(Common::InSaveFile *stream, SavegameMainHeader *he
// Entries
//////////////////////////////////////////////////////////////////////////
void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) {
#define WRITE_ENTRY(name, func, expected) { \
uint32 _prevPosition = _savegame->pos(); \
#define WRITE_ENTRY(name, func, val) { \
uint32 _prevPosition = (uint32)_savegame->pos(); \
func; \
uint32 _count = _savegame->pos() - _prevPosition; \
uint32 _count = (uint32)_savegame->pos() - _prevPosition; \
debugC(kLastExpressDebugSavegame, "Savegame: Writing " #name ": %d bytes", _count); \
if (expected != 0 && _count != expected)\
error("SaveLoad::writeEntry: Number of bytes written (%d) differ from expected count (%d)", _count, expected); \
if (_count != val)\
error("SaveLoad::writeEntry: Number of bytes written (%d) differ from expected count (%d)", _count, val); \
}
if (!_savegame)
error("SaveLoad::writeEntry: savegame stream is invalid");
SavegameEntryHeader header;
header.type = type;
header.time = getState()->time;
header.time = (uint32)getState()->time;
header.chapter = getProgress().chapter;
header.value = value;
// Save position
uint32 originalPosition = _savegame->pos();
uint32 originalPosition = (uint32)_savegame->pos();
// Write header
Common::Serializer ser(NULL, _savegame);
@ -359,8 +350,8 @@ void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) {
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), 0);
WRITE_ENTRY("savepoints", getSavePoints()->saveLoadWithSerializer(ser), 0);
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);
@ -371,7 +362,7 @@ void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) {
}
// Save end position
uint32 endPosition = _savegame->pos();
uint32 endPosition = (uint32)_savegame->pos();
// Validate entry header
if (!header.isValid())
@ -398,13 +389,37 @@ SaveLoad::SavegameEntryHeader *SaveLoad::getEntry(uint32 index) {
uint32 SaveLoad::computeOffset(uint32 originalPosition) {
warning("SaveLoad::computePadding: not implemented!");
if (!_savegame)
error("SaveLoad::computeOffset: savegame stream is invalid");
return (_savegame->pos() - originalPosition - 32);
return ((uint32)_savegame->pos() - (originalPosition + 32));
}
//////////////////////////////////////////////////////////////////////////
// Checks
//////////////////////////////////////////////////////////////////////////
// Check if a specific savegame exists
bool SaveLoad::isSavegamePresent(GameId id) {
if (g_system->getSavefileManager()->listSavefiles(getFilename(id)).size() == 0)
return false;
return true;
}
// 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", getFilename(id).c_str());
return false;
}
SavegameMainHeader header;
Common::InSaveFile *save = openForLoading(id);
return loadMainHeader(save, &header);
}
bool SaveLoad::isGameFinished(uint32 menuIndex, uint32 savegameIndex) {
SavegameEntryHeader *data = getEntry(menuIndex);

View File

@ -94,7 +94,7 @@ public:
// Init
void create(GameId id);
void clear();
void clearHeaders();
uint32 init(GameId id, bool resetHeaders);
// Save & Load
@ -278,6 +278,7 @@ private:
// Savegame stream
void initStream();
void loadStream(GameId id);
void flushStream(GameId id);
};

View File

@ -131,6 +131,8 @@ public:
*/
Common::String toString();
uint32 count() { return _savepoints.size(); }
private:
static const uint32 _savePointsMaxSize = 128;

View File

@ -508,14 +508,8 @@ void SoundManager::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsUint32LE(_currentType);
// Compute the number of entries to save
uint32 count = 0;
if (s.isSaving()) {
for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i)
if ((*i)->name2.matchString("NISSND?"))
++count;
}
s.syncAsUint32LE(count);
uint32 numEntries = count();
s.syncAsUint32LE(numEntries);
// Save or load each entry data
if (s.isSaving()) {
@ -551,6 +545,15 @@ void SoundManager::saveLoadWithSerializer(Common::Serializer &s) {
}
}
uint32 SoundManager::count() {
uint32 numEntries = 0;
for (Common::List<SoundEntry *>::iterator i = _cache.begin(); i != _cache.end(); ++i)
if ((*i)->name2.matchString("NISSND?"))
++numEntries;
return numEntries;
}
//////////////////////////////////////////////////////////////////////////
// Game-related functions
//////////////////////////////////////////////////////////////////////////

View File

@ -196,6 +196,7 @@ public:
// Serializable
void saveLoadWithSerializer(Common::Serializer &ser);
uint32 count();
private:
typedef int32 *SoundBuffer;