diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index 44698cd2e3..5e94f40b0f 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -253,6 +253,70 @@ int CSMDoc::WriteCellCollectionStage::setup() return mDocument.getData().getCells().getSize(); } +int CSMDoc::WriteCellCollectionStage::writeReferences (const std::deque& references, bool interior, unsigned int& newRefNum, bool temp) +{ + ESM::ESMWriter& writer = mState.getWriter(); + size_t refCount = 0; + + for (std::deque::const_iterator iter (references.begin()); + iter!=references.end(); ++iter) + { + const CSMWorld::Record& ref = + mDocument.getData().getReferences().getRecord (*iter); + + if (temp && ref.get().mIsPersistent || !temp && !ref.get().mIsPersistent) + continue; + + refCount++; + + if (ref.isModified() || ref.mState == CSMWorld::RecordBase::State_Deleted) + { + CSMWorld::CellRef refRecord = ref.get(); + + // Check for uninitialized content file + if (!refRecord.mRefNum.hasContentFile()) + refRecord.mRefNum.mContentFile = 0; + + // recalculate the ref's cell location + std::ostringstream stream; + if (!interior) + { + std::pair index = refRecord.getCellIndex(); + stream << "#" << index.first << " " << index.second; + } + + if (refRecord.mNew || refRecord.mRefNum.mIndex == 0 || + (!interior && ref.mState==CSMWorld::RecordBase::State_ModifiedOnly && + refRecord.mCell!=stream.str())) + { + refRecord.mRefNum.mIndex = newRefNum++; + } + else if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell) + != stream.str() && !interior) + { + // An empty mOriginalCell is meant to indicate that it is the same as + // the current cell. It is possible that a moved ref is moved again. + + ESM::MovedCellRef moved; + moved.mRefNum = refRecord.mRefNum; + + // Need to fill mTarget with the ref's new position. + std::istringstream istream (stream.str().c_str()); + + char ignore; + istream >> ignore >> moved.mTarget[0] >> moved.mTarget[1]; + + refRecord.mRefNum.save (writer, false, "MVRF"); + writer.writeHNT ("CNDT", moved.mTarget); + } + + refRecord.save (writer, false, false, ref.mState == CSMWorld::RecordBase::State_Deleted); + } + } + + return refCount; +} + void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages) { ESM::ESMWriter& writer = mState.getWriter(); @@ -289,7 +353,6 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages) if (refRecord.mRefNum.mIndex >= newRefNum) newRefNum = refRecord.mRefNum.mIndex + 1; - } } @@ -312,56 +375,10 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages) // write references if (references!=mState.getSubRecords().end()) { - for (std::deque::const_iterator iter (references->second.begin()); - iter!=references->second.end(); ++iter) - { - const CSMWorld::Record& ref = - mDocument.getData().getReferences().getRecord (*iter); - - if (ref.isModified() || ref.mState == CSMWorld::RecordBase::State_Deleted) - { - CSMWorld::CellRef refRecord = ref.get(); - - // Check for uninitialized content file - if (!refRecord.mRefNum.hasContentFile()) - refRecord.mRefNum.mContentFile = 0; - - // recalculate the ref's cell location - std::ostringstream stream; - if (!interior) - { - std::pair index = refRecord.getCellIndex(); - stream << "#" << index.first << " " << index.second; - } - - if (refRecord.mNew || refRecord.mRefNum.mIndex == 0 || - (!interior && ref.mState==CSMWorld::RecordBase::State_ModifiedOnly && - refRecord.mCell!=stream.str())) - { - refRecord.mRefNum.mIndex = newRefNum++; - } - else if ((refRecord.mOriginalCell.empty() ? refRecord.mCell : refRecord.mOriginalCell) - != stream.str() && !interior) - { - // An empty mOriginalCell is meant to indicate that it is the same as - // the current cell. It is possible that a moved ref is moved again. - - ESM::MovedCellRef moved; - moved.mRefNum = refRecord.mRefNum; - - // Need to fill mTarget with the ref's new position. - std::istringstream istream (stream.str().c_str()); - - char ignore; - istream >> ignore >> moved.mTarget[0] >> moved.mTarget[1]; - - refRecord.mRefNum.save (writer, false, "MVRF"); - writer.writeHNT ("CNDT", moved.mTarget); - } - - refRecord.save (writer, false, false, ref.mState == CSMWorld::RecordBase::State_Deleted); - } - } + int persistentRefCount = writeReferences(references->second, interior, newRefNum, false/*temp*/); + cellRecord.saveTempMarker(writer, int(references->second.size()) - persistentRefCount); + // FIXME: loops twice, inefficient + writeReferences(references->second, interior, newRefNum, true/*temp*/); } writer.endRecord (cellRecord.sRecordId); diff --git a/apps/opencs/model/doc/savingstages.hpp b/apps/opencs/model/doc/savingstages.hpp index d3a0cc9b31..e9497a6064 100644 --- a/apps/opencs/model/doc/savingstages.hpp +++ b/apps/opencs/model/doc/savingstages.hpp @@ -171,6 +171,8 @@ namespace CSMDoc Document& mDocument; SavingState& mState; + int writeReferences (const std::deque& references, bool interior, unsigned int& newRefNum, bool temp); + public: WriteCellCollectionStage (Document& document, SavingState& state); diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index dfdb8e73bf..2f5db65636 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -19,8 +19,9 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool ESM::MovedCellRef mref; mref.mRefNum.mIndex = 0; bool isDeleted = false; + int tempRefCount = cell.get().mRefNumCounter; // if initially non-zero, all refs in cell are temp - while (ESM::Cell::getNextRef(reader, ref, isDeleted, true, &mref)) + while (ESM::Cell::getNextRef(reader, ref, isDeleted, &tempRefCount, true, &mref)) { // Keep mOriginalCell empty when in modified (as an indicator that the // original cell will always be equal the current cell). @@ -59,6 +60,8 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool else ref.mCell = cell2.mId; + // TODO: update cell.get().mRefNumCounter with tempRefCount? + mref.mRefNum.mIndex = 0; // ignore content file number diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index b4d6ac7a72..42bc3d648f 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -31,10 +31,10 @@ void ESM::RefNum::save (ESMWriter &esm, bool wide, const std::string& tag) const } -void ESM::CellRef::load (ESMReader& esm, bool &isDeleted, bool wideRefNum) +void ESM::CellRef::load (ESMReader& esm, bool &isDeleted, int *tempRefCount, bool wideRefNum) { loadId(esm, wideRefNum); - loadData(esm, isDeleted); + loadData(esm, isDeleted, tempRefCount); } void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum) @@ -57,9 +57,11 @@ void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum) } } -void ESM::CellRef::loadData(ESMReader &esm, bool &isDeleted) +void ESM::CellRef::loadData(ESMReader &esm, bool &isDeleted, int *tempRefCount) { isDeleted = false; + mIsPersistent = !tempRefCount // default to persistent + || (tempRefCount && *tempRefCount == -1); bool isLoaded = false; while (!isLoaded && esm.hasMoreSubs()) @@ -121,8 +123,17 @@ void ESM::CellRef::loadData(ESMReader &esm, bool &isDeleted) esm.getHT(mPos, 24); break; case ESM::FourCC<'N','A','M','0'>::value: - esm.skipHSub(); + { + if (tempRefCount && *tempRefCount == -1) + { + esm.getHT(*tempRefCount); + // TODO: check that there are no more subs following this sub + } + else + esm.skipHSub(); + break; + } case ESM::SREC_DELE: esm.skipHSub(); isDeleted = true; @@ -236,4 +247,6 @@ void ESM::CellRef::blank() mPos.pos[i] = 0; mPos.rot[i] = 0; } + + mIsPersistent = false; } diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index c2f7ff6de5..97cfea89d0 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -106,13 +106,15 @@ namespace ESM // Position and rotation of this object within the cell Position mPos; + bool mIsPersistent; + /// Calls loadId and loadData - void load (ESMReader& esm, bool &isDeleted, bool wideRefNum = false); + void load (ESMReader& esm, bool &isDeleted, int *tempRefCount, bool wideRefNum = false); void loadId (ESMReader& esm, bool wideRefNum = false); /// Implicitly called by load - void loadData (ESMReader& esm, bool &isDeleted); + void loadData (ESMReader& esm, bool &isDeleted, int *tempRefCount = nullptr); void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false, bool isDeleted = false) const; diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index bf70aad96f..8aeacd5116 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -146,7 +146,7 @@ namespace ESM } } - if (saveContext) + if (saveContext) { mContextList.push_back(esm.getContext()); esm.skipRecord(); @@ -197,9 +197,12 @@ namespace ESM if (mMapColor != 0) esm.writeHNT("NAM5", mMapColor); } + } - if (mRefNumCounter != 0) - esm.writeHNT("NAM0", mRefNumCounter); + void Cell::saveTempMarker(ESMWriter &esm, int tempCount) const + { + if (tempCount != 0) + esm.writeHNT("NAM0", tempCount); } void Cell::restore(ESMReader &esm, int iCtx) const @@ -221,7 +224,7 @@ namespace ESM return region + ' ' + cellGrid; } - bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool &isDeleted, bool ignoreMoves, MovedCellRef *mref) + bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool &isDeleted, int *tempRefCount, bool ignoreMoves, MovedCellRef *mref) { isDeleted = false; @@ -249,7 +252,9 @@ namespace ESM if (esm.peekNextSub("FRMR")) { - ref.load (esm, isDeleted); + ref.load (esm, isDeleted, tempRefCount); + + // TODO: should count the number of temp refs and validate the number // Identify references belonging to a parent file and adapt the ID accordingly. adjustRefNum (ref.mRefNum, esm); @@ -275,7 +280,7 @@ namespace ESM mWater = 0; mWaterInt = false; mMapColor = 0; - mRefNumCounter = 0; + mRefNumCounter = -1; mData.mFlags = 0; mData.mX = 0; diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index d06b509c5a..ca4ca6e577 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -94,7 +94,7 @@ struct Cell mWater(0), mWaterInt(false), mMapColor(0), - mRefNumCounter(0) + mRefNumCounter(-1) {} // Interior cells are indexed by this (it's the 'id'), for exterior @@ -133,6 +133,7 @@ struct Cell void loadCell(ESMReader &esm, bool saveContext = true); // Load everything, except NAME, DATAstruct and references void save(ESMWriter &esm, bool isDeleted = false) const; + void saveTempMarker(ESMWriter &esm, int tempCount) const; bool isExterior() const { @@ -184,6 +185,7 @@ struct Cell static bool getNextRef(ESMReader &esm, CellRef &ref, bool &isDeleted, + int *tempRefCount = nullptr, bool ignoreMoves = false, MovedCellRef *mref = nullptr);