void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base) { Record<Cell> cell = mCells.getRecord (cellIndex); Cell& cell2 = base ? cell.mBase : cell.mModified; cell2.restore (reader, 0); /// \todo fix the index CellRef ref; while (cell2.getNextRef (reader, ref)) { /// \todo handle deleted and moved references std::ostringstream stream; stream << "ref#" << mNextId++; ref.load (reader, cell2, stream.str()); Record<CellRef> record2; record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record2.mBase : record2.mModified) = ref; appendRecord (record2); } mCells.setRecord (cellIndex, cell); }
bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted) { // TODO: Try and document reference numbering, I don't think this has been done anywhere else. if (!esm.hasMoreSubs()) return false; // NOTE: We should not need this check. It is a safety check until we have checked // more plugins, and how they treat these moved references. if (esm.isNextSub("MVRF")) { esm.skipRecord(); // skip MVRF esm.skipRecord(); // skip CNDT // That should be it, I haven't seen any other fields yet. } ref.load (esm); // Identify references belonging to a parent file and adapt the ID accordingly. adjustRefNum (ref.mRefNum, esm); if (esm.isNextSub("DELE")) { esm.skipHSub(); deleted = true; } else deleted = false; return true; }
static void printIt(CellRef obj, const char *label) { const Heap &heap = obj.getHeap(); PrintParam param; param.setMaxWidth(78-param.getStartColumn()); if (label != NULL) { std::cout << label; } heap.print(std::cout, obj, param); std::cout << "\n"; }
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base) { Record<Cell> cell = mCells.getRecord (cellIndex); Cell& cell2 = base ? cell.mBase : cell.mModified; CellRef ref; while (cell2.getNextRef (reader, ref)) { /// \todo handle deleted and moved references ref.load (reader, cell2, getNewId()); Record<CellRef> record2; record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record2.mBase : record2.mModified) = ref; appendRecord (record2); } mCells.setRecord (cellIndex, cell); }
bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted, bool ignoreMoves, MovedCellRef *mref) { // TODO: Try and document reference numbering, I don't think this has been done anywhere else. if (!esm.hasMoreSubs()) return false; // NOTE: We should not need this check. It is a safety check until we have checked // more plugins, and how they treat these moved references. if (esm.isNextSub("MVRF")) { if (ignoreMoves) { esm.getHT (mref->mRefNum.mIndex); esm.getHNOT (mref->mTarget, "CNDT"); adjustRefNum (mref->mRefNum, esm); } else { // skip rest of cell record (moved references), they are handled elsewhere esm.skipRecord(); // skip MVRF, CNDT return false; } } ref.load (esm); // Identify references belonging to a parent file and adapt the ID accordingly. adjustRefNum (ref.mRefNum, esm); if (esm.isNextSub("DELE")) { esm.skipHSub(); deleted = true; } else deleted = false; return true; }
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base, std::map<ESM::RefNum, std::string>& cache, CSMDoc::Messages& messages) { Record<Cell> cell = mCells.getRecord (cellIndex); Cell& cell2 = base ? cell.mBase : cell.mModified; CellRef ref; ESM::MovedCellRef mref; bool isDeleted = false; // hack to initialise mindex while (!(mref.mRefNum.mIndex = 0) && ESM::Cell::getNextRef(reader, ref, isDeleted, true, &mref)) { // Keep mOriginalCell empty when in modified (as an indicator that the // original cell will always be equal the current cell). ref.mOriginalCell = base ? cell2.mId : ""; if (cell.get().isExterior()) { // ignoring moved references sub-record; instead calculate cell from coordinates std::pair<int, int> index = ref.getCellIndex(); std::ostringstream stream; stream << "#" << index.first << " " << index.second; ref.mCell = stream.str(); if (!base && // don't try to update base records mref.mRefNum.mIndex != 0) // MVRF tag found { // there is a requirement for a placeholder where the original object was // // see the forum discussions here for more details: // https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30 ref.mOriginalCell = cell2.mId; // It is not always possibe to ignore moved references sub-record and // calculate from coordinates. Some mods may place the ref in positions // outside normal bounds, resulting in non sensical cell id's. This often // happens if the moved ref was deleted. // // Use the target cell from the MVRF tag but if different output an error // message if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1]) { std::cerr << "The Position of moved ref " << ref.mRefID << " does not match the target cell" << std::endl; std::cerr << "Position: #" << index.first << " " << index.second <<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl; std::ostringstream stream; stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1]; ref.mCell = stream.str(); // overwrite } } } else ref.mCell = cell2.mId; // ignore content file number std::map<ESM::RefNum, std::string>::iterator iter = cache.begin(); for (; iter != cache.end(); ++iter) { if (ref.mRefNum.mIndex == iter->first.mIndex) break; } if (isDeleted) { if (iter==cache.end()) { CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell, mCells.getId (cellIndex)); messages.add (id, "Attempt to delete a non-existing reference"); continue; } int index = getIndex (iter->second); Record<CellRef> record = getRecord (index); if (base) { removeRows (index, 1); cache.erase (iter); } else { record.mState = RecordBase::State_Deleted; setRecord (index, record); } continue; } if (iter==cache.end()) { // new reference ref.mId = getNewId(); Record<CellRef> record; record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record.mBase : record.mModified) = ref; appendRecord (record); cache.insert (std::make_pair (ref.mRefNum, ref.mId)); } else { // old reference -> merge ref.mId = iter->second; int index = getIndex (ref.mId); Record<CellRef> record = getRecord (index); record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified; (base ? record.mBase : record.mModified) = ref; setRecord (index, record); } } }
void testGC() { // An explicit GC test Heap heap; heap.setStrict(true); // Will clear top of heap after GC // Create 11 cells of garbage // We put this into a scope so that there are no live references // to it (otherwise it will not be garbage.) { const size_t TRASH_ARITY = 10; CellRef trash = heap.newStr(heap.getConst("foo", TRASH_ARITY)); for (size_t i = 0; i < TRASH_ARITY; i++) { heap.setArg(trash, i, heap.newConst("nothing")); } } CellRef middle; // This is the only reference that will survive { // Create a small term above1(Q) // This will have location [12]: CON:above1/1 // [13]: REF:13 CellRef above1 = heap.newTerm(heap.getConst("above1", 1)); // Create another small term above2(R) // This will have location [14]: CON:above/1 // [15]: REF:15 CellRef above2 = heap.newTerm(heap.getConst("above2", 1)); // Create a term with 3 args // This will have location [14]: CON:middle/3 // [15]: REF:15 // [16]: REF:16 // [17]: REF:17 middle = heap.newTerm(heap.getConst("middle", 3)); // Create a small term below(W) // This will have location [18]: CON:below/1 // [19]: REF:19 CellRef below = heap.newTerm(heap.getConst("below", 1)); // Now we'll create a forward pointer from above1(Q) to point at middle/3 heap.unify(heap.getArg(above1,0), middle); // Then we'll create a back pointer to above1(Q) from middle(A,B,C) // So that B = above1(Q). heap.unify(heap.getArg(middle,1), above1); // Create another back pointer to above2(R) from middle(A,B,C) // So that C = above2(Q). (above2 will not have a forward pointer // to middle) heap.unify(heap.getArg(middle,2), above2); // Then finally a back pointer from below(W) to middle/3 heap.unify(heap.getArg(below,0), middle); } heap.printRaw(std::cout); // Now check that we have precisely one forward pointer. HeapTest heapTest(heap); BitMap &fwd = heapTest.getForwardPointers(); size_t n = fwd.getSize(); std::cout << "Number of bits for forward pointers: " << n << "\n"; int cnt = 0; CellRef cell; for (size_t index = 0; index < n;) { index = fwd.findBit(index, n); if (index < n) { std::cout << "Found at : [" << index << "]: "; cell = heap.getCell(HeapRef(index)); heap.printCell(std::cout, cell); std::cout << "\n"; cnt++; } index++; } std::cout << "Expecting 1 forward pointer: got=" << cnt << "\n"; assert(cnt == 1); std::cout << "Forward pointer should be REF:13 and got: "; heap.printCell(std::cout, cell); std::cout << "\n"; assert(cell->getTag() == Cell::REF); assert(heap.toHeapRef(cell) == HeapRef(13)); std::cout << ">>> Do a full GC --------------------------------\n"; // Now let's invoke a full GC heap.gc(1.0, 3); std::cout << "Another scan for forward pointers:\n"; cnt = 0; for (size_t index = 0; index < n;) { index = fwd.findBit(index, n); if (index < n) { std::cout << "Found at : [" << index << "]: "; if (index == 0) { std::cout << "NULL"; } else { cell = heap.getCell(HeapRef(index)); heap.printCell(std::cout, cell); } std::cout << "\n"; cnt++; } index++; } std::cout << "<<< Done\n"; // Print heap again heap.printRaw(std::cout); // Size of heap should now be 9 cells std::cout << "Size of heap: " << heap.getHeapSize() << " (expecting 9)\n"; assert(heap.getHeapSize() == 9); }