static void SaveBuffers(void) { int i; int num_buffers; int log_level = DEBUG3; SavedBuffer *saved_buffers; volatile BufferDesc *bufHdr; // XXX: Do we really need volatile here? FILE *file = NULL; int database_counter= 0; Oid prev_database = InvalidOid; Oid prev_filenode = InvalidOid; ForkNumber prev_forknum = InvalidForkNumber; BlockNumber prev_blocknum = InvalidBlockNumber; BlockNumber range_counter = 0; const char *savefile_path; /* * XXX: If the memory request fails, ask for a smaller memory chunk, and use * it to create chunks of save-files, and make the workers read those chunks. * * This is not a concern as of now, so deferred; there's at least one other * place that allocates (NBuffers * (much_bigger_struct)), so this seems to * be an acceptable practice. */ saved_buffers = (SavedBuffer *) palloc(sizeof(SavedBuffer) * NBuffers); /* Lock the buffer partitions for reading. */ for (i = 0; i < NUM_BUFFER_PARTITIONS; ++i) LWLockAcquire(FirstBufMappingLock + i, LW_SHARED); /* Scan and save a list of valid buffers. */ for (num_buffers = 0, i = 0, bufHdr = BufferDescriptors; i < NBuffers; ++i, ++bufHdr) { /* Lock each buffer header before inspecting. */ LockBufHdr(bufHdr); /* Skip invalid buffers */ if ((bufHdr->flags & BM_VALID) && (bufHdr->flags & BM_TAG_VALID)) { saved_buffers[num_buffers].database = bufHdr->tag.rnode.dbNode; saved_buffers[num_buffers].filenode = bufHdr->tag.rnode.relNode; saved_buffers[num_buffers].forknum = bufHdr->tag.forkNum; saved_buffers[num_buffers].blocknum = bufHdr->tag.blockNum; ++num_buffers; } UnlockBufHdr(bufHdr); } /* Unlock the buffer partitions in reverse order, to avoid a deadlock. */ for (i = NUM_BUFFER_PARTITIONS - 1; i >= 0; --i) LWLockRelease(FirstBufMappingLock + i); /* * Sort the list, so that we can optimize the storage of these buffers. * * The side-effect of this storage optimization is that when reading the * blocks back from relation forks, it leads to sequential reads, which * improve the restore speeds quite considerably as compared to random reads * from different blocks all over the data directory. */ pg_qsort(saved_buffers, num_buffers, sizeof(SavedBuffer), SavedBufferCmp); /* Connect to the database and start a transaction for database name lookups. */ BackgroundWorkerInitializeConnection(guc_default_database, NULL); SetCurrentStatementStartTimestamp(); StartTransactionCommand(); PushActiveSnapshot(GetTransactionSnapshot()); pgstat_report_activity(STATE_RUNNING, "saving buffers"); for (i = 0; i < num_buffers; ++i) { int j; SavedBuffer *buf = &saved_buffers[i]; if (i == 0) { /* * Special case for global objects. The sort brings them to the * front of the list. */ /* Make sure the first buffer we save belongs to global object. */ Assert(buf->database == InvalidOid); /* * Database number (and save-file name) 1 is reserverd for storing * list of buffers of global objects. */ database_counter = 1; savefile_path = getSavefileName(database_counter); file = fileOpen(savefile_path, PG_BINARY_W); writeDBName("", file, savefile_path); prev_database = buf->database; } if (buf->database != prev_database) { char *dbname; /* * We are beginning to process a different database than the * previous one; close the save-file of previous database, and open * a new one. */ ++database_counter; dbname = get_database_name(buf->database); Assert(dbname != NULL); if (file != NULL) fileClose(file, savefile_path); savefile_path = getSavefileName(database_counter); file = fileOpen(savefile_path, PG_BINARY_W); writeDBName(dbname, file, savefile_path); pfree(dbname); /* Reset trackers appropriately */ prev_database = buf->database; prev_filenode = InvalidOid; prev_forknum = InvalidForkNumber; prev_blocknum = InvalidBlockNumber; range_counter = 0; } if (buf->filenode != prev_filenode) { /* We're beginning to process a new relation; emit a record for it. */ fileWrite("r", 1, file, savefile_path); fileWrite(&(buf->filenode), sizeof(Oid), file, savefile_path); /* Reset trackers appropriately */ prev_filenode = buf->filenode; prev_forknum = InvalidForkNumber; prev_blocknum = InvalidBlockNumber; range_counter = 0; } if (buf->forknum != prev_forknum) { /* * We're beginning to process a new fork of this relation; add a * record for it. */ fileWrite("f", 1, file, savefile_path); fileWrite(&(buf->forknum), sizeof(ForkNumber), file, savefile_path); /* Reset trackers appropriately */ prev_forknum = buf->forknum; prev_blocknum = InvalidBlockNumber; range_counter = 0; } ereport(log_level, (errmsg("writer: writing block db %d filenode %d forknum %d blocknum %d", database_counter, prev_filenode, prev_forknum, buf->blocknum))); fileWrite("b", 1, file, savefile_path); fileWrite(&(buf->blocknum), sizeof(BlockNumber), file, savefile_path); prev_blocknum = buf->blocknum; /* * If a continuous range of blocks follows this block, then emit one * entry for the range, instead of one for each block. */ range_counter = 0; for ( j = i+1; j < num_buffers; ++j) { SavedBuffer *tmp = &saved_buffers[j]; if (tmp->database == prev_database && tmp->filenode == prev_filenode && tmp->forknum == prev_forknum && tmp->blocknum == (prev_blocknum + range_counter + 1)) { ++range_counter; } } if (range_counter != 0) { ereport(log_level, (errmsg("writer: writing range db %d filenode %d forknum %d blocknum %d range %d", database_counter, prev_filenode, prev_forknum, prev_blocknum, range_counter))); fileWrite("N", 1, file, savefile_path); fileWrite(&range_counter, sizeof(range_counter), file, savefile_path); i += range_counter; } } ereport(LOG, (errmsg("Buffer Saver: saved metadata of %d blocks", num_buffers))); Assert(file != NULL); fileClose(file, savefile_path); pfree(saved_buffers); PopActiveSnapshot(); CommitTransactionCommand(); pgstat_report_activity(STATE_IDLE, NULL); }
LoadgameResult DMEngine::loadgame(int16 slot) { if (slot == -1 && _newGameFl == k0_modeLoadSavedGame) return kDMLoadgameFailure; bool fadePalette = true; Common::String fileName; Common::SaveFileManager *saveFileManager = nullptr; Common::InSaveFile *file = nullptr; struct { SaveTarget _saveTarget; int32 _saveVersion; OriginalSaveFormat _saveFormat; OriginalSavePlatform _savePlatform; uint16 _dungeonId; } dmSaveHeader; if (_newGameFl) { //L1366_B_FadePalette = !F0428_DIALOG_RequireGameDiskInDrive_NoDialogDrawn(C0_DO_NOT_FORCE_DIALOG_DM_CSB, true); _restartGameAllowed = false; _championMan->_partyChampionCount = 0; _championMan->_leaderHandObject = Thing::_none; } else { fileName = getSavefileName(slot); saveFileManager = _system->getSavefileManager(); file = saveFileManager->openForLoading(fileName); SaveGameHeader header; readSaveGameHeader(file, &header); warning("MISSING CODE: missing check for matching format and platform in save in f435_loadgame"); dmSaveHeader._saveTarget = (SaveTarget)file->readSint32BE(); dmSaveHeader._saveVersion = file->readSint32BE(); dmSaveHeader._saveFormat = (OriginalSaveFormat)file->readSint32BE(); dmSaveHeader._savePlatform = (OriginalSavePlatform)file->readSint32BE(); // Skip _gameId, which was useless file->readSint32BE(); dmSaveHeader._dungeonId = file->readUint16BE(); _gameTime = file->readSint32BE(); // G0349_ul_LastRandomNumber = L1371_s_GlobalData.LastRandomNumber; _championMan->_partyChampionCount = file->readUint16BE(); _dungeonMan->_partyMapX = file->readSint16BE(); _dungeonMan->_partyMapY = file->readSint16BE(); _dungeonMan->_partyDir = (Direction)file->readUint16BE(); _dungeonMan->_partyMapIndex = file->readByte(); _championMan->_leaderIndex = (ChampionIndex)file->readSint16BE(); _championMan->_magicCasterChampionIndex = (ChampionIndex)file->readSint16BE(); _timeline->_eventCount = file->readUint16BE(); _timeline->_firstUnusedEventIndex = file->readUint16BE(); _timeline->_eventMaxCount = file->readUint16BE(); _groupMan->_currActiveGroupCount = file->readUint16BE(); _projexpl->_lastCreatureAttackTime = file->readSint32BE(); _projexpl->_lastPartyMovementTime = file->readSint32BE(); _disabledMovementTicks = file->readSint16BE(); _projectileDisableMovementTicks = file->readSint16BE(); _lastProjectileDisabledMovementDirection = file->readSint16BE(); _championMan->_leaderHandObject = Thing(file->readUint16BE()); _groupMan->_maxActiveGroupCount = file->readUint16BE(); if (!_restartGameRequest) { _timeline->initTimeline(); _groupMan->initActiveGroups(); } _groupMan->loadActiveGroupPart(file); _championMan->loadPartyPart2(file); _timeline->loadEventsPart(file); _timeline->loadTimelinePart(file); // read sentinel uint32 sentinel = file->readUint32BE(); assert(sentinel == 0x6f85e3d3); _dungeonId = dmSaveHeader._dungeonId; } _dungeonMan->loadDungeonFile(file); delete file; if (_newGameFl) { _timeline->initTimeline(); _groupMan->initActiveGroups(); if (fadePalette) { _displayMan->startEndFadeToPalette(_displayMan->_blankBuffer); delay(1); _displayMan->fillScreen(kDMColorBlack); _displayMan->startEndFadeToPalette(_displayMan->_paletteTopAndBottomScreen); } } else { _restartGameAllowed = true; switch (getGameLanguage()) { // localized case Common::DE_DEU: _dialog->dialogDraw(nullptr, "SPIEL WIRD GELADEN . . .", nullptr, nullptr, nullptr, nullptr, true, true, true); break; case Common::FR_FRA: _dialog->dialogDraw(nullptr, "CHARGEMENT DU JEU . . .", nullptr, nullptr, nullptr, nullptr, true, true, true); break; default: _dialog->dialogDraw(nullptr, "LOADING GAME . . .", nullptr, nullptr, nullptr, nullptr, true, true, true); break; } } _championMan->_partyDead = false; return kDMLoadgameSuccess; }
static void ReadBlocks(int filenum) { FILE *file; char record_type; char *dbname; Oid record_filenode; ForkNumber record_forknum; BlockNumber record_blocknum; BlockNumber record_range; int log_level = DEBUG3; Oid relOid = InvalidOid; Relation rel = NULL; bool skip_relation = false; bool skip_fork = false; bool skip_block = false; BlockNumber nblocks = 0; BlockNumber blocks_restored = 0; const char *filepath; /* * If this condition changes, then this code, and the code in the writer * will need to be changed; especially the format specifiers in log and * error messages. */ StaticAssertStmt(MaxBlockNumber == 0xFFFFFFFE, "Code may need review."); filepath = getSavefileName(filenum); file = fileOpen(filepath, PG_BINARY_R); dbname = readDBName(file, filepath); /* * When restoring global objects, the dbname is zero-length string, and non- * zero length otherwise. And filenum is never expected to be smaller than 1. */ Assert(filenum >= 1); Assert(filenum == 1 ? strlen(dbname) == 0 : strlen(dbname) > 0); /* To restore the global objects, use default database */ BackgroundWorkerInitializeConnection(filenum == 1 ? guc_default_database : dbname, NULL); SetCurrentStatementStartTimestamp(); StartTransactionCommand(); SPI_connect(); PushActiveSnapshot(GetTransactionSnapshot()); pgstat_report_activity(STATE_RUNNING, "restoring buffers"); /* * Note that in case of a read error, we will leak relcache entry that we may * currently have open. In case of EOF, we close the relation after the loop. */ while (fileRead(&record_type, 1, file, true, filepath)) { /* * If we want to process the signals, this seems to be the best place * to do it. Generally the backends refrain from processing config file * while in transaction, but that's more for the fear of allowing GUC * changes to affect expression evaluation, causing different results * for the same expression in a transaction. Since this worker is not * processing any queries, it is okay to process the config file here. * * Even though it's okay to process SIGHUP here, doing so doesn't add * any value. The only reason we might want to process config file here * would be to allow the user to interrupt the BlockReader's operation * by changing this extenstion's GUC parameter. But the user can do that * anyway, using SIGTERM or pg_terminate_backend(). */ /* Stop processing the save-file if the Postmaster wants us to die. */ if (got_sigterm) break; ereport(log_level, (errmsg("record type %x - %c", record_type, record_type))); switch (record_type) { case 'r': { /* Close the previous relation, if any. */ if (rel) { relation_close(rel, AccessShareLock); rel = NULL; } record_forknum = InvalidForkNumber; record_blocknum = InvalidBlockNumber; nblocks = 0; fileRead(&record_filenode, sizeof(Oid), file, false, filepath); relOid = GetRelOid(record_filenode); ereport(log_level, (errmsg("processing filenode %u, relation %u", record_filenode, relOid))); /* * If the relation has been rewritten/dropped since we saved it, * just skip it and process the next relation. */ if (relOid == InvalidOid) skip_relation = true; else { skip_relation = false; /* Open the relation */ rel = relation_open(relOid, AccessShareLock); RelationOpenSmgr(rel); } } break; case 'f': { record_blocknum = InvalidBlockNumber; nblocks = 0; fileRead(&record_forknum, sizeof(ForkNumber), file, false, filepath); if (skip_relation) continue; if (rel == NULL) ereport(ERROR, (errmsg("found a fork record without a preceeding relation record"))); ereport(log_level, (errmsg("processing fork %d", record_forknum))); if (!smgrexists(rel->rd_smgr, record_forknum)) skip_fork = true; else { skip_fork = false; nblocks = RelationGetNumberOfBlocksInFork(rel, record_forknum); } } break; case 'b': { if (record_forknum == InvalidForkNumber) ereport(ERROR, (errmsg("found a block record without a preceeding fork record"))); fileRead(&record_blocknum, sizeof(BlockNumber), file, false, filepath); if (skip_relation || skip_fork) continue; /* * Don't try to read past the file; the file may have been shrunk * by a vaccum/truncate operation. */ if (record_blocknum >= nblocks) { ereport(log_level, (errmsg("reader %d skipping block filenode %u forknum %d blocknum %u", filenum, record_filenode, record_forknum, record_blocknum))); skip_block = true; continue; } else { Buffer buf; skip_block = false; ereport(log_level, (errmsg("reader %d reading block filenode %u forknum %d blocknum %u", filenum, record_filenode, record_forknum, record_blocknum))); buf = ReadBufferExtended(rel, record_forknum, record_blocknum, RBM_NORMAL, NULL); ReleaseBuffer(buf); ++blocks_restored; } } break; case 'N': { BlockNumber block; Assert(record_blocknum != InvalidBlockNumber); if (record_blocknum == InvalidBlockNumber) ereport(ERROR, (errmsg("found a block range record without a preceeding block record"))); fileRead(&record_range, sizeof(int), file, false, filepath); if (skip_relation || skip_fork || skip_block) continue; ereport(log_level, (errmsg("reader %d reading range filenode %u forknum %d blocknum %u range %u", filenum, record_filenode, record_forknum, record_blocknum, record_range))); for (block = record_blocknum + 1; block <= (record_blocknum + record_range); ++block) { Buffer buf; /* * Don't try to read past the file; the file may have been * shrunk by a vaccum operation. */ if (block >= nblocks) { ereport(log_level, (errmsg("reader %d skipping block range filenode %u forknum %d start %u end %u", filenum, record_filenode, record_forknum, block, record_blocknum + record_range))); break; } buf = ReadBufferExtended(rel, record_forknum, block, RBM_NORMAL, NULL); ReleaseBuffer(buf); ++blocks_restored; } } break; default: { ereport(ERROR, (errmsg("found unexpected save-file marker %x - %c)", record_type, record_type))); Assert(false); } break; } } if (rel) relation_close(rel, AccessShareLock); ereport(LOG, (errmsg("Block Reader %d: restored %u blocks", filenum, blocks_restored))); SPI_finish(); PopActiveSnapshot(); CommitTransactionCommand(); pgstat_report_activity(STATE_IDLE, NULL); fileClose(file, filepath); /* Remove the save-file */ if (remove(filepath) != 0) ereport(ERROR, (errcode_for_file_access(), errmsg("error removing file \"%s\" : %m", filepath))); }
bool DMEngine::writeCompleteSaveFile(int16 saveSlot, Common::String& saveDescription, int16 saveAndPlayChoice) { Common::String savefileName = getSavefileName(saveSlot); Common::SaveFileManager *saveFileManager = _system->getSavefileManager(); Common::OutSaveFile *file = saveFileManager->openForSaving(savefileName); if (!file) return false; writeSaveGameHeader(file, saveDescription); file->writeSint32BE(_gameVersion->_saveTargetToWrite); file->writeSint32BE(1); // save version file->writeSint32BE(_gameVersion->_origSaveFormatToWrite); file->writeSint32BE(_gameVersion->_origPlatformToWrite); // Was _gameID, useless. file->writeSint32BE(0); file->writeUint16BE(_dungeonId); // write C0_SAVE_PART_GLOBAL_DATA part file->writeSint32BE(_gameTime); //L1348_s_GlobalData.LastRandomNumber = G0349_ul_LastRandomNumber; file->writeUint16BE(_championMan->_partyChampionCount); file->writeSint16BE(_dungeonMan->_partyMapX); file->writeSint16BE(_dungeonMan->_partyMapY); file->writeUint16BE(_dungeonMan->_partyDir); file->writeByte(_dungeonMan->_partyMapIndex); file->writeSint16BE(_championMan->_leaderIndex); file->writeSint16BE(_championMan->_magicCasterChampionIndex); file->writeUint16BE(_timeline->_eventCount); file->writeUint16BE(_timeline->_firstUnusedEventIndex); file->writeUint16BE(_timeline->_eventMaxCount); file->writeUint16BE(_groupMan->_currActiveGroupCount); file->writeSint32BE(_projexpl->_lastCreatureAttackTime); file->writeSint32BE(_projexpl->_lastPartyMovementTime); file->writeSint16BE(_disabledMovementTicks); file->writeSint16BE(_projectileDisableMovementTicks); file->writeSint16BE(_lastProjectileDisabledMovementDirection); file->writeUint16BE(_championMan->_leaderHandObject.toUint16()); file->writeUint16BE(_groupMan->_maxActiveGroupCount); // write C1_SAVE_PART_ACTIVE_GROUP part _groupMan->saveActiveGroupPart(file); // write C2_SAVE_PART_PARTY part _championMan->savePartyPart2(file); // write C3_SAVE_PART_EVENTS part _timeline->saveEventsPart(file); // write C4_SAVE_PART_TIMELINE part _timeline->saveTimelinePart(file); // write sentinel file->writeUint32BE(0x6f85e3d3); // save _g278_dungeonFileHeader DungeonFileHeader &header = _dungeonMan->_dungeonFileHeader; file->writeUint16BE(header._ornamentRandomSeed); file->writeUint16BE(header._rawMapDataSize); file->writeByte(header._mapCount); file->writeByte(0); // to match the structure of dungeon.dat, will be discarded file->writeUint16BE(header._textDataWordCount); file->writeUint16BE(header._partyStartLocation); file->writeUint16BE(header._squareFirstThingCount); for (uint16 i = 0; i < 16; ++i) file->writeUint16BE(header._thingCounts[i]); // save _g277_dungeonMaps for (uint16 i = 0; i < _dungeonMan->_dungeonFileHeader._mapCount; ++i) { Map &map = _dungeonMan->_dungeonMaps[i]; file->writeUint16BE(map._rawDunDataOffset); file->writeUint32BE(0); // to match the structure of dungeon.dat, will be discarded file->writeByte(map._offsetMapX); file->writeByte(map._offsetMapY); uint16 tmp; tmp = ((map._height & 0x1F) << 11) | ((map._width & 0x1F) << 6) | (map._level & 0x3F); file->writeUint16BE(tmp); tmp = ((map._randFloorOrnCount & 0xF) << 12) | ((map._floorOrnCount & 0xF) << 8) | ((map._randWallOrnCount & 0xF) << 4) | (map._wallOrnCount & 0xF); file->writeUint16BE(tmp); tmp = ((map._difficulty & 0xF) << 12) | ((map._creatureTypeCount & 0xF) << 4) | (map._doorOrnCount & 0xF); file->writeUint16BE(tmp); tmp = ((map._doorSet1 & 0xF) << 12) | ((map._doorSet0 & 0xF) << 8) | ((map._wallSet & 0xF) << 4) | (map._floorSet & 0xF); file->writeUint16BE(tmp); } // save _g280_dungeonColumnsCumulativeSquareThingCount for (uint16 i = 0; i < _dungeonMan->_dungeonColumCount; ++i) file->writeUint16BE(_dungeonMan->_dungeonColumnsCumulativeSquareThingCount[i]); // save _g283_squareFirstThings for (uint16 i = 0; i < _dungeonMan->_dungeonFileHeader._squareFirstThingCount; ++i) file->writeUint16BE(_dungeonMan->_squareFirstThings[i].toUint16()); // save _g260_dungeonTextData for (uint16 i = 0; i < _dungeonMan->_dungeonFileHeader._textDataWordCount; ++i) file->writeUint16BE(_dungeonMan->_dungeonTextData[i]); // save _g284_thingData for (uint16 thingIndex = 0; thingIndex < 16; ++thingIndex) for (uint16 i = 0; i < _dungeonMan->_thingDataWordCount[thingIndex] * _dungeonMan->_dungeonFileHeader._thingCounts[thingIndex]; ++i) file->writeUint16BE(_dungeonMan->_thingData[thingIndex][i]); // save _g276_dungeonRawMapData for (uint32 i = 0; i < _dungeonMan->_dungeonFileHeader._rawMapDataSize; ++i) file->writeByte(_dungeonMan->_dungeonRawMapData[i]); file->flush(); file->finalize(); delete file; return true; }