static byte *readSavegameThumbnail(const Common::String &filename, uint &fileSize, bool &isPNG) { byte *pFileData; Common::SaveFileManager *sfm = g_system->getSavefileManager(); Common::InSaveFile *file = sfm->openForLoading(lastPathComponent(filename, '/')); if (!file) error("Save file \"%s\" could not be loaded.", filename.c_str()); // Seek to the actual PNG image loadString(*file); // Marker (BS25SAVEGAME) Common::String storedVersionID = loadString(*file); // Version if (storedVersionID != "SCUMMVM1") loadString(*file); loadString(*file); // Description uint32 compressedGamedataSize = atoi(loadString(*file).c_str()); loadString(*file); // Uncompressed game data size file->skip(compressedGamedataSize); // Skip the game data and move to the thumbnail itself uint32 thumbnailStart = file->pos(); fileSize = file->size() - thumbnailStart; // Check if the thumbnail is in our own format, or a PNG file. uint32 header = file->readUint32BE(); isPNG = (header != MKTAG('S','C','R','N')); file->seek(-4, SEEK_CUR); pFileData = new byte[fileSize]; file->read(pFileData, fileSize); delete file; return pFileData; }
SaveStateList WageMetaEngine::listSaves(const char *target) const { const uint32 WAGEflag = MKTAG('W','A','G','E'); Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); Common::StringArray filenames; char saveDesc[128] = {0}; Common::String pattern = target; pattern += ".###"; filenames = saveFileMan->listSavefiles(pattern); SaveStateList saveList; for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) { // Obtain the last 3 digits of the filename, since they correspond to the save slot int slotNum = atoi(file->c_str() + file->size() - 3); if (slotNum >= 0 && slotNum <= 999) { Common::InSaveFile *in = saveFileMan->openForLoading(*file); if (in) { saveDesc[0] = 0; in->seek(in->size() - 8); uint32 offset = in->readUint32BE(); uint32 type = in->readUint32BE(); if (type == WAGEflag) { in->seek(offset); type = in->readUint32BE(); if (type == WAGEflag) { in->read(saveDesc, 127); } } saveList.push_back(SaveStateDescriptor(slotNum, saveDesc)); delete in; } } } // Sort saves based on slot number. Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator()); return saveList; }
/** * DoRestore */ static bool DoRestore() { Common::InSaveFile *f = _vm->getSaveFileMan()->openForLoading(g_savedFiles[g_RestoreGameNumber].name); if (f == NULL) { return false; } Common::Serializer s(f, 0); SaveGameHeader hdr; if (!syncSaveGameHeader(s, hdr)) { delete f; // Invalid header, or savegame too new -> skip it return false; } // Load in the data. For older savegame versions, we potentially need to load the data twice, once // for pre 1.5 savegames, and if that fails, a second time for 1.5 savegames int numInterpreters = hdr.numInterpreters; int32 currentPos = f->pos(); for (int tryNumber = 0; tryNumber < ((hdr.ver >= 2) ? 1 : 2); ++tryNumber) { // If it's the second loop iteration, try with the 1.5 savegame number of interpreter contexts if (tryNumber == 1) { f->seek(currentPos); numInterpreters = 80; } // Load the savegame data if (DoSync(s, numInterpreters)) // Data load was successful (or likely), so break out of loop break; } uint32 id = f->readSint32LE(); if (id != (uint32)0xFEEDFACE) error("Incompatible saved game"); bool failed = (f->eos() || f->err()); delete f; if (failed) { GUI::MessageDialog dialog(_("Failed to load game state from file.")); dialog.runModal(); } return !failed; }
Common::SeekableReadStream *EventRecorder::processSaveStream(const Common::String &fileName) { Common::InSaveFile *saveFile; switch (_recordMode) { case kRecorderPlayback: debugC(1, kDebugLevelEventRec, "playback:action=\"Process save file\" filename=%s len=%d", fileName.c_str(), _playbackFile->getHeader().saveFiles[fileName].size); return new Common::MemoryReadStream(_playbackFile->getHeader().saveFiles[fileName].buffer, _playbackFile->getHeader().saveFiles[fileName].size); case kRecorderRecord: saveFile = _realSaveManager->openForLoading(fileName); if (saveFile != NULL) { _playbackFile->addSaveFile(fileName, saveFile); saveFile->seek(0); } return saveFile; default: return NULL; break; } }
SaveStateList DreamWebMetaEngine::listSaves(const char *target) const { Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); Common::StringArray files = saveFileMan->listSavefiles("DREAMWEB.D??"); Common::sort(files.begin(), files.end()); SaveStateList saveList; for(uint i = 0; i < files.size(); ++i) { const Common::String &file = files[i]; Common::InSaveFile *stream = saveFileMan->openForLoading(file); if (!stream) error("cannot open save file %s", file.c_str()); char name[17] = {}; stream->seek(0x61); stream->read(name, sizeof(name) - 1); delete stream; int slotNum = atoi(file.c_str() + file.size() - 2); SaveStateDescriptor sd(slotNum, name); saveList.push_back(sd); } return saveList; }
bool PersistenceService::loadGame(uint slotID) { Common::SaveFileManager *sfm = g_system->getSavefileManager(); Common::InSaveFile *file; // Überprüfen, ob die Slot-ID zulässig ist. if (slotID >= SLOT_COUNT) { error("Tried to load from an invalid slot (%d). Only slot ids form 0 to %d are allowed.", slotID, SLOT_COUNT - 1); return false; } SavegameInformation &curSavegameInfo = _impl->_savegameInformations[slotID]; // Überprüfen, ob der Slot belegt ist. if (!curSavegameInfo.isOccupied) { error("Tried to load from an empty slot (%d).", slotID); return false; } // Überprüfen, ob der Spielstand im angegebenen Slot mit der aktuellen Engine-Version kompatibel ist. // Im Debug-Modus wird dieser Test übersprungen. Für das Testen ist es hinderlich auf die Einhaltung dieser strengen Bedingung zu bestehen, // da sich die Versions-ID bei jeder Codeänderung mitändert. #ifndef DEBUG if (!curSavegameInfo.isCompatible) { error("Tried to load a savegame (%d) that is not compatible with this engine version.", slotID); return false; } #endif byte *compressedDataBuffer = new byte[curSavegameInfo.gamedataLength]; byte *uncompressedDataBuffer = new byte[curSavegameInfo.gamedataUncompressedLength]; Common::String filename = generateSavegameFilename(slotID); file = sfm->openForLoading(filename); file->seek(curSavegameInfo.gamedataOffset); file->read(reinterpret_cast<char *>(&compressedDataBuffer[0]), curSavegameInfo.gamedataLength); if (file->err()) { error("Unable to load the gamedata from the savegame file \"%s\".", filename.c_str()); delete[] compressedDataBuffer; delete[] uncompressedDataBuffer; return false; } // Uncompress game data, if needed. unsigned long uncompressedBufferSize = curSavegameInfo.gamedataUncompressedLength; if (uncompressedBufferSize > curSavegameInfo.gamedataLength) { // Older saved game, where the game data was compressed again. if (!Common::uncompress(reinterpret_cast<byte *>(&uncompressedDataBuffer[0]), &uncompressedBufferSize, reinterpret_cast<byte *>(&compressedDataBuffer[0]), curSavegameInfo.gamedataLength)) { error("Unable to decompress the gamedata from savegame file \"%s\".", filename.c_str()); delete[] uncompressedDataBuffer; delete[] compressedDataBuffer; delete file; return false; } } else { // Newer saved game with uncompressed game data, copy it as-is. memcpy(uncompressedDataBuffer, compressedDataBuffer, uncompressedBufferSize); } InputPersistenceBlock reader(&uncompressedDataBuffer[0], curSavegameInfo.gamedataUncompressedLength, curSavegameInfo.version); // Einzelne Engine-Module depersistieren. bool success = true; success &= Kernel::getInstance()->getScript()->unpersist(reader); // Muss unbedingt nach Script passieren. Da sonst die bereits wiederhergestellten Regions per Garbage-Collection gekillt werden. success &= RegionRegistry::instance().unpersist(reader); success &= Kernel::getInstance()->getGfx()->unpersist(reader); success &= Kernel::getInstance()->getSfx()->unpersist(reader); success &= Kernel::getInstance()->getInput()->unpersist(reader); delete[] compressedDataBuffer; delete[] uncompressedDataBuffer; delete file; if (!success) { error("Unable to unpersist the gamedata from savegame file \"%s\".", filename.c_str()); return false; } return true; }