Exemple #1
0
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);
}
Exemple #2
0
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;
}
Exemple #3
0
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);
}
Exemple #5
0
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);
        }
    }
}
Exemple #7
0
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);
}