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); } }
uint32 SaveLoad::init(GameId id, bool resetHeaders) { initStream(); // Load game data loadStream(id); // Get the main header Common::Serializer ser(_savegame, NULL); SavegameMainHeader mainHeader; mainHeader.saveLoadWithSerializer(ser); if (!mainHeader.isValid()) error("SaveLoad::init - Savegame seems to be corrupted (invalid header)"); // Reset cached entry headers if needed if (resetHeaders) { clear(); SavegameEntryHeader *entryHeader = new SavegameEntryHeader(); entryHeader->time = kTimeCityParis; entryHeader->chapter = kChapter1; _gameHeaders.push_back(entryHeader); } // Read the list of entry headers if (_savegame->size() > 32) { while (_savegame->pos() < _savegame->size() && !_savegame->eos() && !_savegame->err()) { // Update sound queue while we go through the savegame getSound()->updateQueue(); SavegameEntryHeader *entry = new SavegameEntryHeader(); entry->saveLoadWithSerializer(ser); if (!entry->isValid()) break; _gameHeaders.push_back(entry); _savegame->seek(entry->offset, SEEK_CUR); } } // return the index to the current save game entry (we store count + 1 entries, so we're good) return mainHeader.count; }
////////////////////////////////////////////////////////////////////////// // Entries ////////////////////////////////////////////////////////////////////////// void SaveLoad::writeEntry(SavegameType type, EntityIndex entity, uint32 value) { #define WRITE_ENTRY(name, func, val) { \ uint32 _prevPosition = (uint32)_savegame->pos(); \ func; \ uint32 _count = (uint32)_savegame->pos() - _prevPosition; \ debugC(kLastExpressDebugSavegame, "Savegame: Writing " #name ": %d bytes", _count); \ 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 = (uint32)getState()->time; header.chapter = getProgress().chapter; header.value = value; // Save position uint32 originalPosition = (uint32)_savegame->pos(); // Write header Common::Serializer ser(NULL, _savegame); header.saveLoadWithSerializer(ser); // Write game data WRITE_ENTRY("entity index", ser.syncAsUint32LE(entity), 4); WRITE_ENTRY("state", getState()->saveLoadWithSerializer(ser), 4 + 4 + 4 + 4 + 1 + 4 + 4); WRITE_ENTRY("selected item", getInventory()->saveSelectedItem(ser), 4); 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()->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 = (uint32)_savegame->pos() - (originalPosition + 32); // Add padding if necessary while (header.offset & 0xF) { _savegame->writeByte(0); header.offset++; } // Save end position uint32 endPosition = (uint32)_savegame->pos(); // Validate entry header if (!header.isValid()) error("SaveLoad::writeEntry: entry header is invalid"); // Save the header with the updated info _savegame->seek(originalPosition); header.saveLoadWithSerializer(ser); // Move back to the end of the entry _savegame->seek(endPosition); }
// 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; } if (!_savegame) error("SaveLoad::saveGame: savegame stream is invalid"); // 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()) { warning("SaveLoad::saveGame: Invalid entry. This savegame might be corrupted!"); _savegame->seek(header.offset); } else if (getState()->time < entry.time || (type == kSavegameTypeTickInterval && getState()->time == entry.time)) { // Not ready to save a game, skipping! return; } else 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 = (uint32)_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 = (uint32)_savegame->pos(); // Save ticks _gameTicksLastSavegame = getState()->timeTicks; } // Validate the main header if (!header.isValid()) error("SaveLoad::saveGame: main game header is invalid!"); // Write the main header _savegame->seek(0); Common::Serializer ser(NULL, _savegame); header.saveLoadWithSerializer(ser); flushStream(getMenu()->getGameId()); }