#include "refcollection.hpp" #include #include #include #include #include "ref.hpp" #include "cell.hpp" #include "universalid.hpp" #include "record.hpp" void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base, std::map& cache, CSMDoc::Messages& messages) { Record 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 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 stream2; stream2 << "#" << mref.mTarget[0] << " " << mref.mTarget[1]; ref.mCell = stream2.str(); // overwrite } } } else ref.mCell = cell2.mId; std::map::iterator iter = cache.find (ref.mRefNum.mIndex); 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); if (base) { removeRows (index, 1); cache.erase (iter); } else { std::unique_ptr > record2(new Record(getRecord(index))); record2->mState = RecordBase::State_Deleted; setRecord(index, std::move(record2)); } continue; } if (iter==cache.end()) { // new reference ref.mId = getNewId(); std::unique_ptr > record(new Record); record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; cache.insert (std::make_pair (ref.mRefNum.mIndex, ref.mId)); (base ? record->mBase : record->mModified) = std::move(ref); appendRecord(std::move(record)); } else { // old reference -> merge ref.mId = iter->second; int index = getIndex (ref.mId); std::unique_ptr > record(new Record(getRecord(index))); record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified; (base ? record->mBase : record->mModified) = std::move(ref); setRecord(index, std::move(record)); } } } std::string CSMWorld::RefCollection::getNewId() { std::ostringstream stream; stream << "ref#" << mNextId++; return stream.str(); }