diff --git a/apps/opencs/model/tools/mergeoperation.cpp b/apps/opencs/model/tools/mergeoperation.cpp index 9b595046a..b15b4b83f 100644 --- a/apps/opencs/model/tools/mergeoperation.cpp +++ b/apps/opencs/model/tools/mergeoperation.cpp @@ -38,9 +38,10 @@ CSMTools::MergeOperation::MergeOperation (CSMDoc::Document& document, ToUTF8::Fr appendStage (new MergeRefIdsStage (mState)); appendStage (new MergeReferencesStage (mState)); appendStage (new MergeReferencesStage (mState)); - appendStage (new ListLandTexturesMergeStage (mState)); - appendStage (new MergeLandTexturesStage (mState)); + appendStage (new PopulateLandTexturesMergeStage (mState)); appendStage (new MergeLandStage (mState)); + appendStage (new FixLandsAndLandTexturesMergeStage (mState)); + appendStage (new CleanupLandTexturesMergeStage (mState)); appendStage (new FinishMergedDocumentStage (mState, encoding)); } diff --git a/apps/opencs/model/tools/mergestages.cpp b/apps/opencs/model/tools/mergestages.cpp index 176d35914..897c3329c 100644 --- a/apps/opencs/model/tools/mergestages.cpp +++ b/apps/opencs/model/tools/mergestages.cpp @@ -8,7 +8,9 @@ #include "mergestate.hpp" #include "../doc/document.hpp" +#include "../world/commands.hpp" #include "../world/data.hpp" +#include "../world/idtable.hpp" CSMTools::StartMergeStage::StartMergeStage (MergeState& state) @@ -109,149 +111,105 @@ void CSMTools::MergeReferencesStage::perform (int stage, CSMDoc::Messages& messa } -CSMTools::ListLandTexturesMergeStage::ListLandTexturesMergeStage (MergeState& state) -: mState (state) -{} +CSMTools::PopulateLandTexturesMergeStage::PopulateLandTexturesMergeStage (MergeState& state) + : mState (state) +{ +} -int CSMTools::ListLandTexturesMergeStage::setup() +int CSMTools::PopulateLandTexturesMergeStage::setup() { - return mState.mSource.getData().getLand().getSize(); + return mState.mSource.getData().getLandTextures().getSize(); } -void CSMTools::ListLandTexturesMergeStage::perform (int stage, CSMDoc::Messages& messages) +void CSMTools::PopulateLandTexturesMergeStage::perform (int stage, CSMDoc::Messages& messages) { - const CSMWorld::Record& record = - mState.mSource.getData().getLand().getRecord (stage); + const CSMWorld::Record& record = + mState.mSource.getData().getLandTextures().getRecord (stage); if (!record.isDeleted()) { - const CSMWorld::Land& land = record.get(); - - // make sure record is loaded - land.loadData (ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | - ESM::Land::DATA_VCLR | ESM::Land::DATA_VTEX); - - if (const ESM::Land::LandData *data = land.getLandData (ESM::Land::DATA_VTEX)) - { - // list texture indices - std::pair key; - key.second = land.mPlugin; - - for (int i=0; imTextures[i]; - - mState.mTextureIndices[key] = -1; - } - } + mState.mTarget->getData().getLandTextures().appendRecord(record); } } -CSMTools::MergeLandTexturesStage::MergeLandTexturesStage (MergeState& state) -: mState (state), mNext (mState.mTextureIndices.end()) -{} - -int CSMTools::MergeLandTexturesStage::setup() +CSMTools::MergeLandStage::MergeLandStage (MergeState& state) + : mState (state) { - // Should use the size of mState.mTextureIndices instead, but that is not available at this - // point. Unless there are any errors in the land and land texture records this will not - // make a difference. - return mState.mSource.getData().getLandTextures().getSize(); } -void CSMTools::MergeLandTexturesStage::perform (int stage, CSMDoc::Messages& messages) +int CSMTools::MergeLandStage::setup() { - if (stage==0) - mNext = mState.mTextureIndices.begin(); + return mState.mSource.getData().getLand().getSize(); +} - bool found = false; +void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages) +{ + const CSMWorld::Record& record = + mState.mSource.getData().getLand().getRecord (stage); - do + if (!record.isDeleted()) { - if (mNext==mState.mTextureIndices.end()) - return; - - mNext->second = stage+1; - - std::ostringstream stream; - stream << mNext->first.first-1 << "_" << mNext->first.second; - - int index = mState.mSource.getData().getLandTextures().searchId (stream.str()); - - if (index!=-1) - { - CSMWorld::LandTexture texture = - mState.mSource.getData().getLandTextures().getRecord (index).get(); - - stream.clear(); - stream << mNext->second-1 << "_0"; - - texture.mIndex = mNext->second-1; - texture.mId = stream.str(); - - CSMWorld::Record newRecord ( - CSMWorld::RecordBase::State_ModifiedOnly, 0, &texture); - - mState.mTarget->getData().getLandTextures().appendRecord (newRecord); - - found = true; - } - - ++mNext; + mState.mTarget->getData().getLand().appendRecord (record); } - while (!found); } -CSMTools::MergeLandStage::MergeLandStage (MergeState& state) : mState (state) {} +CSMTools::FixLandsAndLandTexturesMergeStage::FixLandsAndLandTexturesMergeStage (MergeState& state) + : mState (state) +{ +} -int CSMTools::MergeLandStage::setup() +int CSMTools::FixLandsAndLandTexturesMergeStage::setup() { + // We will have no more than the source return mState.mSource.getData().getLand().getSize(); } -void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages) +void CSMTools::FixLandsAndLandTexturesMergeStage::perform (int stage, CSMDoc::Messages& messages) { - const CSMWorld::Record& record = - mState.mSource.getData().getLand().getRecord (stage); - - if (!record.isDeleted()) + if (stage < mState.mTarget->getData().getLand().getSize()) { - const CSMWorld::Land& land = record.get(); + CSMWorld::IdTable& landTable = dynamic_cast( + *mState.mTarget->getData().getTableModel(CSMWorld::UniversalId::Type_Lands)); + + CSMWorld::IdTable& ltexTable = dynamic_cast( + *mState.mTarget->getData().getTableModel(CSMWorld::UniversalId::Type_LandTextures)); - land.loadData (ESM::Land::DATA_VCLR | ESM::Land::DATA_VHGT | ESM::Land::DATA_VNML | - ESM::Land::DATA_VTEX); + std::string id = mState.mTarget->getData().getLand().getId(stage); - CSMWorld::Land newLand (land); + CSMWorld::TouchLandCommand cmd(landTable, ltexTable, id); + cmd.redo(); - newLand.mPlugin = 0; + // Get rid of base data + const CSMWorld::Record& oldRecord = + mState.mTarget->getData().getLand().getRecord (stage); - if (land.mDataTypes & ESM::Land::DATA_VTEX) - { - // adjust land texture references - if (ESM::Land::LandData *data = newLand.getLandData()) - { - std::pair key; - key.second = land.mPlugin; + CSMWorld::Record newRecord(CSMWorld::RecordBase::State_ModifiedOnly, + nullptr, &oldRecord.get()); - for (int i=0; imTextures[i]; - std::map, int>::const_iterator iter = - mState.mTextureIndices.find (key); + mState.mTarget->getData().getLand().setRecord(stage, newRecord); + } +} - if (iter!=mState.mTextureIndices.end()) - data->mTextures[i] = iter->second; - else - data->mTextures[i] = 0; - } - } - } +CSMTools::CleanupLandTexturesMergeStage::CleanupLandTexturesMergeStage (MergeState& state) + : mState (state) +{ +} - CSMWorld::Record newRecord ( - CSMWorld::RecordBase::State_ModifiedOnly, 0, &newLand); +int CSMTools::CleanupLandTexturesMergeStage::setup() +{ + return 1; +} - mState.mTarget->getData().getLand().appendRecord (newRecord); +void CSMTools::CleanupLandTexturesMergeStage::perform (int stage, CSMDoc::Messages& messages) +{ + auto& landTextures = mState.mTarget->getData().getLandTextures(); + for (int i = 0; i < landTextures.getSize(); ) + { + if (!landTextures.getRecord(i).isModified()) + landTextures.removeRows(i, 1); + else + ++i; } } diff --git a/apps/opencs/model/tools/mergestages.hpp b/apps/opencs/model/tools/mergestages.hpp index f88f5be9f..4b41c5a04 100644 --- a/apps/opencs/model/tools/mergestages.hpp +++ b/apps/opencs/model/tools/mergestages.hpp @@ -116,13 +116,14 @@ namespace CSMTools ///< Messages resulting from this stage will be appended to \a messages. }; - class ListLandTexturesMergeStage : public CSMDoc::Stage + /// Adds all land texture records that could potentially be referenced when merging + class PopulateLandTexturesMergeStage : public CSMDoc::Stage { MergeState& mState; public: - ListLandTexturesMergeStage (MergeState& state); + PopulateLandTexturesMergeStage (MergeState& state); virtual int setup(); ///< \return number of steps @@ -131,14 +132,13 @@ namespace CSMTools ///< Messages resulting from this stage will be appended to \a messages. }; - class MergeLandTexturesStage : public CSMDoc::Stage + class MergeLandStage : public CSMDoc::Stage { MergeState& mState; - std::map, int>::iterator mNext; public: - MergeLandTexturesStage (MergeState& state); + MergeLandStage (MergeState& state); virtual int setup(); ///< \return number of steps @@ -147,13 +147,32 @@ namespace CSMTools ///< Messages resulting from this stage will be appended to \a messages. }; - class MergeLandStage : public CSMDoc::Stage + /// During this stage, the complex process of combining LandTextures from + /// potentially multiple plugins is undertaken. + class FixLandsAndLandTexturesMergeStage : public CSMDoc::Stage { MergeState& mState; public: - MergeLandStage (MergeState& state); + FixLandsAndLandTexturesMergeStage (MergeState& state); + + virtual int setup(); + ///< \return number of steps + + virtual void perform (int stage, CSMDoc::Messages& messages); + ///< Messages resulting from this stage will be appended to \a messages. + }; + + /// Removes base LandTexture records. This gets rid of the base records previously + /// needed in FixLandsAndLandTexturesMergeStage. + class CleanupLandTexturesMergeStage : public CSMDoc::Stage + { + MergeState& mState; + + public: + + CleanupLandTexturesMergeStage (MergeState& state); virtual int setup(); ///< \return number of steps diff --git a/apps/opencs/model/world/columnimp.cpp b/apps/opencs/model/world/columnimp.cpp index 18da81b53..49e4bebe6 100644 --- a/apps/opencs/model/world/columnimp.cpp +++ b/apps/opencs/model/world/columnimp.cpp @@ -108,7 +108,7 @@ namespace CSMWorld throw std::runtime_error("invalid land map LOD data"); Land copy = record.get(); - copy.setDataLoaded(Land::DATA_WNAM); + copy.add(Land::DATA_WNAM); for (int i = 0; i < values.size(); ++i) { @@ -155,7 +155,7 @@ namespace CSMWorld throw std::runtime_error("invalid land normals data"); Land copy = record.get(); - copy.setDataLoaded(Land::DATA_VNML); + copy.add(Land::DATA_VNML); for (int i = 0; i < values.size(); ++i) { @@ -202,7 +202,7 @@ namespace CSMWorld throw std::runtime_error("invalid land heights data"); Land copy = record.get(); - copy.setDataLoaded(Land::DATA_VHGT); + copy.add(Land::DATA_VHGT); for (int i = 0; i < values.size(); ++i) { @@ -249,7 +249,7 @@ namespace CSMWorld throw std::runtime_error("invalid land colours data"); Land copy = record.get(); - copy.setDataLoaded(Land::DATA_VCLR); + copy.add(Land::DATA_VCLR); for (int i = 0; i < values.size(); ++i) { @@ -296,7 +296,7 @@ namespace CSMWorld throw std::runtime_error("invalid land textures data"); Land copy = record.get(); - copy.setDataLoaded(Land::DATA_VTEX); + copy.add(Land::DATA_VTEX); for (int i = 0; i < values.size(); ++i) { diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index be86dd508..58a1b1d1c 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -44,6 +44,15 @@ namespace CSMWorld bool mChanged; }; + /// \brief Adds LandTexture records and modifies texture indices as needed. + /// + /// LandTexture records are different from other types of records, because + /// they only effect the current plugin. Thus, when modifying or copying + /// a Land record, all of the LandTexture records referenced need to be + /// added to the current plugin. Since these newly added LandTextures could + /// have indices that conflict with pre-existing LandTextures in the current + /// plugin, the indices might have to be changed, both for the newly added + /// LandRecord and within the Land record. class ImportLandTexturesCommand : public QUndoCommand { public: @@ -71,6 +80,9 @@ namespace CSMWorld std::vector mCreatedTextures; }; + /// \brief This command is used to fix LandTexture records and texture + /// indices after cloning a Land. See ImportLandTexturesCommand for + /// details. class CopyLandTexturesCommand : public ImportLandTexturesCommand { public: @@ -90,6 +102,9 @@ namespace CSMWorld std::string mDestId; }; + /// \brief This command brings a land record into the current plugin, adding + /// LandTexture records and modifying texture indices as needed. + /// \note See ImportLandTextures for more details. class TouchLandCommand : public ImportLandTexturesCommand { public: diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index fcfc8577e..3e503a80c 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -336,7 +336,7 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import int oldRow = idCollection()->searchId(id); // If it does not exist or it is in the current plugin, it can be skipped. - if (oldRow <= 0 || plugin == 0) + if (oldRow < 0 || plugin == 0) { results.recordMapping.push_back(std::make_pair(id, id)); continue; diff --git a/components/esm/loadland.cpp b/components/esm/loadland.cpp index f3f72e88a..a91dfe3d3 100644 --- a/components/esm/loadland.cpp +++ b/components/esm/loadland.cpp @@ -315,16 +315,7 @@ namespace ESM bool Land::isDataLoaded(int flags) const { - return mLandData && (mLandData->mDataLoaded & flags) == (flags & mDataTypes); - } - - void Land::setDataLoaded(int flags) - { - if (!mLandData) - mLandData = new LandData; - - mDataTypes |= flags; - mLandData->mDataLoaded |= flags; + return mLandData && (mLandData->mDataLoaded & flags) == flags; } Land::Land (const Land& land) diff --git a/components/esm/loadland.hpp b/components/esm/loadland.hpp index 7be954b3e..2163c30fc 100644 --- a/components/esm/loadland.hpp +++ b/components/esm/loadland.hpp @@ -132,7 +132,6 @@ struct Land void unloadData() const; /// Check if given data type is loaded - /// @note We only check data types that *can* be loaded (present in mDataTypes) bool isDataLoaded(int flags) const; /// Sets the flags and creates a LandData if needed