From 04c5c0d82a1acde5e558563c113af285951e985a Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 8 Nov 2015 06:02:53 +1100 Subject: [PATCH 01/29] Move NPC autocal code out to a separate class. --- apps/opencs/CMakeLists.txt | 4 +- apps/opencs/model/world/data.cpp | 365 +------------------ apps/opencs/model/world/data.hpp | 25 +- apps/opencs/model/world/npcautocalc.cpp | 367 ++++++++++++++++++++ apps/opencs/model/world/npcautocalc.hpp | 75 ++++ apps/opencs/model/world/refidadapterimp.cpp | 15 +- 6 files changed, 466 insertions(+), 385 deletions(-) create mode 100644 apps/opencs/model/world/npcautocalc.cpp create mode 100644 apps/opencs/model/world/npcautocalc.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 41d44ca18c..30c78e710b 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -19,7 +19,7 @@ opencs_hdrs_noqt (model/doc opencs_units (model/world idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree infotableproxymodel - pathgridcommands + pathgridcommands npcautocalc ) @@ -36,7 +36,7 @@ opencs_hdrs_noqt (model/world opencs_units (model/tools - tools reportmodel mergeoperation + tools reportmodel mergeoperation ) opencs_units_noqt (model/tools diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index dfb2d99d32..84cb505c00 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -10,10 +10,6 @@ #include #include -#include -#include -#include - #include "idtable.hpp" #include "idtree.hpp" #include "columnimp.hpp" @@ -23,70 +19,7 @@ #include "resourcetable.hpp" #include "nestedcoladapterimp.hpp" #include "npcstats.hpp" - -namespace -{ - class CSStore : public AutoCalc::StoreCommon - { - const CSMWorld::IdCollection& mGmstTable; - const CSMWorld::IdCollection& mSkillTable; - const CSMWorld::IdCollection& mMagicEffectTable; - const CSMWorld::NestedIdCollection& mSpells; - - public: - - CSStore(const CSMWorld::IdCollection& gmst, - const CSMWorld::IdCollection& skills, - const CSMWorld::IdCollection& magicEffects, - const CSMWorld::NestedIdCollection& spells) - : mGmstTable(gmst), mSkillTable(skills), mMagicEffectTable(magicEffects), mSpells(spells) - { } - - ~CSStore() {} - - virtual int findGmstInt(const std::string& name) const - { - return mGmstTable.getRecord(name).get().getInt(); - } - - virtual float findGmstFloat(const std::string& name) const - { - return mGmstTable.getRecord(name).get().getFloat(); - } - - virtual const ESM::Skill *findSkill(int index) const - { - // if the skill does not exist, throws std::runtime_error ("invalid ID: " + id) - return &mSkillTable.getRecord(ESM::Skill::indexToId(index)).get(); - } - - virtual const ESM::MagicEffect* findMagicEffect(int id) const - { - // if the magic effect does not exist, throws std::runtime_error ("invalid ID: " + id) - return &mMagicEffectTable.getRecord(ESM::MagicEffect::indexToId((short)id)).get(); - } - - virtual void getSpells(std::vector& spells) - { - // prepare data in a format used by OpenMW store - for (int index = 0; index < mSpells.getSize(); ++index) - spells.push_back(const_cast(&mSpells.getRecord(index).get())); - } - }; - - unsigned short autoCalculateMana(AutoCalc::StatsBase& stats) - { - return stats.getBaseAttribute(ESM::Attribute::Intelligence) * 2; - } - - unsigned short autoCalculateFatigue(AutoCalc::StatsBase& stats) - { - return stats.getBaseAttribute(ESM::Attribute::Strength) - + stats.getBaseAttribute(ESM::Attribute::Willpower) - + stats.getBaseAttribute(ESM::Attribute::Agility) - + stats.getBaseAttribute(ESM::Attribute::Endurance); - } -} +#include "npcautocalc.hpp" void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update) { @@ -129,8 +62,9 @@ int CSMWorld::Data::count (RecordBase::State state, const CollectionBase& collec } CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager) -: mEncoder (encoding), mPathgrids (mCells), mReferenceables(self()), mRefs (mCells), - mResourcesManager (resourcesManager), mReader (0), mDialogue (0), mReaderIndex(0) +: mEncoder (encoding), mPathgrids (mCells), mReferenceables (self()), mRefs (mCells), + mResourcesManager (resourcesManager), mReader (0), mDialogue (0), mReaderIndex(0), + mNpcAutoCalc (0) { int index = 0; @@ -621,32 +555,18 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc CSMWorld::IdTree *objects = static_cast(getTableModel(UniversalId::Type_Referenceable)); - connect (gmsts, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), - this, SLOT (gmstDataChanged (const QModelIndex&, const QModelIndex&))); - connect (skills, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), - this, SLOT (skillDataChanged (const QModelIndex&, const QModelIndex&))); - connect (classes, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), - this, SLOT (classDataChanged (const QModelIndex&, const QModelIndex&))); - connect (races, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), - this, SLOT (raceDataChanged (const QModelIndex&, const QModelIndex&))); - connect (objects, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), - this, SLOT (npcDataChanged (const QModelIndex&, const QModelIndex&))); - connect (this, SIGNAL (updateNpcAutocalc (int, const std::string&)), - objects, SLOT (updateNpcAutocalc (int, const std::string&))); - connect (this, SIGNAL (cacheNpcStats (const std::string&, NpcStats*)), - this, SLOT (cacheNpcStatsEvent (const std::string&, NpcStats*))); + mNpcAutoCalc = new NpcAutoCalc (self(), gmsts, skills, classes, races, objects); mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files } CSMWorld::Data::~Data() { - clearNpcStatsCache(); - for (std::vector::iterator iter (mModels.begin()); iter!=mModels.end(); ++iter) delete *iter; delete mReader; + delete mNpcAutoCalc; } const CSMWorld::IdCollection& CSMWorld::Data::getGlobals() const @@ -946,6 +866,11 @@ void CSMWorld::Data::setMetaData (const MetaData& metaData) mMetaData.setRecord (0, record); } +const CSMWorld::NpcAutoCalc& CSMWorld::Data::getNpcAutoCalc() const +{ + return *mNpcAutoCalc; +} + QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& id) { std::map::iterator iter = mModelIndex.find (id.getType()); @@ -1297,271 +1222,3 @@ const CSMWorld::Data& CSMWorld::Data::self () { return *this; } - -void CSMWorld::Data::skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) -{ - // mData.mAttribute (affects attributes skill bonus autocalc) - // mData.mSpecialization (affects skills autocalc) - CSMWorld::IdTable *skillModel = - static_cast(getTableModel(CSMWorld::UniversalId::Type_Skill)); - - int attributeColumn = skillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute); - int specialisationColumn = skillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation); - - if ((topLeft.column() <= attributeColumn && attributeColumn <= bottomRight.column()) - || (topLeft.column() <= specialisationColumn && specialisationColumn <= bottomRight.column())) - { - clearNpcStatsCache(); - - std::string empty; - emit updateNpcAutocalc(0/*all*/, empty); - } -} - -void CSMWorld::Data::classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) -{ - // update autocalculated attributes/skills of every NPC with matching class - // - mData.mAttribute[2] - // - mData.mSkills[5][2] - // - mData.mSpecialization - CSMWorld::IdTable *classModel = - static_cast(getTableModel(CSMWorld::UniversalId::Type_Class)); - - int attribute1Column = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute1); // +1 - int majorSkill1Column = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_MajorSkill1); // +4 - int minorSkill1Column = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_MinorSkill1); // +4 - int specialisationColumn = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation); - - if ((topLeft.column() > attribute1Column+1 || attribute1Column > bottomRight.column()) - && (topLeft.column() > majorSkill1Column+4 || majorSkill1Column > bottomRight.column()) - && (topLeft.column() > minorSkill1Column+4 || minorSkill1Column > bottomRight.column()) - && (topLeft.column() > specialisationColumn || specialisationColumn > bottomRight.column())) - { - return; - } - - // get the affected class - int idColumn = classModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id); - for (int classRow = topLeft.row(); classRow <= bottomRight.row(); ++classRow) - { - clearNpcStatsCache(); - - std::string classId = - classModel->data(classModel->index(classRow, idColumn)).toString().toUtf8().constData(); - emit updateNpcAutocalc(1/*class*/, classId); - } -} - -void CSMWorld::Data::raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) -{ - // affects racial bonus attributes & skills - // - mData.mAttributeValues[] - // - mData.mBonus[].mBonus - // - mPowers.mList[] - CSMWorld::IdTree *raceModel = - static_cast(getTableModel(CSMWorld::UniversalId::Type_Race)); - - int attrColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceAttributes); - int bonusColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceSkillBonus); - int powersColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_PowerList); - - bool match = false; - int raceRow = topLeft.row(); - int raceEnd = bottomRight.row(); - if (topLeft.parent().isValid() && bottomRight.parent().isValid()) - { - if ((topLeft.parent().column() <= attrColumn && attrColumn <= bottomRight.parent().column()) - || (topLeft.parent().column() <= bonusColumn && bonusColumn <= bottomRight.parent().column()) - || (topLeft.parent().column() <= powersColumn && powersColumn <= bottomRight.parent().column())) - { - match = true; // TODO: check for specific nested column? - raceRow = topLeft.parent().row(); - raceEnd = bottomRight.parent().row(); - } - } - else - { - if ((topLeft.column() <= attrColumn && attrColumn <= bottomRight.column()) - || (topLeft.column() <= bonusColumn && bonusColumn <= bottomRight.column()) - || (topLeft.column() <= powersColumn && powersColumn <= bottomRight.column())) - { - match = true; // maybe the whole table changed - } - } - - if (!match) - return; - - // update autocalculated attributes/skills of every NPC with matching race - int idColumn = raceModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id); - for (; raceRow <= raceEnd; ++raceRow) - { - clearNpcStatsCache(); - - std::string raceId = - raceModel->data(raceModel->index(raceRow, idColumn)).toString().toUtf8().constData(); - emit updateNpcAutocalc(2/*race*/, raceId); - } -} - -void CSMWorld::Data::npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) -{ - // TODO: for now always recalculate - clearNpcStatsCache(); -} - -void CSMWorld::Data::gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) -{ - static const QStringList gmsts(QStringList()<< "fNPCbaseMagickaMult" << "fAutoSpellChance" - << "fEffectCostMult" << "iAutoSpellAlterationMax" << "iAutoSpellConjurationMax" - << "iAutoSpellDestructionMax" << "iAutoSpellIllusionMax" << "iAutoSpellMysticismMax" - << "iAutoSpellRestorationMax" << "iAutoSpellTimesCanCast" << "iAutoSpellAttSkillMin"); - - bool match = false; - for (int row = topLeft.row(); row <= bottomRight.row(); ++row) - { - if (gmsts.contains(mGmsts.getRecord(row).get().mId.c_str())) - { - match = true; - break; - } - } - - if (!match) - return; - - clearNpcStatsCache(); - - std::string empty; - emit updateNpcAutocalc(0/*all*/, empty); -} - -void CSMWorld::Data::clearNpcStatsCache () -{ - for (std::map::iterator it (mNpcStatCache.begin()); - it != mNpcStatCache.end(); ++it) - delete it->second; - - mNpcStatCache.clear(); -} - -CSMWorld::NpcStats* CSMWorld::Data::npcAutoCalculate(const ESM::NPC& npc) const -{ - CSMWorld::NpcStats * cachedStats = getCachedNpcData (npc.mId); - if (cachedStats) - return cachedStats; - - int raceIndex = mRaces.searchId(npc.mRace); - int classIndex = mClasses.searchId(npc.mClass); - // this can happen when creating a new game from scratch - if (raceIndex == -1 || classIndex == -1) - return 0; - - const ESM::Race *race = &mRaces.getRecord(raceIndex).get(); - const ESM::Class *class_ = &mClasses.getRecord(classIndex).get(); - - bool autoCalc = npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS; - short level = npc.mNpdt52.mLevel; - if (autoCalc) - level = npc.mNpdt12.mLevel; - - std::auto_ptr stats (new CSMWorld::NpcStats()); - - CSStore store(mGmsts, mSkills, mMagicEffects, mSpells); - - if (autoCalc) - { - AutoCalc::autoCalcAttributesImpl (&npc, race, class_, level, *stats, &store); - - stats->setHealth(autoCalculateHealth(level, class_, *stats)); - stats->setMana(autoCalculateMana(*stats)); - stats->setFatigue(autoCalculateFatigue(*stats)); - - AutoCalc::autoCalcSkillsImpl(&npc, race, class_, level, *stats, &store); - - AutoCalc::autoCalculateSpells(race, *stats, &store); - } - else - { - for (std::vector::const_iterator it = npc.mSpells.mList.begin(); - it != npc.mSpells.mList.end(); ++it) - { - stats->addSpell(*it); - } - } - - // update spell info - const std::vector &racePowers = race->mPowers.mList; - for (unsigned int i = 0; i < racePowers.size(); ++i) - { - int type = -1; - int spellIndex = mSpells.searchId(racePowers[i]); - if (spellIndex != -1) - type = mSpells.getRecord(spellIndex).get().mData.mType; - stats->addPowers(racePowers[i], type); - } - // cost/chance - int skills[ESM::Skill::Length]; - if (autoCalc) - for (int i = 0; i< ESM::Skill::Length; ++i) - skills[i] = stats->getBaseSkill(i); - else - for (int i = 0; i< ESM::Skill::Length; ++i) - skills[i] = npc.mNpdt52.mSkills[i]; - - int attributes[ESM::Attribute::Length]; - if (autoCalc) - for (int i = 0; i< ESM::Attribute::Length; ++i) - attributes[i] = stats->getBaseAttribute(i); - else - { - attributes[ESM::Attribute::Strength] = npc.mNpdt52.mStrength; - attributes[ESM::Attribute::Willpower] = npc.mNpdt52.mWillpower; - attributes[ESM::Attribute::Agility] = npc.mNpdt52.mAgility; - attributes[ESM::Attribute::Speed] = npc.mNpdt52.mSpeed; - attributes[ESM::Attribute::Endurance] = npc.mNpdt52.mEndurance; - attributes[ESM::Attribute::Personality] = npc.mNpdt52.mPersonality; - attributes[ESM::Attribute::Luck] = npc.mNpdt52.mLuck; - } - - const std::vector& spells = stats->spells(); - for (std::vector::const_iterator it = spells.begin(); it != spells.end(); ++it) - { - int cost = -1; - int spellIndex = mSpells.searchId((*it).mName); - const ESM::Spell* spell = 0; - if (spellIndex != -1) - { - spell = &mSpells.getRecord(spellIndex).get(); - cost = spell->mData.mCost; - - int school; - float skillTerm; - AutoCalc::calcWeakestSchool(spell, skills, school, skillTerm, &store); - float chance = calcAutoCastChance(spell, skills, attributes, school, &store); - - stats->addCostAndChance((*it).mName, cost, (int)ceil(chance)); // percent - } - } - - if (stats.get() == 0) - return 0; - - CSMWorld::NpcStats *result = stats.release(); - emit cacheNpcStats (npc.mId, result); - return result; -} - -void CSMWorld::Data::cacheNpcStatsEvent (const std::string& id, CSMWorld::NpcStats *stats) -{ - mNpcStatCache[id] = stats; -} - -CSMWorld::NpcStats* CSMWorld::Data::getCachedNpcData (const std::string& id) const -{ - std::map::const_iterator it = mNpcStatCache.find(id); - if (it != mNpcStatCache.end()) - return it->second; - else - return 0; -} diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 944a636f0a..3d3996a1de 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -62,6 +62,7 @@ namespace CSMWorld class ResourcesManager; class Resources; class NpcStats; + class NpcAutoCalc; class Data : public QObject { @@ -109,7 +110,7 @@ namespace CSMWorld std::vector > mReaders; - std::map mNpcStatCache; + NpcAutoCalc *mNpcAutoCalc; // not implemented Data (const Data&); @@ -282,37 +283,17 @@ namespace CSMWorld int count (RecordBase::State state) const; ///< Return number of top-level records with the given \a state. - NpcStats* npcAutoCalculate (const ESM::NPC& npc) const; - - NpcStats* getCachedNpcData (const std::string& id) const; + const NpcAutoCalc& getNpcAutoCalc() const; signals: void idListChanged(); - // refresh NPC dialogue subviews via object table model - void updateNpcAutocalc (int type, const std::string& id); - - void cacheNpcStats (const std::string& id, NpcStats *stats) const; - private slots: void dataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void rowsChanged (const QModelIndex& parent, int start, int end); - - // for autocalc updates when gmst/race/class/skils tables change - void gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); - - void raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); - - void classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); - - void skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); - - void npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); - - void cacheNpcStatsEvent (const std::string& id, NpcStats *stats); }; } diff --git a/apps/opencs/model/world/npcautocalc.cpp b/apps/opencs/model/world/npcautocalc.cpp new file mode 100644 index 0000000000..21eb822660 --- /dev/null +++ b/apps/opencs/model/world/npcautocalc.cpp @@ -0,0 +1,367 @@ +#include "npcautocalc.hpp" + +#include + +#include +#include +#include + +#include "npcstats.hpp" +#include "data.hpp" +#include "idtable.hpp" +#include "idtree.hpp" + +namespace +{ + class CSStore : public AutoCalc::StoreCommon + { + const CSMWorld::IdCollection& mGmstTable; + const CSMWorld::IdCollection& mSkillTable; + const CSMWorld::IdCollection& mMagicEffectTable; + const CSMWorld::NestedIdCollection& mSpells; + + public: + + CSStore(const CSMWorld::IdCollection& gmst, + const CSMWorld::IdCollection& skills, + const CSMWorld::IdCollection& magicEffects, + const CSMWorld::NestedIdCollection& spells) + : mGmstTable(gmst), mSkillTable(skills), mMagicEffectTable(magicEffects), mSpells(spells) + {} + + ~CSStore() {} + + virtual int findGmstInt(const std::string& name) const + { + return mGmstTable.getRecord(name).get().getInt(); + } + + virtual float findGmstFloat(const std::string& name) const + { + return mGmstTable.getRecord(name).get().getFloat(); + } + + virtual const ESM::Skill *findSkill(int index) const + { + // if the skill does not exist, throws std::runtime_error ("invalid ID: " + id) + return &mSkillTable.getRecord(ESM::Skill::indexToId(index)).get(); + } + + virtual const ESM::MagicEffect* findMagicEffect(int id) const + { + // if the magic effect does not exist, throws std::runtime_error ("invalid ID: " + id) + return &mMagicEffectTable.getRecord(ESM::MagicEffect::indexToId((short)id)).get(); + } + + virtual void getSpells(std::vector& spells) + { + // prepare data in a format used by OpenMW store + for (int index = 0; index < mSpells.getSize(); ++index) + spells.push_back(const_cast(&mSpells.getRecord(index).get())); + } + }; + + unsigned short autoCalculateMana(const AutoCalc::StatsBase& stats) + { + return stats.getBaseAttribute(ESM::Attribute::Intelligence) * 2; + } + + unsigned short autoCalculateFatigue(const AutoCalc::StatsBase& stats) + { + return stats.getBaseAttribute(ESM::Attribute::Strength) + + stats.getBaseAttribute(ESM::Attribute::Willpower) + + stats.getBaseAttribute(ESM::Attribute::Agility) + + stats.getBaseAttribute(ESM::Attribute::Endurance); + } +} + +CSMWorld::NpcAutoCalc::NpcAutoCalc (const Data& data, + const IdTable *gmsts, const IdTable *skills, const IdTable *classes, const IdTree *races, const IdTree *objects) +: mData(data), mSkillModel(skills), mClassModel(classes), mRaceModel(races) +{ + // for autocalc updates when gmst/race/class/skils tables change + connect (gmsts, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (gmstDataChanged (const QModelIndex&, const QModelIndex&))); + connect (mSkillModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (skillDataChanged (const QModelIndex&, const QModelIndex&))); + connect (mClassModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (classDataChanged (const QModelIndex&, const QModelIndex&))); + connect (mRaceModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (raceDataChanged (const QModelIndex&, const QModelIndex&))); + connect (objects, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (npcDataChanged (const QModelIndex&, const QModelIndex&))); + connect (this, SIGNAL (updateNpcAutocalc (int, const std::string&)), + objects, SLOT (updateNpcAutocalc (int, const std::string&))); + + //connect (this, SIGNAL (cacheNpcStats (const std::string&, NpcStats*)), + //this, SLOT (cacheNpcStatsEvent (const std::string&, NpcStats*))); +} + +CSMWorld::NpcAutoCalc::~NpcAutoCalc() +{ + clearNpcStatsCache(); +} + +void CSMWorld::NpcAutoCalc::skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + // mData.mAttribute (affects attributes skill bonus autocalc) + // mData.mSpecialization (affects skills autocalc) + int attributeColumn = mSkillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute); + int specialisationColumn = mSkillModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation); + + if ((topLeft.column() <= attributeColumn && attributeColumn <= bottomRight.column()) + || (topLeft.column() <= specialisationColumn && specialisationColumn <= bottomRight.column())) + { + clearNpcStatsCache(); + + std::string empty; + emit updateNpcAutocalc(0/*all*/, empty); + } +} + +void CSMWorld::NpcAutoCalc::classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + // update autocalculated attributes/skills of every NPC with matching class + // - mData.mAttribute[2] + // - mData.mSkills[5][2] + // - mData.mSpecialization + int attribute1Column = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_Attribute1); // +1 + int majorSkill1Column = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_MajorSkill1); // +4 + int minorSkill1Column = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_MinorSkill1); // +4 + int specialisationColumn = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_Specialisation); + + if ((topLeft.column() > attribute1Column+1 || attribute1Column > bottomRight.column()) + && (topLeft.column() > majorSkill1Column+4 || majorSkill1Column > bottomRight.column()) + && (topLeft.column() > minorSkill1Column+4 || minorSkill1Column > bottomRight.column()) + && (topLeft.column() > specialisationColumn || specialisationColumn > bottomRight.column())) + { + return; + } + + // get the affected class + int idColumn = mClassModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id); + for (int classRow = topLeft.row(); classRow <= bottomRight.row(); ++classRow) + { + clearNpcStatsCache(); + + std::string classId = + mClassModel->data(mClassModel->index(classRow, idColumn)).toString().toUtf8().constData(); + emit updateNpcAutocalc(1/*class*/, classId); + } +} + +void CSMWorld::NpcAutoCalc::raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + // affects racial bonus attributes & skills + // - mData.mAttributeValues[] + // - mData.mBonus[].mBonus + // - mPowers.mList[] + int attrColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceAttributes); + int bonusColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_RaceSkillBonus); + int powersColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_PowerList); + + bool match = false; + int raceRow = topLeft.row(); + int raceEnd = bottomRight.row(); + if (topLeft.parent().isValid() && bottomRight.parent().isValid()) + { + if ((topLeft.parent().column() <= attrColumn && attrColumn <= bottomRight.parent().column()) + || (topLeft.parent().column() <= bonusColumn && bonusColumn <= bottomRight.parent().column()) + || (topLeft.parent().column() <= powersColumn && powersColumn <= bottomRight.parent().column())) + { + match = true; // TODO: check for specific nested column? + raceRow = topLeft.parent().row(); + raceEnd = bottomRight.parent().row(); + } + } + else + { + if ((topLeft.column() <= attrColumn && attrColumn <= bottomRight.column()) + || (topLeft.column() <= bonusColumn && bonusColumn <= bottomRight.column()) + || (topLeft.column() <= powersColumn && powersColumn <= bottomRight.column())) + { + match = true; // maybe the whole table changed + } + } + + if (!match) + return; + + // update autocalculated attributes/skills of every NPC with matching race + int idColumn = mRaceModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id); + for (; raceRow <= raceEnd; ++raceRow) + { + clearNpcStatsCache(); + + std::string raceId = + mRaceModel->data(mRaceModel->index(raceRow, idColumn)).toString().toUtf8().constData(); + emit updateNpcAutocalc(2/*race*/, raceId); + } +} + +void CSMWorld::NpcAutoCalc::npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + // TODO: for now always recalculate + clearNpcStatsCache(); + + // TODO: check if below signal slows things down + std::string empty; + emit updateNpcAutocalc(0/*all*/, empty); +} + +void CSMWorld::NpcAutoCalc::gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + static const QStringList gmsts(QStringList()<< "fNPCbaseMagickaMult" << "fAutoSpellChance" + << "fEffectCostMult" << "iAutoSpellAlterationMax" << "iAutoSpellConjurationMax" + << "iAutoSpellDestructionMax" << "iAutoSpellIllusionMax" << "iAutoSpellMysticismMax" + << "iAutoSpellRestorationMax" << "iAutoSpellTimesCanCast" << "iAutoSpellAttSkillMin"); + + bool match = false; + for (int row = topLeft.row(); row <= bottomRight.row(); ++row) + { + if (gmsts.contains(mData.getGmsts().getRecord(row).get().mId.c_str())) + { + match = true; + break; + } + } + + if (!match) + return; + + clearNpcStatsCache(); + + std::string empty; + emit updateNpcAutocalc(0/*all*/, empty); +} + +void CSMWorld::NpcAutoCalc::clearNpcStatsCache () +{ + for (std::map::iterator it (mNpcStatCache.begin()); + it != mNpcStatCache.end(); ++it) + delete it->second; + + mNpcStatCache.clear(); +} + +CSMWorld::NpcStats* CSMWorld::NpcAutoCalc::npcAutoCalculate(const ESM::NPC& npc) const +{ + CSMWorld::NpcStats *cachedStats = getCachedNpcData (npc.mId); + if (cachedStats) + return cachedStats; + + int raceIndex = mData.getRaces().searchId(npc.mRace); + int classIndex = mData.getClasses().searchId(npc.mClass); + // this can happen when creating a new game from scratch + if (raceIndex == -1 || classIndex == -1) + return 0; + + const ESM::Race *race = &mData.getRaces().getRecord(raceIndex).get(); + const ESM::Class *class_ = &mData.getClasses().getRecord(classIndex).get(); + + bool autoCalc = npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS; + short level = npc.mNpdt52.mLevel; + if (autoCalc) + level = npc.mNpdt12.mLevel; + + std::auto_ptr stats (new CSMWorld::NpcStats()); + + CSStore store(mData.getGmsts(), mData.getSkills(), mData.getMagicEffects(), static_cast&>(mData.getSpells())); + + if (autoCalc) + { + AutoCalc::autoCalcAttributesImpl (&npc, race, class_, level, *stats, &store); + + stats->setHealth(autoCalculateHealth(level, class_, *stats)); + stats->setMana(autoCalculateMana(*stats)); + stats->setFatigue(autoCalculateFatigue(*stats)); + + AutoCalc::autoCalcSkillsImpl(&npc, race, class_, level, *stats, &store); + + AutoCalc::autoCalculateSpells(race, *stats, &store); + } + else + { + for (std::vector::const_iterator it = npc.mSpells.mList.begin(); + it != npc.mSpells.mList.end(); ++it) + { + stats->addSpell(*it); + } + } + + // update spell info + const std::vector &racePowers = race->mPowers.mList; + for (unsigned int i = 0; i < racePowers.size(); ++i) + { + int type = -1; + int spellIndex = mData.getSpells().searchId(racePowers[i]); + if (spellIndex != -1) + type = mData.getSpells().getRecord(spellIndex).get().mData.mType; + stats->addPowers(racePowers[i], type); + } + // cost/chance + int skills[ESM::Skill::Length]; + if (autoCalc) + for (int i = 0; i< ESM::Skill::Length; ++i) + skills[i] = stats->getBaseSkill(i); + else + for (int i = 0; i< ESM::Skill::Length; ++i) + skills[i] = npc.mNpdt52.mSkills[i]; + + int attributes[ESM::Attribute::Length]; + if (autoCalc) + for (int i = 0; i< ESM::Attribute::Length; ++i) + attributes[i] = stats->getBaseAttribute(i); + else + { + attributes[ESM::Attribute::Strength] = npc.mNpdt52.mStrength; + attributes[ESM::Attribute::Willpower] = npc.mNpdt52.mWillpower; + attributes[ESM::Attribute::Agility] = npc.mNpdt52.mAgility; + attributes[ESM::Attribute::Speed] = npc.mNpdt52.mSpeed; + attributes[ESM::Attribute::Endurance] = npc.mNpdt52.mEndurance; + attributes[ESM::Attribute::Personality] = npc.mNpdt52.mPersonality; + attributes[ESM::Attribute::Luck] = npc.mNpdt52.mLuck; + } + + const std::vector& spells = stats->spells(); + for (std::vector::const_iterator it = spells.begin(); it != spells.end(); ++it) + { + int cost = -1; + int spellIndex = mData.getSpells().searchId((*it).mName); + const ESM::Spell* spell = 0; + if (spellIndex != -1) + { + spell = &mData.getSpells().getRecord(spellIndex).get(); + cost = spell->mData.mCost; + + int school; + float skillTerm; + AutoCalc::calcWeakestSchool(spell, skills, school, skillTerm, &store); + float chance = calcAutoCastChance(spell, skills, attributes, school, &store); + + stats->addCostAndChance((*it).mName, cost, (int)ceil(chance)); // percent + } + } + + if (stats.get() == 0) + return 0; + + CSMWorld::NpcStats *result = stats.release(); + //emit cacheNpcStats (npc.mId, result); + mNpcStatCache[npc.mId] = result; + return result; +} + +//void CSMWorld::NpcAutoCalc::cacheNpcStatsEvent (const std::string& id, CSMWorld::NpcStats *stats) +//{ + //mNpcStatCache[id] = stats; +//} + +CSMWorld::NpcStats* CSMWorld::NpcAutoCalc::getCachedNpcData (const std::string& id) const +{ + std::map::const_iterator it = mNpcStatCache.find(id); + if (it != mNpcStatCache.end()) + return it->second; + else + return 0; +} diff --git a/apps/opencs/model/world/npcautocalc.hpp b/apps/opencs/model/world/npcautocalc.hpp new file mode 100644 index 0000000000..ceb604d353 --- /dev/null +++ b/apps/opencs/model/world/npcautocalc.hpp @@ -0,0 +1,75 @@ +#ifndef CSM_WORLD_NPCAUTOCALC_H +#define CSM_WORLD_NPCAUTOCALC_H + +#include +#include + +#include +#include + +namespace ESM +{ + struct NPC; +} + +namespace CSMWorld +{ + class Data; + class NpcStats; + class IdTable; + class IdTree; + + class NpcAutoCalc : public QObject + { + Q_OBJECT + + const Data& mData; + const IdTable *mSkillModel; + const IdTable *mClassModel; + const IdTree *mRaceModel; + mutable std::map mNpcStatCache; + + public: + + NpcAutoCalc (const Data& data, const IdTable *gmsts, const IdTable *skills, const IdTable *classes, + const IdTree *races, const IdTree *objects); + + ~NpcAutoCalc (); + + NpcStats* npcAutoCalculate (const ESM::NPC& npc) const; + + private: + + // not implemented + NpcAutoCalc (const NpcAutoCalc&); + NpcAutoCalc& operator= (const NpcAutoCalc&); + + NpcStats* getCachedNpcData (const std::string& id) const; + + void clearNpcStatsCache (); + + signals: + + // refresh NPC dialogue subviews via object table model + void updateNpcAutocalc (int type, const std::string& id); + + //void cacheNpcStats (const std::string& id, NpcStats *stats) const; + + private slots: + + // for autocalc updates when gmst/race/class/skils tables change + void gmstDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void raceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void classDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void skillDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void npcDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + //void cacheNpcStatsEvent (const std::string& id, NpcStats *stats); + }; +} + +#endif // CSM_WORLD_NPCAUTOCALC_H diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index a268cb5f08..1683fd000f 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -12,6 +12,7 @@ #include "usertype.hpp" #include "idtree.hpp" #include "npcstats.hpp" +#include "npcautocalc.hpp" CSMWorld::PotionColumns::PotionColumns (const InventoryColumns& columns) : InventoryColumns (columns) {} @@ -777,7 +778,7 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d { if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) { - CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc); + CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc); if (!stats) { record.setModified (npc); @@ -817,7 +818,7 @@ void CSMWorld::NpcRefIdAdapter::setData (const RefIdColumn *column, RefIdData& d { npc.mNpdtType = ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS; npc.mNpdt12.mLevel = npc.mNpdt52.mLevel; // for NPC's loaded as non-autocalc - mData.npcAutoCalculate(npc); + mData.getNpcAutoCalc().npcAutoCalculate(npc); } } } @@ -888,7 +889,7 @@ QVariant CSMWorld::NpcAttributesRefIdAdapter::getNestedData (const RefIdColumn * else if (subColIndex == 1) if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) { - CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc); + CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc); if (!stats) return QVariant(); @@ -1020,7 +1021,7 @@ QVariant CSMWorld::NpcSkillsRefIdAdapter::getNestedData (const RefIdColumn *colu { if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) { - CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc); + CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc); if (!stats) return QVariant(); @@ -1108,7 +1109,7 @@ QVariant CSMWorld::NpcMiscRefIdAdapter::getNestedData (const RefIdColumn *column if (autoCalc) { - CSMWorld::NpcStats *stats = mData.npcAutoCalculate(npc); + CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(npc); switch (subColIndex) { @@ -1732,7 +1733,7 @@ QVariant NestedSpellRefIdAdapter::getNestedData (const RefIdColumn *co const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); - CSMWorld::NpcStats *stats = mData.npcAutoCalculate(record.get()); + CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(record.get()); if (!stats) return QVariant(); @@ -1766,7 +1767,7 @@ int NestedSpellRefIdAdapter::getNestedRowsCount(const RefIdColumn *col const Record& record = static_cast&> (data.getRecord (RefIdData::LocalIndex (index, mType))); - CSMWorld::NpcStats *stats = mData.npcAutoCalculate(record.get()); + CSMWorld::NpcStats *stats = mData.getNpcAutoCalc().npcAutoCalculate(record.get()); if (!stats) return 0; From a38881e4cf26782c4277d075340c8456e4212a64 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 8 Nov 2015 09:45:12 +1100 Subject: [PATCH 02/29] Further clean up of moving out NpcAutoCalc from Data. --- apps/opencs/model/world/data.cpp | 1 - apps/opencs/model/world/data.hpp | 3 --- 2 files changed, 4 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 84cb505c00..c87f590a8b 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -18,7 +18,6 @@ #include "resourcesmanager.hpp" #include "resourcetable.hpp" #include "nestedcoladapterimp.hpp" -#include "npcstats.hpp" #include "npcautocalc.hpp" void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update) diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 3d3996a1de..a3ccb28be8 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -61,7 +61,6 @@ namespace CSMWorld { class ResourcesManager; class Resources; - class NpcStats; class NpcAutoCalc; class Data : public QObject @@ -127,8 +126,6 @@ namespace CSMWorld const Data& self (); - void clearNpcStatsCache (); - public: Data (ToUTF8::FromType encoding, const ResourcesManager& resourcesManager); From f68e423c331f4bcb784f69f901eae6e14d2b0fd0 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 22 Nov 2015 18:43:44 +1100 Subject: [PATCH 03/29] Add missing Ogre headers. --- apps/opencs/view/render/cell.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp index f81b0708fa..44dccb67d5 100644 --- a/apps/opencs/view/render/cell.cpp +++ b/apps/opencs/view/render/cell.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include From cc984497d7d8e524d872613ed6416d1af175edf8 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 22 Nov 2015 18:49:58 +1100 Subject: [PATCH 04/29] Fix warning C4838 (conversion from 'double' to 'const float' requires a narrowing conversion) --- apps/opencs/model/doc/document.cpp | 516 ++++++++++++++--------------- 1 file changed, 258 insertions(+), 258 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 925eef659e..18dc9af4a4 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -278,264 +278,264 @@ void CSMDoc::Document::addGmsts() static const float gmstFloatsValues[] = { - 0.3, // fAIFleeFleeMult - 7.0, // fAIFleeHealthMult - 3.0, // fAIMagicSpellMult - 1.0, // fAIMeleeArmorMult - 1.0, // fAIMeleeSummWeaponMult - 2.0, // fAIMeleeWeaponMult - 5.0, // fAIRangeMagicSpellMult - 5.0, // fAIRangeMeleeWeaponMult - 2000.0, // fAlarmRadius - 1.0, // fAthleticsRunBonus - 40.0, // fAudioDefaultMaxDistance - 5.0, // fAudioDefaultMinDistance - 50.0, // fAudioMaxDistanceMult - 20.0, // fAudioMinDistanceMult - 60.0, // fAudioVoiceDefaultMaxDistance - 10.0, // fAudioVoiceDefaultMinDistance - 50.0, // fAutoPCSpellChance - 80.0, // fAutoSpellChance - 50.0, // fBargainOfferBase - -4.0, // fBargainOfferMulti - 24.0, // fBarterGoldResetDelay - 1.75, // fBaseRunMultiplier - 1.25, // fBlockStillBonus - 150.0, // fBribe1000Mod - 75.0, // fBribe100Mod - 35.0, // fBribe10Mod - 60.0, // fCombatAngleXY - 60.0, // fCombatAngleZ - 0.25, // fCombatArmorMinMult - -90.0, // fCombatBlockLeftAngle - 30.0, // fCombatBlockRightAngle - 4.0, // fCombatCriticalStrikeMult - 0.1, // fCombatDelayCreature - 0.1, // fCombatDelayNPC - 128.0, // fCombatDistance - 0.3, // fCombatDistanceWerewolfMod - 30.0, // fCombatForceSideAngle - 0.2, // fCombatInvisoMult - 1.5, // fCombatKODamageMult - 45.0, // fCombatTorsoSideAngle - 0.3, // fCombatTorsoStartPercent - 0.8, // fCombatTorsoStopPercent - 15.0, // fConstantEffectMult - 72.0, // fCorpseClearDelay - 72.0, // fCorpseRespawnDelay - 0.5, // fCrimeGoldDiscountMult - 0.9, // fCrimeGoldTurnInMult - 1.0, // fCrimeStealing - 0.5, // fDamageStrengthBase - 0.1, // fDamageStrengthMult - 5.0, // fDifficultyMult - 2.5, // fDiseaseXferChance - -10.0, // fDispAttacking - -1.0, // fDispBargainFailMod - 1.0, // fDispBargainSuccessMod - 0.0, // fDispCrimeMod - -10.0, // fDispDiseaseMod - 3.0, // fDispFactionMod - 1.0, // fDispFactionRankBase - 0.5, // fDispFactionRankMult - 1.0, // fDispositionMod - 50.0, // fDispPersonalityBase - 0.5, // fDispPersonalityMult - -25.0, // fDispPickPocketMod - 5.0, // fDispRaceMod - -0.5, // fDispStealing - -5.0, // fDispWeaponDrawn - 0.5, // fEffectCostMult - 0.1, // fElementalShieldMult - 3.0, // fEnchantmentChanceMult - 0.5, // fEnchantmentConstantChanceMult - 100.0, // fEnchantmentConstantDurationMult - 0.1, // fEnchantmentMult - 1000.0, // fEnchantmentValueMult - 0.3, // fEncumberedMoveEffect - 5.0, // fEncumbranceStrMult - 0.04, // fEndFatigueMult - 0.25, // fFallAcroBase - 0.01, // fFallAcroMult - 400.0, // fFallDamageDistanceMin - 0.0, // fFallDistanceBase - 0.07, // fFallDistanceMult - 2.0, // fFatigueAttackBase - 0.0, // fFatigueAttackMult - 1.25, // fFatigueBase - 4.0, // fFatigueBlockBase - 0.0, // fFatigueBlockMult - 5.0, // fFatigueJumpBase - 0.0, // fFatigueJumpMult - 0.5, // fFatigueMult - 2.5, // fFatigueReturnBase - 0.02, // fFatigueReturnMult - 5.0, // fFatigueRunBase - 2.0, // fFatigueRunMult - 1.5, // fFatigueSneakBase - 1.5, // fFatigueSneakMult - 0.0, // fFatigueSpellBase - 0.0, // fFatigueSpellCostMult - 0.0, // fFatigueSpellMult - 7.0, // fFatigueSwimRunBase - 0.0, // fFatigueSwimRunMult - 2.5, // fFatigueSwimWalkBase - 0.0, // fFatigueSwimWalkMult - 0.2, // fFightDispMult - 0.005, // fFightDistanceMultiplier - 50.0, // fFightStealing - 3000.0, // fFleeDistance - 512.0, // fGreetDistanceReset - 0.1, // fHandtoHandHealthPer - 1.0, // fHandToHandReach - 0.5, // fHoldBreathEndMult - 20.0, // fHoldBreathTime - 0.75, // fIdleChanceMultiplier - 1.0, // fIngredientMult - 0.5, // fInteriorHeadTrackMult - 128.0, // fJumpAcrobaticsBase - 4.0, // fJumpAcroMultiplier - 0.5, // fJumpEncumbranceBase - 1.0, // fJumpEncumbranceMultiplier - 0.5, // fJumpMoveBase - 0.5, // fJumpMoveMult - 1.0, // fJumpRunMultiplier - 0.5, // fKnockDownMult - 5.0, // fLevelMod - 0.1, // fLevelUpHealthEndMult - 0.6, // fLightMaxMod - 10.0, // fLuckMod - 10.0, // fMagesGuildTravel - 1.5, // fMagicCreatureCastDelay - 0.0167, // fMagicDetectRefreshRate - 1.0, // fMagicItemConstantMult - 1.0, // fMagicItemCostMult - 1.0, // fMagicItemOnceMult - 1.0, // fMagicItemPriceMult - 0.05, // fMagicItemRechargePerSecond - 1.0, // fMagicItemStrikeMult - 1.0, // fMagicItemUsedMult - 3.0, // fMagicStartIconBlink - 0.5, // fMagicSunBlockedMult - 0.75, // fMajorSkillBonus - 300.0, // fMaxFlySpeed - 0.5, // fMaxHandToHandMult - 400.0, // fMaxHeadTrackDistance - 200.0, // fMaxWalkSpeed - 300.0, // fMaxWalkSpeedCreature - 0.9, // fMedMaxMod - 0.1, // fMessageTimePerChar - 5.0, // fMinFlySpeed - 0.1, // fMinHandToHandMult - 1.0, // fMinorSkillBonus - 100.0, // fMinWalkSpeed - 5.0, // fMinWalkSpeedCreature - 1.25, // fMiscSkillBonus - 2.0, // fNPCbaseMagickaMult - 0.5, // fNPCHealthBarFade - 3.0, // fNPCHealthBarTime - 1.0, // fPCbaseMagickaMult - 0.3, // fPerDieRollMult - 5.0, // fPersonalityMod - 1.0, // fPerTempMult - -1.0, // fPickLockMult - 0.3, // fPickPocketMod - 20.0, // fPotionMinUsefulDuration - 0.5, // fPotionStrengthMult - 0.5, // fPotionT1DurMult - 1.5, // fPotionT1MagMult - 20.0, // fPotionT4BaseStrengthMult - 12.0, // fPotionT4EquipStrengthMult - 3000.0, // fProjectileMaxSpeed - 400.0, // fProjectileMinSpeed - 25.0, // fProjectileThrownStoreChance - 3.0, // fRepairAmountMult - 1.0, // fRepairMult - 1.0, // fReputationMod - 0.15, // fRestMagicMult - 0.0, // fSeriousWoundMult - 0.25, // fSleepRandMod - 0.3, // fSleepRestMod - -1.0, // fSneakBootMult - 0.5, // fSneakDistanceBase - 0.002, // fSneakDistanceMultiplier - 0.5, // fSneakNoViewMult - 1.0, // fSneakSkillMult - 0.75, // fSneakSpeedMultiplier - 1.0, // fSneakUseDelay - 500.0, // fSneakUseDist - 1.5, // fSneakViewMult - 3.0, // fSoulGemMult - 0.8, // fSpecialSkillBonus - 7.0, // fSpellMakingValueMult - 2.0, // fSpellPriceMult - 10.0, // fSpellValueMult - 0.25, // fStromWalkMult - 0.7, // fStromWindSpeed - 3.0, // fSuffocationDamage - 0.9, // fSwimHeightScale - 0.1, // fSwimRunAthleticsMult - 0.5, // fSwimRunBase - 0.02, // fSwimWalkAthleticsMult - 0.5, // fSwimWalkBase - 1.0, // fSwingBlockBase - 1.0, // fSwingBlockMult - 1000.0, // fTargetSpellMaxSpeed - 1000.0, // fThrownWeaponMaxSpeed - 300.0, // fThrownWeaponMinSpeed - 0.0, // fTrapCostMult - 4000.0, // fTravelMult - 16000.0,// fTravelTimeMult - 0.1, // fUnarmoredBase1 - 0.065, // fUnarmoredBase2 - 30.0, // fVanityDelay - 10.0, // fVoiceIdleOdds - 0.0, // fWaterReflectUpdateAlways - 10.0, // fWaterReflectUpdateSeldom - 0.1, // fWeaponDamageMult - 1.0, // fWeaponFatigueBlockMult - 0.25, // fWeaponFatigueMult - 150.0, // fWereWolfAcrobatics - 150.0, // fWereWolfAgility - 1.0, // fWereWolfAlchemy - 1.0, // fWereWolfAlteration - 1.0, // fWereWolfArmorer - 150.0, // fWereWolfAthletics - 1.0, // fWereWolfAxe - 1.0, // fWereWolfBlock - 1.0, // fWereWolfBluntWeapon - 1.0, // fWereWolfConjuration - 1.0, // fWereWolfDestruction - 1.0, // fWereWolfEnchant - 150.0, // fWereWolfEndurance - 400.0, // fWereWolfFatigue - 100.0, // fWereWolfHandtoHand - 2.0, // fWereWolfHealth - 1.0, // fWereWolfHeavyArmor - 1.0, // fWereWolfIllusion - 1.0, // fWereWolfIntellegence - 1.0, // fWereWolfLightArmor - 1.0, // fWereWolfLongBlade - 1.0, // fWereWolfLuck - 100.0, // fWereWolfMagicka - 1.0, // fWereWolfMarksman - 1.0, // fWereWolfMediumArmor - 1.0, // fWereWolfMerchantile - 1.0, // fWereWolfMysticism - 1.0, // fWereWolfPersonality - 1.0, // fWereWolfRestoration - 1.5, // fWereWolfRunMult - 1.0, // fWereWolfSecurity - 1.0, // fWereWolfShortBlade - 1.5, // fWereWolfSilverWeaponDamageMult - 1.0, // fWereWolfSneak - 1.0, // fWereWolfSpear - 1.0, // fWereWolfSpeechcraft - 150.0, // fWereWolfSpeed - 150.0, // fWereWolfStrength - 100.0, // fWereWolfUnarmored - 1.0, // fWereWolfWillPower - 15.0, // fWortChanceValue + 0.3f, // fAIFleeFleeMult + 7.0f, // fAIFleeHealthMult + 3.0f, // fAIMagicSpellMult + 1.0f, // fAIMeleeArmorMult + 1.0f, // fAIMeleeSummWeaponMult + 2.0f, // fAIMeleeWeaponMult + 5.0f, // fAIRangeMagicSpellMult + 5.0f, // fAIRangeMeleeWeaponMult + 2000.0f, // fAlarmRadius + 1.0f, // fAthleticsRunBonus + 40.0f, // fAudioDefaultMaxDistance + 5.0f, // fAudioDefaultMinDistance + 50.0f, // fAudioMaxDistanceMult + 20.0f, // fAudioMinDistanceMult + 60.0f, // fAudioVoiceDefaultMaxDistance + 10.0f, // fAudioVoiceDefaultMinDistance + 50.0f, // fAutoPCSpellChance + 80.0f, // fAutoSpellChance + 50.0f, // fBargainOfferBase + -4.0f, // fBargainOfferMulti + 24.0f, // fBarterGoldResetDelay + 1.75f, // fBaseRunMultiplier + 1.25f, // fBlockStillBonus + 150.0f, // fBribe1000Mod + 75.0f, // fBribe100Mod + 35.0f, // fBribe10Mod + 60.0f, // fCombatAngleXY + 60.0f, // fCombatAngleZ + 0.25f, // fCombatArmorMinMult + -90.0f, // fCombatBlockLeftAngle + 30.0f, // fCombatBlockRightAngle + 4.0f, // fCombatCriticalStrikeMult + 0.1f, // fCombatDelayCreature + 0.1f, // fCombatDelayNPC + 128.0f, // fCombatDistance + 0.3f, // fCombatDistanceWerewolfMod + 30.0f, // fCombatForceSideAngle + 0.2f, // fCombatInvisoMult + 1.5f, // fCombatKODamageMult + 45.0f, // fCombatTorsoSideAngle + 0.3f, // fCombatTorsoStartPercent + 0.8f, // fCombatTorsoStopPercent + 15.0f, // fConstantEffectMult + 72.0f, // fCorpseClearDelay + 72.0f, // fCorpseRespawnDelay + 0.5f, // fCrimeGoldDiscountMult + 0.9f, // fCrimeGoldTurnInMult + 1.0f, // fCrimeStealing + 0.5f, // fDamageStrengthBase + 0.1f, // fDamageStrengthMult + 5.0f, // fDifficultyMult + 2.5f, // fDiseaseXferChance + -10.0f, // fDispAttacking + -1.0f, // fDispBargainFailMod + 1.0f, // fDispBargainSuccessMod + 0.0f, // fDispCrimeMod + -10.0f, // fDispDiseaseMod + 3.0f, // fDispFactionMod + 1.0f, // fDispFactionRankBase + 0.5f, // fDispFactionRankMult + 1.0f, // fDispositionMod + 50.0f, // fDispPersonalityBase + 0.5f, // fDispPersonalityMult + -25.0f, // fDispPickPocketMod + 5.0f, // fDispRaceMod + -0.5f, // fDispStealing + -5.0f, // fDispWeaponDrawn + 0.5f, // fEffectCostMult + 0.1f, // fElementalShieldMult + 3.0f, // fEnchantmentChanceMult + 0.5f, // fEnchantmentConstantChanceMult + 100.0f, // fEnchantmentConstantDurationMult + 0.1f, // fEnchantmentMult + 1000.0f, // fEnchantmentValueMult + 0.3f, // fEncumberedMoveEffect + 5.0f, // fEncumbranceStrMult + 0.04f, // fEndFatigueMult + 0.25f, // fFallAcroBase + 0.01f, // fFallAcroMult + 400.0f, // fFallDamageDistanceMin + 0.0f, // fFallDistanceBase + 0.07f, // fFallDistanceMult + 2.0f, // fFatigueAttackBase + 0.0f, // fFatigueAttackMult + 1.25f, // fFatigueBase + 4.0f, // fFatigueBlockBase + 0.0f, // fFatigueBlockMult + 5.0f, // fFatigueJumpBase + 0.0f, // fFatigueJumpMult + 0.5f, // fFatigueMult + 2.5f, // fFatigueReturnBase + 0.02f, // fFatigueReturnMult + 5.0f, // fFatigueRunBase + 2.0f, // fFatigueRunMult + 1.5f, // fFatigueSneakBase + 1.5f, // fFatigueSneakMult + 0.0f, // fFatigueSpellBase + 0.0f, // fFatigueSpellCostMult + 0.0f, // fFatigueSpellMult + 7.0f, // fFatigueSwimRunBase + 0.0f, // fFatigueSwimRunMult + 2.5f, // fFatigueSwimWalkBase + 0.0f, // fFatigueSwimWalkMult + 0.2f, // fFightDispMult + 0.005f, // fFightDistanceMultiplier + 50.0f, // fFightStealing + 3000.0f, // fFleeDistance + 512.0f, // fGreetDistanceReset + 0.1f, // fHandtoHandHealthPer + 1.0f, // fHandToHandReach + 0.5f, // fHoldBreathEndMult + 20.0f, // fHoldBreathTime + 0.75f, // fIdleChanceMultiplier + 1.0f, // fIngredientMult + 0.5f, // fInteriorHeadTrackMult + 128.0f, // fJumpAcrobaticsBase + 4.0f, // fJumpAcroMultiplier + 0.5f, // fJumpEncumbranceBase + 1.0f, // fJumpEncumbranceMultiplier + 0.5f, // fJumpMoveBase + 0.5f, // fJumpMoveMult + 1.0f, // fJumpRunMultiplier + 0.5f, // fKnockDownMult + 5.0f, // fLevelMod + 0.1f, // fLevelUpHealthEndMult + 0.6f, // fLightMaxMod + 10.0f, // fLuckMod + 10.0f, // fMagesGuildTravel + 1.5f, // fMagicCreatureCastDelay + 0.0167f, // fMagicDetectRefreshRate + 1.0f, // fMagicItemConstantMult + 1.0f, // fMagicItemCostMult + 1.0f, // fMagicItemOnceMult + 1.0f, // fMagicItemPriceMult + 0.05f, // fMagicItemRechargePerSecond + 1.0f, // fMagicItemStrikeMult + 1.0f, // fMagicItemUsedMult + 3.0f, // fMagicStartIconBlink + 0.5f, // fMagicSunBlockedMult + 0.75f, // fMajorSkillBonus + 300.0f, // fMaxFlySpeed + 0.5f, // fMaxHandToHandMult + 400.0f, // fMaxHeadTrackDistance + 200.0f, // fMaxWalkSpeed + 300.0f, // fMaxWalkSpeedCreature + 0.9f, // fMedMaxMod + 0.1f, // fMessageTimePerChar + 5.0f, // fMinFlySpeed + 0.1f, // fMinHandToHandMult + 1.0f, // fMinorSkillBonus + 100.0f, // fMinWalkSpeed + 5.0f, // fMinWalkSpeedCreature + 1.25f, // fMiscSkillBonus + 2.0f, // fNPCbaseMagickaMult + 0.5f, // fNPCHealthBarFade + 3.0f, // fNPCHealthBarTime + 1.0f, // fPCbaseMagickaMult + 0.3f, // fPerDieRollMult + 5.0f, // fPersonalityMod + 1.0f, // fPerTempMult + -1.0f, // fPickLockMult + 0.3f, // fPickPocketMod + 20.0f, // fPotionMinUsefulDuration + 0.5f, // fPotionStrengthMult + 0.5f, // fPotionT1DurMult + 1.5f, // fPotionT1MagMult + 20.0f, // fPotionT4BaseStrengthMult + 12.0f, // fPotionT4EquipStrengthMult + 3000.0f, // fProjectileMaxSpeed + 400.0f, // fProjectileMinSpeed + 25.0f, // fProjectileThrownStoreChance + 3.0f, // fRepairAmountMult + 1.0f, // fRepairMult + 1.0f, // fReputationMod + 0.15f, // fRestMagicMult + 0.0f, // fSeriousWoundMult + 0.25f, // fSleepRandMod + 0.3f, // fSleepRestMod + -1.0f, // fSneakBootMult + 0.5f, // fSneakDistanceBase + 0.002f, // fSneakDistanceMultiplier + 0.5f, // fSneakNoViewMult + 1.0f, // fSneakSkillMult + 0.75f, // fSneakSpeedMultiplier + 1.0f, // fSneakUseDelay + 500.0f, // fSneakUseDist + 1.5f, // fSneakViewMult + 3.0f, // fSoulGemMult + 0.8f, // fSpecialSkillBonus + 7.0f, // fSpellMakingValueMult + 2.0f, // fSpellPriceMult + 10.0f, // fSpellValueMult + 0.25f, // fStromWalkMult + 0.7f, // fStromWindSpeed + 3.0f, // fSuffocationDamage + 0.9f, // fSwimHeightScale + 0.1f, // fSwimRunAthleticsMult + 0.5f, // fSwimRunBase + 0.02f, // fSwimWalkAthleticsMult + 0.5f, // fSwimWalkBase + 1.0f, // fSwingBlockBase + 1.0f, // fSwingBlockMult + 1000.0f, // fTargetSpellMaxSpeed + 1000.0f, // fThrownWeaponMaxSpeed + 300.0f, // fThrownWeaponMinSpeed + 0.0f, // fTrapCostMult + 4000.0f, // fTravelMult + 16000.0f,// fTravelTimeMult + 0.1f, // fUnarmoredBase1 + 0.065f, // fUnarmoredBase2 + 30.0f, // fVanityDelay + 10.0f, // fVoiceIdleOdds + 0.0f, // fWaterReflectUpdateAlways + 10.0f, // fWaterReflectUpdateSeldom + 0.1f, // fWeaponDamageMult + 1.0f, // fWeaponFatigueBlockMult + 0.25f, // fWeaponFatigueMult + 150.0f, // fWereWolfAcrobatics + 150.0f, // fWereWolfAgility + 1.0f, // fWereWolfAlchemy + 1.0f, // fWereWolfAlteration + 1.0f, // fWereWolfArmorer + 150.0f, // fWereWolfAthletics + 1.0f, // fWereWolfAxe + 1.0f, // fWereWolfBlock + 1.0f, // fWereWolfBluntWeapon + 1.0f, // fWereWolfConjuration + 1.0f, // fWereWolfDestruction + 1.0f, // fWereWolfEnchant + 150.0f, // fWereWolfEndurance + 400.0f, // fWereWolfFatigue + 100.0f, // fWereWolfHandtoHand + 2.0f, // fWereWolfHealth + 1.0f, // fWereWolfHeavyArmor + 1.0f, // fWereWolfIllusion + 1.0f, // fWereWolfIntellegence + 1.0f, // fWereWolfLightArmor + 1.0f, // fWereWolfLongBlade + 1.0f, // fWereWolfLuck + 100.0f, // fWereWolfMagicka + 1.0f, // fWereWolfMarksman + 1.0f, // fWereWolfMediumArmor + 1.0f, // fWereWolfMerchantile + 1.0f, // fWereWolfMysticism + 1.0f, // fWereWolfPersonality + 1.0f, // fWereWolfRestoration + 1.5f, // fWereWolfRunMult + 1.0f, // fWereWolfSecurity + 1.0f, // fWereWolfShortBlade + 1.5f, // fWereWolfSilverWeaponDamageMult + 1.0f, // fWereWolfSneak + 1.0f, // fWereWolfSpear + 1.0f, // fWereWolfSpeechcraft + 150.0f, // fWereWolfSpeed + 150.0f, // fWereWolfStrength + 100.0f, // fWereWolfUnarmored + 1.0f, // fWereWolfWillPower + 15.0f, // fWortChanceValue }; static const char *gmstIntegers[] = From d70064efe427fbd94e1b22d52b31b47b0a31677e Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 22 Nov 2015 19:15:06 +1100 Subject: [PATCH 05/29] Fix warning C4456 (declaration hides previous local declaration) --- apps/opencs/model/doc/loader.cpp | 8 ++++---- apps/opencs/model/doc/savingstages.cpp | 8 ++++---- apps/opencs/model/filter/parser.cpp | 6 +++--- apps/opencs/model/tools/mergestages.cpp | 6 +++--- apps/opencs/model/world/idtable.cpp | 4 ++-- apps/opencs/model/world/infocollection.cpp | 18 +++++++++--------- apps/opencs/model/world/refcollection.cpp | 6 +++--- apps/opencs/model/world/refidcollection.cpp | 4 ++-- apps/opencs/model/world/resources.cpp | 16 ++++++++-------- apps/opencs/view/render/textoverlay.cpp | 2 +- apps/opencs/view/world/dialoguesubview.cpp | 8 ++++---- 11 files changed, 43 insertions(+), 43 deletions(-) diff --git a/apps/opencs/model/doc/loader.cpp b/apps/opencs/model/doc/loader.cpp index cb3ff2cd0f..b522f89f2b 100644 --- a/apps/opencs/model/doc/loader.cpp +++ b/apps/opencs/model/doc/loader.cpp @@ -64,11 +64,11 @@ void CSMDoc::Loader::load() CSMWorld::UniversalId log (CSMWorld::UniversalId::Type_LoadErrorLog, 0); { // silence a g++ warning - for (CSMDoc::Messages::Iterator iter (messages.begin()); - iter!=messages.end(); ++iter) + for (CSMDoc::Messages::Iterator iter2 (messages.begin()); + iter2!=messages.end(); ++iter2) { - document->getReport (log)->add (*iter); - emit loadMessage (document, iter->mMessage); + document->getReport (log)->add (*iter2); + emit loadMessage (document, iter2->mMessage); } } diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index 3fba2cd85c..10faf30326 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -119,11 +119,11 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter) { - CSMWorld::RecordBase::State state = iter->mState; + CSMWorld::RecordBase::State recState = iter->mState; - if (state==CSMWorld::RecordBase::State_Modified || - state==CSMWorld::RecordBase::State_ModifiedOnly || - state==CSMWorld::RecordBase::State_Deleted) + if (recState==CSMWorld::RecordBase::State_Modified || + recState==CSMWorld::RecordBase::State_ModifiedOnly || + recState==CSMWorld::RecordBase::State_Deleted) { infoModified = true; break; diff --git a/apps/opencs/model/filter/parser.cpp b/apps/opencs/model/filter/parser.cpp index 7936a1ae2b..52de60280a 100644 --- a/apps/opencs/model/filter/parser.cpp +++ b/apps/opencs/model/filter/parser.cpp @@ -313,15 +313,15 @@ boost::shared_ptr CSMFilter::Parser::parseNAry (const Token& ke nodes.push_back (node); - Token token = getNextToken(); + Token token2 = getNextToken(); - if (!token || (token.mType!=Token::Type_Close && token.mType!=Token::Type_Comma)) + if (!token2 || (token2.mType!=Token::Type_Close && token2.mType!=Token::Type_Comma)) { error(); return boost::shared_ptr(); } - if (token.mType==Token::Type_Close) + if (token2.mType==Token::Type_Close) break; } diff --git a/apps/opencs/model/tools/mergestages.cpp b/apps/opencs/model/tools/mergestages.cpp index 52e1e69649..d936be593e 100644 --- a/apps/opencs/model/tools/mergestages.cpp +++ b/apps/opencs/model/tools/mergestages.cpp @@ -183,11 +183,11 @@ void CSMTools::MergeLandTexturesStage::perform (int stage, CSMDoc::Messages& mes CSMWorld::LandTexture texture = mState.mSource.getData().getLandTextures().getRecord (index).get(); - std::ostringstream stream; - stream << mNext->second-1 << "_0"; + std::ostringstream stream2; + stream2 << mNext->second-1 << "_0"; texture.mIndex = mNext->second-1; - texture.mId = stream.str(); + texture.mId = stream2.str(); CSMWorld::Record newRecord ( CSMWorld::RecordBase::State_ModifiedOnly, 0, &texture); diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 7f80eaa8e0..e6acf77de7 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -172,9 +172,9 @@ void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& reco if (index==-1) { - int index = mIdCollection->getAppendIndex (id, type); + int index2 = mIdCollection->getAppendIndex (id, type); - beginInsertRows (QModelIndex(), index, index); + beginInsertRows (QModelIndex(), index2, index2); mIdCollection->appendRecord (record, type); diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index 60c6130416..9eaa744d23 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -19,31 +19,31 @@ void CSMWorld::InfoCollection::load (const Info& record, bool base) record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record2.mBase : record2.mModified) = record; - int index = -1; + int index2 = -1; std::string topic = Misc::StringUtils::lowerCase (record2.get().mTopicId); if (!record2.get().mPrev.empty()) { - index = getInfoIndex (record2.get().mPrev, topic); + index2 = getInfoIndex (record2.get().mPrev, topic); - if (index!=-1) - ++index; + if (index2!=-1) + ++index2; } - if (index==-1 && !record2.get().mNext.empty()) + if (index2==-1 && !record2.get().mNext.empty()) { - index = getInfoIndex (record2.get().mNext, topic); + index2 = getInfoIndex (record2.get().mNext, topic); } - if (index==-1) + if (index2==-1) { Range range = getTopicRange (topic); - index = std::distance (getRecords().begin(), range.second); + index2 = std::distance (getRecords().begin(), range.second); } - insertRecord (record2, index); + insertRecord (record2, index2); } else { diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index f8818807bc..a60ee3d706 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -74,9 +74,9 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool 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 + std::ostringstream stream2; + stream2 << "#" << mref.mTarget[0] << " " << mref.mTarget[1]; + ref.mCell = stream2.str(); // overwrite } } } diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index a5e8133386..9045be5843 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -878,10 +878,10 @@ void CSMWorld::RefIdCollection::load (ESM::ESMReader& reader, bool base, Univers if (index==-1) { // new record - int index = mData.getAppendIndex (type); + int newIndex = mData.getAppendIndex (type); mData.appendRecord (type, id, base); - RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (index); + RefIdData::LocalIndex localIndex = mData.globalToLocalIndex (newIndex); mData.load (localIndex, reader, base); diff --git a/apps/opencs/model/world/resources.cpp b/apps/opencs/model/world/resources.cpp index 8bfd832483..fc5b127dc5 100644 --- a/apps/opencs/model/world/resources.cpp +++ b/apps/opencs/model/world/resources.cpp @@ -26,22 +26,22 @@ CSMWorld::Resources::Resources (const std::string& baseDirectory, UniversalId::T Ogre::StringVectorPtr resources = Ogre::ResourceGroupManager::getSingleton().listResourceNames (*iter); - for (Ogre::StringVector::const_iterator iter (resources->begin()); - iter!=resources->end(); ++iter) + for (Ogre::StringVector::const_iterator iter2 (resources->begin()); + iter2!=resources->end(); ++iter2) { - if (static_cast (iter->size())substr (0, baseSize)!=mBaseDirectory || - ((*iter)[baseSize]!='/' && (*iter)[baseSize]!='\\')) + if (static_cast (iter2->size())substr (0, baseSize)!=mBaseDirectory || + ((*iter2)[baseSize]!='/' && (*iter2)[baseSize]!='\\')) continue; if (extensions) { - std::string::size_type index = iter->find_last_of ('.'); + std::string::size_type index = iter2->find_last_of ('.'); if (index==std::string::npos) continue; - std::string extension = iter->substr (index+1); + std::string extension = iter2->substr (index+1); int i = 0; @@ -53,7 +53,7 @@ CSMWorld::Resources::Resources (const std::string& baseDirectory, UniversalId::T continue; } - std::string file = iter->substr (baseSize+1); + std::string file = iter2->substr (baseSize+1); mFiles.push_back (file); std::replace (file.begin(), file.end(), '\\', '/'); mIndex.insert (std::make_pair ( diff --git a/apps/opencs/view/render/textoverlay.cpp b/apps/opencs/view/render/textoverlay.cpp index 14b60be935..21e898738d 100644 --- a/apps/opencs/view/render/textoverlay.cpp +++ b/apps/opencs/view/render/textoverlay.cpp @@ -105,7 +105,7 @@ TextOverlay::TextOverlay(const Ogre::MovableObject* obj, const Ogre::Camera* cam "TransOverlayMaterial"); if(mQuadMaterial.isNull()) { - Ogre::MaterialPtr mQuadMaterial = Ogre::MaterialManager::getSingleton().create( + mQuadMaterial = Ogre::MaterialManager::getSingleton().create( "TransOverlayMaterial", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true ); Ogre::Pass *pass = mQuadMaterial->getTechnique( 0 )->getPass( 0 ); diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 14b7fcac3d..54e58a7cac 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -667,14 +667,14 @@ void CSVWorld::EditWidget::remake(int row) int displayRole = tree->nestedHeaderData (i, col, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt(); - CSMWorld::ColumnBase::Display display = + CSMWorld::ColumnBase::Display display2 = static_cast (displayRole); - mNestedTableDispatcher->makeDelegate (display); + mNestedTableDispatcher->makeDelegate (display2); // FIXME: assumed all columns are editable QWidget* editor = - mNestedTableDispatcher->makeEditor (display, tree->index (0, col, tree->index(row, i))); + mNestedTableDispatcher->makeEditor (display2, tree->index (0, col, tree->index(row, i))); if (editor) { mNestedTableMapper->addMapping (editor, col); @@ -699,7 +699,7 @@ void CSVWorld::EditWidget::remake(int row) label->setEnabled(false); } - createEditorContextMenu(editor, display, row); + createEditorContextMenu(editor, display2, row); } } mNestedTableMapper->setCurrentModelIndex(tree->index(0, 0, tree->index(row, i))); From 429075101038564e8d96f9ca251540cfb21b59d2 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 22 Nov 2015 19:17:10 +1100 Subject: [PATCH 06/29] Fix warning C4457 (declaration hides function parameter) --- apps/opencs/view/world/referenceablecreator.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/view/world/referenceablecreator.cpp b/apps/opencs/view/world/referenceablecreator.cpp index 1357ca46f0..836e8ac7dc 100644 --- a/apps/opencs/view/world/referenceablecreator.cpp +++ b/apps/opencs/view/world/referenceablecreator.cpp @@ -26,10 +26,10 @@ CSVWorld::ReferenceableCreator::ReferenceableCreator (CSMWorld::Data& data, QUnd for (std::vector::const_iterator iter (types.begin()); iter!=types.end(); ++iter) { - CSMWorld::UniversalId id (*iter, ""); + CSMWorld::UniversalId id2 (*iter, ""); - mType->addItem (QIcon (id.getIcon().c_str()), id.getTypeName().c_str(), - static_cast (id.getType())); + mType->addItem (QIcon (id2.getIcon().c_str()), id2.getTypeName().c_str(), + static_cast (id2.getType())); } insertBeforeButtons (mType, false); From 19eed6f4c4ff8219e504ae7df99fec6fc5557290 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 22 Nov 2015 19:23:11 +1100 Subject: [PATCH 07/29] Fix warning C4458 (declaration of 'data' hides class member) --- apps/opencs/view/filter/filterbox.cpp | 4 ++-- apps/opencs/view/render/pagedworldspacewidget.cpp | 8 ++++---- apps/opencs/view/render/unpagedworldspacewidget.cpp | 8 ++++---- apps/opencs/view/render/worldspacewidget.cpp | 6 +++--- apps/opencs/view/world/dragrecordtable.cpp | 4 ++-- apps/opencs/view/world/scenesubview.cpp | 12 ++++++------ apps/opencs/view/world/tablesubview.cpp | 8 ++++---- 7 files changed, 25 insertions(+), 25 deletions(-) diff --git a/apps/opencs/view/filter/filterbox.cpp b/apps/opencs/view/filter/filterbox.cpp index c6c6cc6ccc..e50191679e 100644 --- a/apps/opencs/view/filter/filterbox.cpp +++ b/apps/opencs/view/filter/filterbox.cpp @@ -38,9 +38,9 @@ void CSVFilter::FilterBox::dropEvent (QDropEvent* event) if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped return; - std::vector data = mime->getData(); + std::vector mimeData = mime->getData(); - emit recordDropped(data, event->proposedAction()); + emit recordDropped(mimeData, event->proposedAction()); } void CSVFilter::FilterBox::dragEnterEvent (QDragEnterEvent* event) diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp index cd247afe52..01d5d0d765 100644 --- a/apps/opencs/view/render/pagedworldspacewidget.cpp +++ b/apps/opencs/view/render/pagedworldspacewidget.cpp @@ -536,18 +536,18 @@ std::pair< int, int > CSVRender::PagedWorldspaceWidget::getCoordinatesFromId (co } bool CSVRender::PagedWorldspaceWidget::handleDrop ( - const std::vector< CSMWorld::UniversalId >& data, DropType type) + const std::vector< CSMWorld::UniversalId >& dropData, DropType type) { - if (WorldspaceWidget::handleDrop (data, type)) + if (WorldspaceWidget::handleDrop (dropData, type)) return true; if (type!=Type_CellsExterior) return false; bool selectionChanged = false; - for (unsigned i = 0; i < data.size(); ++i) + for (unsigned i = 0; i < dropData.size(); ++i) { - std::pair coordinates(getCoordinatesFromId(data[i].getId())); + std::pair coordinates(getCoordinatesFromId(dropData[i].getId())); if (mSelection.add(CSMWorld::CellCoordinates(coordinates.first, coordinates.second))) { selectionChanged = true; diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp index 321c79460a..7455f87360 100644 --- a/apps/opencs/view/render/unpagedworldspacewidget.cpp +++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp @@ -84,21 +84,21 @@ void CSVRender::UnpagedWorldspaceWidget::cellRowsAboutToBeRemoved (const QModelI emit closeRequest(); } -bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector& data, DropType type) +bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector& dropData, DropType type) { - if (WorldspaceWidget::handleDrop (data, type)) + if (WorldspaceWidget::handleDrop (dropData, type)) return true; if (type!=Type_CellsInterior) return false; - mCellId = data.begin()->getId(); + mCellId = dropData.begin()->getId(); Cell *cell = new Cell (getDocument(), getSceneManager(), mCellId, getDocument().getPhysics()); connect (cell->getSignalHandler(), SIGNAL(flagAsModified()), this, SLOT(flagAsModSlot())); mCell.reset (cell); update(); - emit cellChanged(*data.begin()); + emit cellChanged(*dropData.begin()); return true; } diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp index 3c1ef5c64c..53791e05e6 100644 --- a/apps/opencs/view/render/worldspacewidget.cpp +++ b/apps/opencs/view/render/worldspacewidget.cpp @@ -233,15 +233,15 @@ CSVRender::WorldspaceWidget::dropRequirments return ignored; } -bool CSVRender::WorldspaceWidget::handleDrop (const std::vector& data, +bool CSVRender::WorldspaceWidget::handleDrop (const std::vector& dropData, DropType type) { if (type==Type_DebugProfile) { if (mRun) { - for (std::vector::const_iterator iter (data.begin()); - iter!=data.end(); ++iter) + for (std::vector::const_iterator iter (dropData.begin()); + iter!=dropData.end(); ++iter) mRun->addProfile (iter->getId()); } diff --git a/apps/opencs/view/world/dragrecordtable.cpp b/apps/opencs/view/world/dragrecordtable.cpp index a5f933283c..03f5a8739a 100644 --- a/apps/opencs/view/world/dragrecordtable.cpp +++ b/apps/opencs/view/world/dragrecordtable.cpp @@ -67,8 +67,8 @@ void CSVWorld::DragRecordTable::dropEvent(QDropEvent *event) CSMWorld::ColumnBase::Display display = getIndexDisplayType(index); if (CSVWorld::DragDropUtils::canAcceptData(*event, display)) { - const CSMWorld::TableMimeData *data = CSVWorld::DragDropUtils::getTableMimeData(*event); - if (data->fromDocument(mDocument)) + const CSMWorld::TableMimeData *mimeData = CSVWorld::DragDropUtils::getTableMimeData(*event); + if (mimeData->fromDocument(mDocument)) { CSMWorld::UniversalId id = CSVWorld::DragDropUtils::getAcceptedData(*event, display); QVariant newIndexData = QString::fromUtf8(id.getId().c_str()); diff --git a/apps/opencs/view/world/scenesubview.cpp b/apps/opencs/view/world/scenesubview.cpp index c2f3442f89..75ace32bbe 100644 --- a/apps/opencs/view/world/scenesubview.cpp +++ b/apps/opencs/view/world/scenesubview.cpp @@ -187,18 +187,18 @@ void CSVWorld::SceneSubView::cellSelectionChanged (const CSMWorld::CellSelection emit updateTitle(); } -void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& data) +void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalId >& dropData) { CSVRender::PagedWorldspaceWidget* pagedNewWidget = NULL; CSVRender::UnpagedWorldspaceWidget* unPagedNewWidget = NULL; CSVWidget::SceneToolbar* toolbar = NULL; - CSVRender::WorldspaceWidget::DropType type = CSVRender::WorldspaceWidget::getDropType (data); + CSVRender::WorldspaceWidget::DropType type = CSVRender::WorldspaceWidget::getDropType (dropData); switch (mScene->getDropRequirements (type)) { case CSVRender::WorldspaceWidget::canHandle: - mScene->handleDrop (data, type); + mScene->handleDrop (dropData, type); break; case CSVRender::WorldspaceWidget::needPaged: @@ -206,15 +206,15 @@ void CSVWorld::SceneSubView::handleDrop (const std::vector< CSMWorld::UniversalI toolbar = makeToolbar(pagedNewWidget, widget_Paged); makeConnections(pagedNewWidget); replaceToolbarAndWorldspace(pagedNewWidget, toolbar); - mScene->handleDrop (data, type); + mScene->handleDrop (dropData, type); break; case CSVRender::WorldspaceWidget::needUnpaged: - unPagedNewWidget = new CSVRender::UnpagedWorldspaceWidget(data.begin()->getId(), mDocument, this); + unPagedNewWidget = new CSVRender::UnpagedWorldspaceWidget(dropData.begin()->getId(), mDocument, this); toolbar = makeToolbar(unPagedNewWidget, widget_Unpaged); makeConnections(unPagedNewWidget); replaceToolbarAndWorldspace(unPagedNewWidget, toolbar); - cellSelectionChanged(*(data.begin())); + cellSelectionChanged(*(dropData.begin())); break; case CSVRender::WorldspaceWidget::ignored: diff --git a/apps/opencs/view/world/tablesubview.cpp b/apps/opencs/view/world/tablesubview.cpp index 81f78cdadd..e424ab85fb 100644 --- a/apps/opencs/view/world/tablesubview.cpp +++ b/apps/opencs/view/world/tablesubview.cpp @@ -203,14 +203,14 @@ bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event) { if (QDropEvent* drop = dynamic_cast(event)) { - const CSMWorld::TableMimeData* data = dynamic_cast(drop->mimeData()); - if (!data) // May happen when non-records (e.g. plain text) are dragged and dropped + const CSMWorld::TableMimeData* mimeData = dynamic_cast(drop->mimeData()); + if (!mimeData) // May happen when non-records (e.g. plain text) are dragged and dropped return false; - bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); + bool handled = mimeData->holdsType(CSMWorld::UniversalId::Type_Filter); if (handled) { - mFilterBox->setRecordFilter(data->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); + mFilterBox->setRecordFilter(mimeData->returnMatching(CSMWorld::UniversalId::Type_Filter).getId()); } return handled; } From 77394fce99281cea65f30c67dbcd69a2d3eeba88 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 5 Dec 2015 20:38:09 +1100 Subject: [PATCH 08/29] Fix table being sorted twice (at least it appeared that way according to the sample profiler) - Quoting Qt-4.8: "Note: . Setting the property to true with setSortingEnabled() immediately triggers a call to sortByColumn() with the current sort section and order." --- apps/opencs/view/world/table.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 6c4ede6e2c..26abf1744f 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -280,12 +280,6 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionMode (QAbstractItemView::ExtendedSelection); - setSortingEnabled (sorting); - if (sorting) - { - sortByColumn (mModel->findColumnIndex(CSMWorld::Columns::ColumnId_Id), Qt::AscendingOrder); - } - int columns = mModel->columnCount(); for (int i=0; ifindColumnIndex(CSMWorld::Columns::ColumnId_Id), Qt::AscendingOrder); + } + setSortingEnabled (sorting); + mEditAction = new QAction (tr ("Edit Record"), this); connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord())); addAction (mEditAction); @@ -429,7 +430,7 @@ std::vector CSVWorld::Table::getSelectedIds() const QModelIndexList selectedRows = selectionModel()->selectedRows(); int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); - for (QModelIndexList::const_iterator iter (selectedRows.begin()); + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter != selectedRows.end(); ++iter) { From 257126ed69a5f6f964ba771766de061e81f87433 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 5 Dec 2015 20:50:47 +1100 Subject: [PATCH 09/29] Call push_back() if inserting to the end of the vector. It seems MSVC may be generating different code compared to insert(). --- apps/opencs/model/world/collection.hpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 16f5ce51f4..a136734612 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -443,19 +443,24 @@ namespace CSMWorld void Collection::insertRecord (const RecordBase& record, int index, UniversalId::Type type) { - if (index<0 || index>static_cast (mRecords.size())) + int size = static_cast(mRecords.size()); + if (index < 0 || index > size) throw std::runtime_error ("index out of range"); const Record& record2 = dynamic_cast&> (record); - mRecords.insert (mRecords.begin()+index, record2); + if (index == size) + mRecords.push_back (record2); + else + mRecords.insert (mRecords.begin()+index, record2); - if (index (mRecords.size())-1) + if (index < size-1) { - for (std::map::iterator iter (mIndex.begin()); iter!=mIndex.end(); - ++iter) - if (iter->second>=index) - ++(iter->second); + for (std::map::iterator iter (mIndex.begin()); iter!=mIndex.end(); ++iter) + { + if (iter->second >= index) + ++(iter->second); + } } mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId ( From 23e7e3c165bb2631f9d8eb298f86da862e91cefa Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 6 Dec 2015 15:14:05 +1100 Subject: [PATCH 10/29] Use std::unique_ptr to store records in collections, RefidCollection and RefIdData. - std::move support required (C++11) - MSVC 2013 or later should be fine --- apps/opencs/model/doc/document.cpp | 25 +++-- apps/opencs/model/doc/savingstages.cpp | 12 +- apps/opencs/model/tools/mergestages.cpp | 21 ++-- apps/opencs/model/tools/mergestages.hpp | 4 +- apps/opencs/model/world/collection.hpp | 105 +++++++++--------- apps/opencs/model/world/collectionbase.hpp | 5 +- apps/opencs/model/world/commands.cpp | 14 +-- apps/opencs/model/world/commands.hpp | 5 +- apps/opencs/model/world/data.cpp | 7 +- apps/opencs/model/world/idcollection.hpp | 33 +++--- apps/opencs/model/world/idtable.cpp | 7 +- apps/opencs/model/world/idtable.hpp | 3 +- apps/opencs/model/world/infocollection.cpp | 45 ++++---- apps/opencs/model/world/infocollection.hpp | 2 +- .../opencs/model/world/nestedidcollection.hpp | 32 +++--- .../model/world/nestedinfocollection.cpp | 32 +++--- apps/opencs/model/world/record.hpp | 18 +-- apps/opencs/model/world/refcollection.cpp | 23 ++-- apps/opencs/model/world/refidcollection.cpp | 15 +-- apps/opencs/model/world/refidcollection.hpp | 4 +- apps/opencs/model/world/refiddata.cpp | 12 +- apps/opencs/model/world/refiddata.hpp | 53 +++++---- 22 files changed, 248 insertions(+), 229 deletions(-) diff --git a/apps/opencs/model/doc/document.cpp b/apps/opencs/model/doc/document.cpp index 18dc9af4a4..7665036431 100644 --- a/apps/opencs/model/doc/document.cpp +++ b/apps/opencs/model/doc/document.cpp @@ -2,6 +2,7 @@ #include #include +#include #include @@ -2091,10 +2092,10 @@ void CSMDoc::Document::addOptionalGmst (const ESM::GameSetting& gmst) { if (getData().getGmsts().searchId (gmst.mId)==-1) { - CSMWorld::Record record; - record.mBase = gmst; - record.mState = CSMWorld::RecordBase::State_BaseOnly; - getData().getGmsts().appendRecord (record); + std::unique_ptr > record(new CSMWorld::Record); + record->mBase = gmst; + record->mState = CSMWorld::RecordBase::State_BaseOnly; + getData().getGmsts().appendRecord (std::move(record)); } } @@ -2102,10 +2103,10 @@ void CSMDoc::Document::addOptionalGlobal (const ESM::Global& global) { if (getData().getGlobals().searchId (global.mId)==-1) { - CSMWorld::Record record; - record.mBase = global; - record.mState = CSMWorld::RecordBase::State_BaseOnly; - getData().getGlobals().appendRecord (record); + std::unique_ptr > record(new CSMWorld::Record); + record->mBase = global; + record->mState = CSMWorld::RecordBase::State_BaseOnly; + getData().getGlobals().appendRecord (std::move(record)); } } @@ -2113,10 +2114,10 @@ void CSMDoc::Document::addOptionalMagicEffect (const ESM::MagicEffect& magicEffe { if (getData().getMagicEffects().searchId (magicEffect.mId)==-1) { - CSMWorld::Record record; - record.mBase = magicEffect; - record.mState = CSMWorld::RecordBase::State_BaseOnly; - getData().getMagicEffects().appendRecord (record); + std::unique_ptr > record(new CSMWorld::Record); + record->mBase = magicEffect; + record->mState = CSMWorld::RecordBase::State_BaseOnly; + getData().getMagicEffects().appendRecord (std::move(record)); } } diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index db38c4779b..e18f2311fd 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -118,7 +118,7 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter) { - if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted) + if ((*iter)->isModified() || (*iter)->mState == CSMWorld::RecordBase::State_Deleted) { infoModified = true; break; @@ -144,9 +144,9 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message // write modified selected info records for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; ++iter) { - if (iter->isModified() || iter->mState == CSMWorld::RecordBase::State_Deleted) + if ((*iter)->isModified() || (*iter)->mState == CSMWorld::RecordBase::State_Deleted) { - ESM::DialInfo info = iter->get(); + ESM::DialInfo info = (*iter)->get(); info.mId = info.mId.substr (info.mId.find_last_of ('#')+1); info.mPrev = ""; @@ -155,7 +155,7 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message CSMWorld::InfoCollection::RecordConstIterator prev = iter; --prev; - info.mPrev = prev->get().mId.substr (prev->get().mId.find_last_of ('#')+1); + info.mPrev = (*prev)->get().mId.substr ((*prev)->get().mId.find_last_of ('#')+1); } CSMWorld::InfoCollection::RecordConstIterator next = iter; @@ -164,11 +164,11 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, Messages& message info.mNext = ""; if (next!=range.second) { - info.mNext = next->get().mId.substr (next->get().mId.find_last_of ('#')+1); + info.mNext = (*next)->get().mId.substr ((*next)->get().mId.find_last_of ('#')+1); } writer.startRecord (info.sRecordId); - info.save (writer, iter->mState == CSMWorld::RecordBase::State_Deleted); + info.save (writer, (*iter)->mState == CSMWorld::RecordBase::State_Deleted); writer.endRecord (info.sRecordId); } } diff --git a/apps/opencs/model/tools/mergestages.cpp b/apps/opencs/model/tools/mergestages.cpp index d936be593e..105a722d00 100644 --- a/apps/opencs/model/tools/mergestages.cpp +++ b/apps/opencs/model/tools/mergestages.cpp @@ -100,10 +100,9 @@ void CSMTools::MergeReferencesStage::perform (int stage, CSMDoc::Messages& messa ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase (ref.mCell)]++; ref.mRefNum.mContentFile = 0; - CSMWorld::Record newRecord ( - CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref); - - mState.mTarget->getData().getReferences().appendRecord (newRecord); + mState.mTarget->getData().getReferences().appendRecord ( + std::make_unique >( + CSMWorld::Record(CSMWorld::RecordBase::State_ModifiedOnly, 0, &ref))); } } @@ -189,10 +188,9 @@ void CSMTools::MergeLandTexturesStage::perform (int stage, CSMDoc::Messages& mes texture.mIndex = mNext->second-1; texture.mId = stream2.str(); - CSMWorld::Record newRecord ( - CSMWorld::RecordBase::State_ModifiedOnly, 0, &texture); - - mState.mTarget->getData().getLandTextures().appendRecord (newRecord); + mState.mTarget->getData().getLandTextures().appendRecord ( + std::make_unique >( + CSMWorld::Record(CSMWorld::RecordBase::State_ModifiedOnly, 0, &texture))); found = true; } @@ -250,9 +248,8 @@ void CSMTools::MergeLandStage::perform (int stage, CSMDoc::Messages& messages) } } - CSMWorld::Record newRecord ( - CSMWorld::RecordBase::State_ModifiedOnly, 0, &newLand); - - mState.mTarget->getData().getLand().appendRecord (newRecord); + mState.mTarget->getData().getLand().appendRecord ( + std::make_unique >( + CSMWorld::Record(CSMWorld::RecordBase::State_ModifiedOnly, 0, &newLand))); } } diff --git a/apps/opencs/model/tools/mergestages.hpp b/apps/opencs/model/tools/mergestages.hpp index f88f5be9f6..cfb7852dc5 100644 --- a/apps/opencs/model/tools/mergestages.hpp +++ b/apps/opencs/model/tools/mergestages.hpp @@ -3,6 +3,7 @@ #include #include +#include #include @@ -82,7 +83,8 @@ namespace CSMTools const CSMWorld::Record& record = source.getRecord (stage); if (!record.isDeleted()) - target.appendRecord (CSMWorld::Record (CSMWorld::RecordBase::State_ModifiedOnly, 0, &record.get())); + target.appendRecord (std::make_unique >( + CSMWorld::Record(CSMWorld::RecordBase::State_ModifiedOnly, 0, &record.get()))); } class MergeRefIdsStage : public CSMDoc::Stage diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index a136734612..475a262632 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include @@ -49,7 +50,7 @@ namespace CSMWorld private: - std::vector > mRecords; + std::vector > > mRecords; std::map mIndex; std::vector *> mColumns; @@ -61,7 +62,7 @@ namespace CSMWorld const std::map& getIdMap() const; - const std::vector >& getRecords() const; + const std::vector > >& getRecords() const; bool reorderRowsImp (int baseIndex, const std::vector& newOrder); ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices @@ -112,12 +113,12 @@ namespace CSMWorld ////< Search record with \a id. /// \return index of record (if found) or -1 (not found) - virtual void replace (int index, const RecordBase& record); + virtual void replace (int index, std::unique_ptr record); ///< If the record type does not match, an exception is thrown. /// /// \attention \a record must not change the ID. - virtual void appendRecord (const RecordBase& record, + virtual void appendRecord (std::unique_ptr record, UniversalId::Type type = UniversalId::Type_None); ///< If the record type does not match, an exception is thrown. ///< \param type Will be ignored, unless the collection supports multiple record types @@ -135,7 +136,7 @@ namespace CSMWorld /// /// \param listDeleted include deleted record in the list - virtual void insertRecord (const RecordBase& record, int index, + virtual void insertRecord (std::unique_ptr record, int index, UniversalId::Type type = UniversalId::Type_None); ///< Insert record before index. /// @@ -152,7 +153,7 @@ namespace CSMWorld void addColumn (Column *column); - void setRecord (int index, const Record& record); + void setRecord (int index, std::unique_ptr > record); ///< \attention This function must not change the ID. NestableColumn *getNestableColumn (int column) const; @@ -165,7 +166,7 @@ namespace CSMWorld } template - const std::vector >& Collection::getRecords() const + const std::vector > >& Collection::getRecords() const { return mRecords; } @@ -185,15 +186,15 @@ namespace CSMWorld return false; // reorder records - std::vector > buffer (size); + std::vector > > buffer (size); for (int i=0; isetModified (buffer[newOrder[i]]->get()); } - std::copy (buffer.begin(), buffer.end(), mRecords.begin()+baseIndex); + std::move (buffer.begin(), buffer.end(), mRecords.begin()+baseIndex); // adjust index for (std::map::iterator iter (mIndex.begin()); iter!=mIndex.end(); @@ -210,12 +211,12 @@ namespace CSMWorld const std::string& destination, const UniversalId::Type type) { - Record copy; - copy.mModified = getRecord(origin).get(); - copy.mState = RecordBase::State_ModifiedOnly; - copy.get().mId = destination; + std::unique_ptr > copy(new Record); + copy->mModified = getRecord(origin).get(); + copy->mState = RecordBase::State_ModifiedOnly; + copy->get().mId = destination; - insertRecord(copy, getAppendIndex(destination, type)); + insertRecord(std::move(copy), getAppendIndex(destination, type)); } template @@ -238,15 +239,15 @@ namespace CSMWorld if (iter==mIndex.end()) { - Record record2; - record2.mState = Record::State_ModifiedOnly; - record2.mModified = record; + std::unique_ptr > record2(new Record); + record2->mState = Record::State_ModifiedOnly; + record2->mModified = record; - insertRecord (record2, getAppendIndex (id)); + insertRecord (std::move(record2), getAppendIndex (id)); } else { - mRecords[iter->second].setModified (record); + mRecords[iter->second]->setModified (record); } } @@ -259,7 +260,7 @@ namespace CSMWorld template std::string Collection::getId (int index) const { - return IdAccessorT().getId (mRecords.at (index).get()); + return IdAccessorT().getId (mRecords.at (index)->get()); } template @@ -282,13 +283,13 @@ namespace CSMWorld template QVariant Collection::getData (int index, int column) const { - return mColumns.at (column)->get (mRecords.at (index)); + return mColumns.at (column)->get (*mRecords.at (index)); } template void Collection::setData (int index, int column, const QVariant& data) { - return mColumns.at (column)->set (mRecords.at (index), data); + return mColumns.at (column)->set (*mRecords.at (index), data); } template @@ -315,8 +316,8 @@ namespace CSMWorld template void Collection::merge() { - for (typename std::vector >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter) - iter->merge(); + for (typename std::vector > >::iterator iter (mRecords.begin()); iter!=mRecords.end(); ++iter) + (*iter)->merge(); purge(); } @@ -328,7 +329,7 @@ namespace CSMWorld while (i (mRecords.size())) { - if (mRecords[i].isErased()) + if (mRecords[i]->isErased()) removeRows (i, 1); else ++i; @@ -369,11 +370,11 @@ namespace CSMWorld IdAccessorT().getId (record) = id; record.blank(); - Record record2; - record2.mState = Record::State_ModifiedOnly; - record2.mModified = record; + std::unique_ptr > record2(new Record); + record2->mState = Record::State_ModifiedOnly; + record2->mModified = record; - insertRecord (record2, getAppendIndex (id, type), type); + insertRecord (std::move(record2), getAppendIndex (id, type), type); } template @@ -390,18 +391,19 @@ namespace CSMWorld } template - void Collection::replace (int index, const RecordBase& record) + void Collection::replace (int index, std::unique_ptr record) { - mRecords.at (index) = dynamic_cast&> (record); + std::unique_ptr > tmp(static_cast*>(record.release())); + mRecords.at (index) = std::move(tmp); } template - void Collection::appendRecord (const RecordBase& record, + void Collection::appendRecord (std::unique_ptr record, UniversalId::Type type) { - insertRecord (record, - getAppendIndex (IdAccessorT().getId ( - dynamic_cast&> (record).get()), type), type); + int index = + getAppendIndex(IdAccessorT().getId(static_cast*>(record.get())->get()), type); + insertRecord (std::move(record), index, type); } template @@ -419,8 +421,8 @@ namespace CSMWorld for (typename std::map::const_iterator iter = mIndex.begin(); iter!=mIndex.end(); ++iter) { - if (listDeleted || !mRecords[iter->second].isDeleted()) - ids.push_back (IdAccessorT().getId (mRecords[iter->second].get())); + if (listDeleted || !mRecords[iter->second]->isDeleted()) + ids.push_back (IdAccessorT().getId (mRecords[iter->second]->get())); } return ids; @@ -430,29 +432,30 @@ namespace CSMWorld const Record& Collection::getRecord (const std::string& id) const { int index = getIndex (id); - return mRecords.at (index); + return *mRecords.at (index); } template const Record& Collection::getRecord (int index) const { - return mRecords.at (index); + return *mRecords.at (index); } template - void Collection::insertRecord (const RecordBase& record, int index, + void Collection::insertRecord (std::unique_ptr record, int index, UniversalId::Type type) { int size = static_cast(mRecords.size()); if (index < 0 || index > size) throw std::runtime_error ("index out of range"); - const Record& record2 = dynamic_cast&> (record); + std::unique_ptr > record2(static_cast*>(record.release())); + std::string lowerId = Misc::StringUtils::lowerCase(IdAccessorT().getId(record2->get())); if (index == size) - mRecords.push_back (record2); + mRecords.push_back (std::move(record2)); else - mRecords.insert (mRecords.begin()+index, record2); + mRecords.insert (mRecords.begin()+index, std::move(record2)); if (index < size-1) { @@ -463,18 +466,18 @@ namespace CSMWorld } } - mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (IdAccessorT().getId ( - record2.get())), index)); + mIndex.insert (std::make_pair (lowerId, index)); } template - void Collection::setRecord (int index, const Record& record) + void Collection::setRecord (int index, + std::unique_ptr > record) { - if (Misc::StringUtils::lowerCase (IdAccessorT().getId (mRecords.at (index).get()))!= - Misc::StringUtils::lowerCase (IdAccessorT().getId (record.get()))) + if (Misc::StringUtils::lowerCase (IdAccessorT().getId (mRecords.at (index)->get())) != + Misc::StringUtils::lowerCase (IdAccessorT().getId (record->get()))) throw std::runtime_error ("attempt to change the ID of a record"); - mRecords.at (index) = record; + mRecords.at (index) = std::move(record); } template diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index ef826e31c8..fa0399e49e 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "universalid.hpp" #include "columns.hpp" @@ -64,13 +65,13 @@ namespace CSMWorld ////< Search record with \a id. /// \return index of record (if found) or -1 (not found) - virtual void replace (int index, const RecordBase& record) = 0; + virtual void replace (int index, std::unique_ptr record) = 0; ///< If the record type does not match, an exception is thrown. /// /// \attention \a record must not change the ID. ///< \param type Will be ignored, unless the collection supports multiple record types - virtual void appendRecord (const RecordBase& record, + virtual void appendRecord (std::unique_ptr record, UniversalId::Type type = UniversalId::Type_None) = 0; ///< If the record type does not match, an exception is thrown. diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index d510cd1038..0f6681ba17 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -124,16 +124,15 @@ void CSMWorld::CreateCommand::undo() } CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent) -: QUndoCommand (parent), mModel (model), mId (id), mOld (0) + : QUndoCommand (parent), mModel (model), mId (id) { setText (("Revert record " + id).c_str()); - mOld = model.getRecord (id).clone(); + mOld = std::move(model.getRecord (id).clone()); } CSMWorld::RevertCommand::~RevertCommand() { - delete mOld; } void CSMWorld::RevertCommand::redo() @@ -155,21 +154,20 @@ void CSMWorld::RevertCommand::redo() void CSMWorld::RevertCommand::undo() { - mModel.setRecord (mId, *mOld); + mModel.setRecord (mId, std::move(mOld)); } CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, CSMWorld::UniversalId::Type type, QUndoCommand* parent) -: QUndoCommand (parent), mModel (model), mId (id), mOld (0), mType(type) +: QUndoCommand (parent), mModel (model), mId (id), mType(type) { setText (("Delete record " + id).c_str()); - mOld = model.getRecord (id).clone(); + mOld = std::move(model.getRecord (id).clone()); } CSMWorld::DeleteCommand::~DeleteCommand() { - delete mOld; } void CSMWorld::DeleteCommand::redo() @@ -191,7 +189,7 @@ void CSMWorld::DeleteCommand::redo() void CSMWorld::DeleteCommand::undo() { - mModel.setRecord (mId, *mOld, mType); + mModel.setRecord (mId, std::move(mOld), mType); } diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 05f21017c0..5cfad58566 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -97,7 +98,7 @@ namespace CSMWorld { IdTable& mModel; std::string mId; - RecordBase *mOld; + std::unique_ptr mOld; // not implemented RevertCommand (const RevertCommand&); @@ -118,7 +119,7 @@ namespace CSMWorld { IdTable& mModel; std::string mId; - RecordBase *mOld; + std::unique_ptr mOld; UniversalId::Type mType; // not implemented diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 9f92935dc0..0e35c4141a 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -861,8 +861,8 @@ const CSMWorld::MetaData& CSMWorld::Data::getMetaData() const void CSMWorld::Data::setMetaData (const MetaData& metaData) { - Record record (RecordBase::State_ModifiedOnly, 0, &metaData); - mMetaData.setRecord (0, record); + mMetaData.setRecord (0, std::make_unique >( + Record(RecordBase::State_ModifiedOnly, 0, &metaData))); } const CSMWorld::NpcAutoCalc& CSMWorld::Data::getNpcAutoCalc() const @@ -920,7 +920,8 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base metaData.mId = "sys::meta"; metaData.load (*mReader); - mMetaData.setRecord (0, Record (RecordBase::State_ModifiedOnly, 0, &metaData)); + mMetaData.setRecord (0, std::make_unique >( + Record (RecordBase::State_ModifiedOnly, 0, &metaData))); } return mReader->getRecordCount(); diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index ea6eefb882..5a7d2410df 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -66,9 +66,9 @@ namespace CSMWorld return -1; } - Record baseRecord = this->getRecord (index); - baseRecord.mState = RecordBase::State_Deleted; - this->setRecord (index, baseRecord); + std::unique_ptr > baseRecord(new Record(this->getRecord(index))); + baseRecord->mState = RecordBase::State_Deleted; + this->setRecord(index, std::move(baseRecord)); return index; } @@ -79,30 +79,31 @@ namespace CSMWorld int IdCollection::load (const ESXRecordT& record, bool base, int index) { - if (index==-2) + if (index==-2) // index unknown index = this->searchId (IdAccessorT().getId (record)); if (index==-1) { // new record - Record record2; - record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; - (base ? record2.mBase : record2.mModified) = record; + std::unique_ptr > record2(new Record); + record2->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + (base ? record2->mBase : record2->mModified) = record; index = this->getSize(); - this->appendRecord (record2); + this->appendRecord(std::move(record2)); } else { // old record - Record record2 = Collection::getRecord (index); + std::unique_ptr > record2( + new Record(Collection::getRecord(index))); if (base) - record2.mBase = record; + record2->mBase = record; else - record2.setModified (record); + record2->setModified(record); - this->setRecord (index, record2); + this->setRecord(index, std::move(record2)); } return index; @@ -116,7 +117,7 @@ namespace CSMWorld if (index==-1) return false; - Record record = Collection::getRecord (index); + const Record& record = Collection::getRecord (index); if (record.isDeleted()) return false; @@ -127,8 +128,10 @@ namespace CSMWorld } else { - record.mState = RecordBase::State_Deleted; - this->setRecord (index, record); + std::unique_ptr > record2( + new Record(Collection::getRecord(index))); + record2->mState = RecordBase::State_Deleted; + this->setRecord(index, std::move(record2)); } return true; diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index e6acf77de7..ec666b3dfc 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -166,7 +166,8 @@ QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) return index(mIdCollection->getIndex (id), column); } -void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& record, CSMWorld::UniversalId::Type type) +void CSMWorld::IdTable::setRecord (const std::string& id, + std::unique_ptr record, CSMWorld::UniversalId::Type type) { int index = mIdCollection->searchId (id); @@ -176,13 +177,13 @@ void CSMWorld::IdTable::setRecord (const std::string& id, const RecordBase& reco beginInsertRows (QModelIndex(), index2, index2); - mIdCollection->appendRecord (record, type); + mIdCollection->appendRecord (std::move(record), type); endInsertRows(); } else { - mIdCollection->replace (index, record); + mIdCollection->replace (index, std::move(record)); emit dataChanged (CSMWorld::IdTable::index (index, 0), CSMWorld::IdTable::index (index, mIdCollection->getColumns()-1)); } diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 9ecba02142..7219e5a480 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -2,6 +2,7 @@ #define CSM_WOLRD_IDTABLE_H #include +#include #include "idtablebase.hpp" #include "universalid.hpp" @@ -59,7 +60,7 @@ namespace CSMWorld virtual QModelIndex getModelIndex (const std::string& id, int column) const; - void setRecord (const std::string& id, const RecordBase& record, + void setRecord (const std::string& id, std::unique_ptr record, UniversalId::Type type = UniversalId::Type_None); ///< Add record or overwrite existing recrod. diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index 083d07da0d..c331a2d730 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -15,25 +15,25 @@ void CSMWorld::InfoCollection::load (const Info& record, bool base) if (index==-1) { // new record - Record record2; - record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; - (base ? record2.mBase : record2.mModified) = record; + std::unique_ptr > record2(new Record); + record2->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + (base ? record2->mBase : record2->mModified) = record; int index2 = -1; - std::string topic = Misc::StringUtils::lowerCase (record2.get().mTopicId); + std::string topic = Misc::StringUtils::lowerCase (record2->get().mTopicId); - if (!record2.get().mPrev.empty()) + if (!record2->get().mPrev.empty()) { - index2 = getInfoIndex (record2.get().mPrev, topic); + index2 = getInfoIndex (record2->get().mPrev, topic); if (index2!=-1) ++index2; } - if (index2==-1 && !record2.get().mNext.empty()) + if (index2==-1 && !record2->get().mNext.empty()) { - index2 = getInfoIndex (record2.get().mNext, topic); + index2 = getInfoIndex (record2->get().mNext, topic); } if (index2==-1) @@ -43,19 +43,19 @@ void CSMWorld::InfoCollection::load (const Info& record, bool base) index2 = std::distance (getRecords().begin(), range.second); } - insertRecord (record2, index2); + insertRecord (std::move(record2), index2); } else { // old record - Record record2 = getRecord (index); + std::unique_ptr > record2(new Record(getRecord(index))); if (base) - record2.mBase = record; + record2->mBase = record; else - record2.setModified (record); + record2->setModified (record); - setRecord (index, record2); + setRecord (index, std::move(record2)); } } @@ -66,7 +66,7 @@ int CSMWorld::InfoCollection::getInfoIndex (const std::string& id, const std::st std::pair range = getTopicRange (topic); for (; range.first!=range.second; ++range.first) - if (Misc::StringUtils::ciEqual(range.first->get().mId, fullId)) + if (Misc::StringUtils::ciEqual((*range.first).get()->get().mId, fullId)) return std::distance (getRecords().begin(), range.first); return -1; @@ -128,9 +128,9 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES } else { - Record record = getRecord (index); - record.mState = RecordBase::State_Deleted; - setRecord (index, record); + std::unique_ptr > record(new Record(getRecord(index))); + record->mState = RecordBase::State_Deleted; + setRecord (index, std::move(record)); } } else @@ -171,7 +171,7 @@ CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const s while (begin != getRecords().begin()) { - if (!Misc::StringUtils::ciEqual(begin->get().mTopicId, topic2)) + if (!Misc::StringUtils::ciEqual((*begin)->get().mTopicId, topic2)) { // we've gone one too far, go back ++begin; @@ -184,7 +184,7 @@ CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const s RecordConstIterator end = begin; for (; end!=getRecords().end(); ++end) - if (!Misc::StringUtils::ciEqual(end->get().mTopicId, topic2)) + if (!Misc::StringUtils::ciEqual((*end)->get().mTopicId, topic2)) break; return Range (begin, end); @@ -199,7 +199,7 @@ void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId std::map::const_iterator end = getIdMap().end(); for (; current != end; ++current) { - Record record = getRecord(current->second); + const Record& record = getRecord(current->second); if (Misc::StringUtils::ciEqual(dialogueId, record.get().mTopicId)) { @@ -209,8 +209,9 @@ void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId } else { - record.mState = RecordBase::State_Deleted; - setRecord(current->second, record); + std::unique_ptr > record2(new Record(record)); + record2->mState = RecordBase::State_Deleted; + setRecord(current->second, std::move(record2)); } } else diff --git a/apps/opencs/model/world/infocollection.hpp b/apps/opencs/model/world/infocollection.hpp index e5a5575c78..da389763ca 100644 --- a/apps/opencs/model/world/infocollection.hpp +++ b/apps/opencs/model/world/infocollection.hpp @@ -15,7 +15,7 @@ namespace CSMWorld { public: - typedef std::vector >::const_iterator RecordConstIterator; + typedef std::vector > >::const_iterator RecordConstIterator; typedef std::pair Range; private: diff --git a/apps/opencs/model/world/nestedidcollection.hpp b/apps/opencs/model/world/nestedidcollection.hpp index 56b1123659..08ed70d427 100644 --- a/apps/opencs/model/world/nestedidcollection.hpp +++ b/apps/opencs/model/world/nestedidcollection.hpp @@ -90,23 +90,23 @@ namespace CSMWorld template void NestedIdCollection::addNestedRow(int row, int column, int position) { - Record record; - record.assign(Collection::getRecord(row)); + std::unique_ptr > record(new Record); + record->assign(Collection::getRecord(row)); - getAdapter(Collection::getColumn(column)).addRow(record, position); + getAdapter(Collection::getColumn(column)).addRow(*record, position); - Collection::setRecord(row, record); + Collection::setRecord(row, std::move(record)); } template void NestedIdCollection::removeNestedRows(int row, int column, int subRow) { - Record record; - record.assign(Collection::getRecord(row)); + std::unique_ptr > record(new Record); + record->assign(Collection::getRecord(row)); - getAdapter(Collection::getColumn(column)).removeRow(record, subRow); + getAdapter(Collection::getColumn(column)).removeRow(*record, subRow); - Collection::setRecord(row, record); + Collection::setRecord(row, std::move(record)); } template @@ -121,13 +121,13 @@ namespace CSMWorld void NestedIdCollection::setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn) { - Record record; - record.assign(Collection::getRecord(row)); + std::unique_ptr > record(new Record); + record->assign(Collection::getRecord(row)); getAdapter(Collection::getColumn(column)).setData( - record, data, subRow, subColumn); + *record, data, subRow, subColumn); - Collection::setRecord(row, record); + Collection::setRecord(row, std::move(record)); } template @@ -142,13 +142,13 @@ namespace CSMWorld void NestedIdCollection::setNestedTable(int row, int column, const CSMWorld::NestedTableWrapperBase& nestedTable) { - Record record; - record.assign(Collection::getRecord(row)); + std::unique_ptr > record(new Record); + record->assign(Collection::getRecord(row)); getAdapter(Collection::getColumn(column)).setTable( - record, nestedTable); + *record, nestedTable); - Collection::setRecord(row, record); + Collection::setRecord(row, std::move(record)); } template diff --git a/apps/opencs/model/world/nestedinfocollection.cpp b/apps/opencs/model/world/nestedinfocollection.cpp index 4abaaf9c02..d404bb9a63 100644 --- a/apps/opencs/model/world/nestedinfocollection.cpp +++ b/apps/opencs/model/world/nestedinfocollection.cpp @@ -35,22 +35,22 @@ namespace CSMWorld void NestedInfoCollection::addNestedRow(int row, int column, int position) { - Record record; - record.assign(Collection >::getRecord(row)); + std::unique_ptr > record(new Record); + record->assign(Collection >::getRecord(row)); - getAdapter(Collection >::getColumn(column)).addRow(record, position); + getAdapter(Collection >::getColumn(column)).addRow(*record, position); - Collection >::setRecord(row, record); + Collection >::setRecord(row, std::move(record)); } void NestedInfoCollection::removeNestedRows(int row, int column, int subRow) { - Record record; - record.assign(Collection >::getRecord(row)); + std::unique_ptr > record(new Record); + record->assign(Collection >::getRecord(row)); - getAdapter(Collection >::getColumn(column)).removeRow(record, subRow); + getAdapter(Collection >::getColumn(column)).removeRow(*record, subRow); - Collection >::setRecord(row, record); + Collection >::setRecord(row, std::move(record)); } QVariant NestedInfoCollection::getNestedData (int row, @@ -63,13 +63,13 @@ namespace CSMWorld void NestedInfoCollection::setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn) { - Record record; - record.assign(Collection >::getRecord(row)); + std::unique_ptr > record(new Record); + record->assign(Collection >::getRecord(row)); getAdapter(Collection >::getColumn(column)).setData( - record, data, subRow, subColumn); + *record, data, subRow, subColumn); - Collection >::setRecord(row, record); + Collection >::setRecord(row, std::move(record)); } CSMWorld::NestedTableWrapperBase* NestedInfoCollection::nestedTable(int row, @@ -82,13 +82,13 @@ namespace CSMWorld void NestedInfoCollection::setNestedTable(int row, int column, const CSMWorld::NestedTableWrapperBase& nestedTable) { - Record record; - record.assign(Collection >::getRecord(row)); + std::unique_ptr > record(new Record); + record->assign(Collection >::getRecord(row)); getAdapter(Collection >::getColumn(column)).setTable( - record, nestedTable); + *record, nestedTable); - Collection >::setRecord(row, record); + Collection >::setRecord(row, std::move(record)); } int NestedInfoCollection::getNestedRowsCount(int row, int column) const diff --git a/apps/opencs/model/world/record.hpp b/apps/opencs/model/world/record.hpp index 3362f9f963..91e4c3c421 100644 --- a/apps/opencs/model/world/record.hpp +++ b/apps/opencs/model/world/record.hpp @@ -1,6 +1,7 @@ #ifndef CSM_WOLRD_RECORD_H #define CSM_WOLRD_RECORD_H +#include #include namespace CSMWorld @@ -20,9 +21,9 @@ namespace CSMWorld virtual ~RecordBase(); - virtual RecordBase *clone() const = 0; + virtual std::unique_ptr clone() const = 0; - virtual RecordBase *modifiedCopy() const = 0; + virtual std::unique_ptr modifiedCopy() const = 0; virtual void assign (const RecordBase& record) = 0; ///< Will throw an exception if the types don't match. @@ -45,9 +46,9 @@ namespace CSMWorld Record(State state, const ESXRecordT *base = 0, const ESXRecordT *modified = 0); - virtual RecordBase *clone() const; + virtual std::unique_ptr clone() const; - virtual RecordBase *modifiedCopy() const; + virtual std::unique_ptr modifiedCopy() const; virtual void assign (const RecordBase& record); @@ -85,15 +86,16 @@ namespace CSMWorld } template - RecordBase *Record::modifiedCopy() const + std::unique_ptr Record::modifiedCopy() const { - return new Record (State_ModifiedOnly, 0, &(this->get())); + return std::make_unique >( + Record(State_ModifiedOnly, 0, &(this->get()))); } template - RecordBase *Record::clone() const + std::unique_ptr Record::clone() const { - return new Record (*this); + return std::make_unique >(Record(*this)); } template diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 3f3688fb70..2568a5120e 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -92,8 +92,6 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool int index = getIndex (iter->second); - Record record = getRecord (index); - if (base) { removeRows (index, 1); @@ -101,8 +99,9 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool } else { - record.mState = RecordBase::State_Deleted; - setRecord (index, record); + std::unique_ptr > record2(new Record(getRecord(index))); + record2->mState = RecordBase::State_Deleted; + setRecord(index, std::move(record2)); } continue; @@ -113,11 +112,11 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool // new reference ref.mId = getNewId(); - Record record; - record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; - (base ? record.mBase : record.mModified) = ref; + std::unique_ptr > record(new Record); + record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + (base ? record->mBase : record->mModified) = ref; - appendRecord (record); + appendRecord(std::move(record)); cache.insert (std::make_pair (ref.mRefNum, ref.mId)); } @@ -128,11 +127,11 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool int index = getIndex (ref.mId); - Record record = getRecord (index); - record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified; - (base ? record.mBase : record.mModified) = ref; + std::unique_ptr > record(new Record(getRecord(index))); + record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified; + (base ? record->mBase : record->mModified) = ref; - setRecord (index, record); + setRecord(index, std::move(record)); } } } diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 9560539b20..7737e62c05 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -810,30 +810,31 @@ int CSMWorld::RefIdCollection::searchId (const std::string& id) const return mData.localToGlobalIndex (localIndex); } -void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record) +void CSMWorld::RefIdCollection::replace (int index, std::unique_ptr record) { - mData.getRecord (mData.globalToLocalIndex (index)).assign (record); + mData.getRecord (mData.globalToLocalIndex (index)).assign (*record.release()); } void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin, const std::string& destination, const CSMWorld::UniversalId::Type type) { - std::auto_ptr newRecord(mData.getRecord(mData.searchId(origin)).modifiedCopy()); + std::unique_ptr newRecord = + std::move(mData.getRecord(mData.searchId(origin)).modifiedCopy()); mAdapters.find(type)->second->setId(*newRecord, destination); - mData.insertRecord(*newRecord, type, destination); + mData.insertRecord(std::move(newRecord), type, destination); } -void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, +void CSMWorld::RefIdCollection::appendRecord (std::unique_ptr record, UniversalId::Type type) { - std::string id = findAdapter (type).getId (record); + std::string id = findAdapter (type).getId (*record.get()); int index = mData.getAppendIndex (type); mData.appendRecord (type, id, false); - mData.getRecord (mData.globalToLocalIndex (index)).assign (record); + mData.getRecord (mData.globalToLocalIndex (index)).assign (*record.release()); } const CSMWorld::RecordBase& CSMWorld::RefIdCollection::getRecord (const std::string& id) const diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index 4db39348e3..f749e7df7b 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -89,12 +89,12 @@ namespace CSMWorld ////< Search record with \a id. /// \return index of record (if found) or -1 (not found) - virtual void replace (int index, const RecordBase& record); + virtual void replace (int index, std::unique_ptr record); ///< If the record type does not match, an exception is thrown. /// /// \attention \a record must not change the ID. - virtual void appendRecord (const RecordBase& record, UniversalId::Type type); + virtual void appendRecord (std::unique_ptr record, UniversalId::Type type); ///< If the record type does not match, an exception is thrown. /// ///< \param type Will be ignored, unless the collection supports multiple record types diff --git a/apps/opencs/model/world/refiddata.cpp b/apps/opencs/model/world/refiddata.cpp index 2d8c9ac108..298a62021a 100644 --- a/apps/opencs/model/world/refiddata.cpp +++ b/apps/opencs/model/world/refiddata.cpp @@ -367,7 +367,7 @@ const CSMWorld::RefIdDataContainer< ESM::Static >& CSMWorld::RefIdData::getStati return mStatics; } -void CSMWorld::RefIdData::insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id) +void CSMWorld::RefIdData::insertRecord (std::unique_ptr record, CSMWorld::UniversalId::Type type, const std::string& id) { std::map::iterator iter = mRecordContainers.find (type); @@ -375,7 +375,7 @@ void CSMWorld::RefIdData::insertRecord (CSMWorld::RecordBase& record, CSMWorld:: if (iter==mRecordContainers.end()) throw std::logic_error ("invalid local index type"); - iter->second->insertRecord(record); + iter->second->insertRecord(std::move(record)); mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id), LocalIndex (iter->second->getSize()-1, type))); @@ -387,9 +387,7 @@ void CSMWorld::RefIdData::copyTo (int index, RefIdData& target) const RefIdDataContainerBase *source = mRecordContainers.find (localIndex.second)->second; - std::string id = source->getId (localIndex.first); - - std::auto_ptr newRecord (source->getRecord (localIndex.first).modifiedCopy()); - - target.insertRecord (*newRecord, localIndex.second, id); + target.insertRecord(source->getRecord(localIndex.first).modifiedCopy(), + localIndex.second, + source->getId(localIndex.first)); } diff --git a/apps/opencs/model/world/refiddata.hpp b/apps/opencs/model/world/refiddata.hpp index 59cad6a661..4a0900e6d4 100644 --- a/apps/opencs/model/world/refiddata.hpp +++ b/apps/opencs/model/world/refiddata.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -49,7 +50,7 @@ namespace CSMWorld virtual void appendRecord (const std::string& id, bool base) = 0; - virtual void insertRecord (RecordBase& record) = 0; + virtual void insertRecord (std::unique_ptr record) = 0; virtual int load (ESM::ESMReader& reader, bool base) = 0; ///< \return index of a loaded record or -1 if no record was loaded @@ -64,7 +65,7 @@ namespace CSMWorld template struct RefIdDataContainer : public RefIdDataContainerBase { - std::vector > mContainer; + std::vector > > mContainer; virtual int getSize() const; @@ -74,7 +75,7 @@ namespace CSMWorld virtual void appendRecord (const std::string& id, bool base); - virtual void insertRecord (RecordBase& record); + virtual void insertRecord (std::unique_ptr record); virtual int load (ESM::ESMReader& reader, bool base); ///< \return index of a loaded record or -1 if no record was loaded @@ -87,10 +88,18 @@ namespace CSMWorld }; template - void RefIdDataContainer::insertRecord(RecordBase& record) + void RefIdDataContainer::insertRecord(std::unique_ptr record) { - Record& newRecord = dynamic_cast& >(record); - mContainer.push_back(newRecord); + Record *tmp = dynamic_cast*>(record.get()); + if(tmp != nullptr) + { + record.release(); + std::unique_ptr > newRecord; + newRecord.reset(tmp); + mContainer.push_back(std::move(newRecord)); + } + else + throw std::runtime_error ("invalid record for RefIdDataContainer"); } template @@ -102,27 +111,27 @@ namespace CSMWorld template const RecordBase& RefIdDataContainer::getRecord (int index) const { - return mContainer.at (index); + return *mContainer.at (index); } template RecordBase& RefIdDataContainer::getRecord (int index) { - return mContainer.at (index); + return *mContainer.at (index); } template void RefIdDataContainer::appendRecord (const std::string& id, bool base) { - Record record; + std::unique_ptr > record(new Record); - record.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; + record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; - record.mBase.mId = id; - record.mModified.mId = id; - (base ? record.mBase : record.mModified).blank(); + record->mBase.mId = id; + record->mModified.mId = id; + (base ? record->mBase : record->mModified).blank(); - mContainer.push_back (record); + mContainer.push_back (std::move(record)); } template @@ -137,7 +146,7 @@ namespace CSMWorld int numRecords = static_cast(mContainer.size()); for (; index < numRecords; ++index) { - if (Misc::StringUtils::ciEqual(mContainer[index].get().mId, record.mId)) + if (Misc::StringUtils::ciEqual(mContainer[index]->get().mId, record.mId)) { break; } @@ -155,7 +164,7 @@ namespace CSMWorld // Flag the record as Deleted even for a base content file. // RefIdData is responsible for its erasure. - mContainer[index].mState = RecordBase::State_Deleted; + mContainer[index]->mState = RecordBase::State_Deleted; } else { @@ -164,16 +173,16 @@ namespace CSMWorld appendRecord(record.mId, base); if (base) { - mContainer.back().mBase = record; + mContainer.back()->mBase = record; } else { - mContainer.back().mModified = record; + mContainer.back()->mModified = record; } } else if (!base) { - mContainer[index].setModified(record); + mContainer[index]->setModified(record); } } @@ -192,13 +201,13 @@ namespace CSMWorld template std::string RefIdDataContainer::getId (int index) const { - return mContainer.at (index).get().mId; + return mContainer.at (index)->get().mId; } template void RefIdDataContainer::save (int index, ESM::ESMWriter& writer) const { - Record record = mContainer.at(index); + const Record& record = *mContainer.at(index); if (record.isModified() || record.mState == RecordBase::State_Deleted) { @@ -260,7 +269,7 @@ namespace CSMWorld void erase (int index, int count); - void insertRecord (CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, + void insertRecord (std::unique_ptr record, CSMWorld::UniversalId::Type type, const std::string& id); const RecordBase& getRecord (const LocalIndex& index) const; From 19af94b73eb4d8974f16f48fbda7ed87d561508c Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 6 Dec 2015 15:20:45 +1100 Subject: [PATCH 11/29] Reduce copying further by adding move constructors and move assignment operators to CellRef structs. --- apps/opencs/model/world/ref.cpp | 22 ++++++++ apps/opencs/model/world/ref.hpp | 5 ++ apps/opencs/model/world/refcollection.cpp | 4 +- components/esm/cellref.cpp | 61 +++++++++++++++++++++++ components/esm/cellref.hpp | 9 ++++ 5 files changed, 99 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/ref.cpp b/apps/opencs/model/world/ref.cpp index 638f7ec9ca..8c6f1d8365 100644 --- a/apps/opencs/model/world/ref.cpp +++ b/apps/opencs/model/world/ref.cpp @@ -4,10 +4,32 @@ CSMWorld::CellRef::CellRef() { + mId.clear(); + mCell.clear(); + mOriginalCell.clear(); + mRefNum.mIndex = 0; mRefNum.mContentFile = 0; } +CSMWorld::CellRef::CellRef (CSMWorld::CellRef&& other) : ESM::CellRef (other) +{ + *this = std::move(other); +} + +CSMWorld::CellRef& CSMWorld::CellRef::operator= (CSMWorld::CellRef&& other) +{ + if (this != &other) + { + ESM::CellRef::operator= (other); + mId = std::move(other.mId); + mCell = std::move(other.mCell); + mOriginalCell = std::move(other.mOriginalCell); + } + + return *this; +} + std::pair CSMWorld::CellRef::getCellIndex() const { const int cellSize = 8192; diff --git a/apps/opencs/model/world/ref.hpp b/apps/opencs/model/world/ref.hpp index c60392221a..c439e7ca5d 100644 --- a/apps/opencs/model/world/ref.hpp +++ b/apps/opencs/model/world/ref.hpp @@ -15,6 +15,11 @@ namespace CSMWorld std::string mOriginalCell; CellRef(); + CellRef(const CellRef&) = default; + CellRef& operator= (const CellRef&) = default; + + CellRef (CellRef&& other); + CellRef& operator= (CellRef&& other); /// Calculate cell index based on coordinates (x and y) std::pair getCellIndex() const; diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 2568a5120e..117aeb0121 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -114,7 +114,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool std::unique_ptr > record(new Record); record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; - (base ? record->mBase : record->mModified) = ref; + (base ? record->mBase : record->mModified) = std::move(ref); appendRecord(std::move(record)); @@ -129,7 +129,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool std::unique_ptr > record(new Record(getRecord(index))); record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified; - (base ? record->mBase : record->mModified) = ref; + (base ? record->mBase : record->mModified) = std::move(ref); setRecord(index, std::move(record)); } diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 76a82fe232..b3fb410e38 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -206,6 +206,67 @@ void ESM::CellRef::blank() } } +ESM::CellRef::CellRef () +{ + blank(); +} + +ESM::CellRef::CellRef (const CellRef& other) + : mRefNum(other.mRefNum) + , mRefID(other.mRefID) + , mScale(other.mScale) + , mOwner(other.mOwner) + , mGlobalVariable(other.mGlobalVariable) + , mSoul(other.mSoul) + , mFaction(other.mFaction) + , mFactionRank(other.mFactionRank) + , mChargeInt(other.mChargeInt) + , mEnchantmentCharge(other.mEnchantmentCharge) + , mGoldValue(other.mGoldValue) + , mTeleport(other.mTeleport) + , mDoorDest(other.mDoorDest) + , mDestCell(other.mDestCell) + , mLockLevel(other.mLockLevel) + , mKey(other.mKey) + , mTrap(other.mTrap) + , mReferenceBlocked(other.mReferenceBlocked) + , mPos(other.mPos) +{ +} + +ESM::CellRef::CellRef (CellRef&& other) +{ + *this = std::move(other); +} + +ESM::CellRef& ESM::CellRef::operator= (CellRef&& other) +{ + if (this != &other) + { + mRefNum = other.mRefNum; // RefNum + mRefID = std::move(other.mRefID); + mScale = other.mScale; + mOwner = std::move(other.mOwner); + mGlobalVariable = std::move(other.mGlobalVariable); + mSoul = std::move(other.mSoul); + mFaction = std::move(other.mFaction); + mFactionRank = other.mFactionRank; + mChargeInt = other.mChargeInt; + mEnchantmentCharge = other.mEnchantmentCharge; + mGoldValue = other.mGoldValue; + mTeleport = other.mTeleport; + mDoorDest = other.mDoorDest; // Position + mDestCell = std::move(other.mDestCell); + mLockLevel = other.mLockLevel; + mKey = std::move(other.mKey); + mTrap = std::move(other.mTrap); + mReferenceBlocked = other.mReferenceBlocked; + mPos = other.mPos; // Position + } + + return *this; +} + bool ESM::operator== (const RefNum& left, const RefNum& right) { return left.mIndex==right.mIndex && left.mContentFile==right.mContentFile; diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index c371a4f015..f84386b3ad 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -109,6 +109,15 @@ namespace ESM void save (ESMWriter &esm, bool wideRefNum = false, bool inInventory = false, bool isDeleted = false) const; void blank(); + + CellRef(); + ~CellRef() = default; + + CellRef(const CellRef&); + CellRef& operator=(const CellRef&) = default; + + CellRef (CellRef&& other); + CellRef& operator=(CellRef&& other); }; bool operator== (const RefNum& left, const RefNum& right); From 65df15a89d7fa129dc92bb6c2538aa4befccd8ab Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 6 Dec 2015 15:26:20 +1100 Subject: [PATCH 12/29] Suppress MSVC 2015 warnings about hidden/deleted base class move constructors and move assignment operators. --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index bd01db606e..4395df940f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -704,6 +704,11 @@ if (WIN32) 4800 # Boolean optimization warning, e.g. myBool = (myInt != 0) instead of myBool = myInt ) + # MSVC 2015 + if (MSVC_VERSION GREATER 1899) + set(WARNINGS_DISABLE ${WARNINGS_DISABLE} 5026 5027) + endif() + foreach(d ${WARNINGS_DISABLE}) set(WARNINGS "${WARNINGS} /wd${d}") endforeach(d) From ff072441fde05d189cb06cb44cc9c360690c2f09 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 6 Dec 2015 19:48:56 +1100 Subject: [PATCH 13/29] Change the loader's top progress bar to show total number of records processed rather than the number of files. --- apps/opencs/model/doc/loader.cpp | 8 ++--- apps/opencs/model/world/data.cpp | 16 +++++++++ apps/opencs/model/world/data.hpp | 2 ++ apps/opencs/view/doc/loader.cpp | 56 +++++++++++++++++--------------- apps/opencs/view/doc/loader.hpp | 8 +++-- 5 files changed, 56 insertions(+), 34 deletions(-) diff --git a/apps/opencs/model/doc/loader.cpp b/apps/opencs/model/doc/loader.cpp index b522f89f2b..993e076a4e 100644 --- a/apps/opencs/model/doc/loader.cpp +++ b/apps/opencs/model/doc/loader.cpp @@ -77,19 +77,19 @@ void CSMDoc::Loader::load() return; } - if (iter->second.mFilesecond.mFilegetContentFiles()[iter->second.mFile]; - int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, false); + int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, /*project*/false); iter->second.mRecordsLeft = true; iter->second.mRecordsLoaded = 0; emit nextStage (document, path.filename().string(), steps); } - else if (iter->second.mFile==size) + else if (iter->second.mFile==size) // start loading the last (project) file { - int steps = document->getData().startLoading (document->getProjectPath(), false, true); + int steps = document->getData().startLoading (document->getProjectPath(), /*base*/false, true); iter->second.mRecordsLeft = true; iter->second.mRecordsLoaded = 0; diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 0e35c4141a..d8916935da 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -897,6 +897,22 @@ void CSMWorld::Data::merge() mGlobals.merge(); } +int CSMWorld::Data::getTotalRecords (const std::vector& files) +{ + int records = 0; + + std::unique_ptr reader = std::unique_ptr(new ESM::ESMReader); + + for (unsigned int i = 0; i < files.size(); ++i) + { + reader->open(files[i].string()); + records += reader->getRecordCount(); + reader->close(); + } + + return records; +} + int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base, bool project) { // Don't delete the Reader yet. Some record types store a reference to the Reader to handle on-demand loading diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index a3ccb28be8..d59b836070 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -260,6 +260,8 @@ namespace CSMWorld void merge(); ///< Merge modified into base. + int getTotalRecords (const std::vector& files); // for better loading bar + int startLoading (const boost::filesystem::path& path, bool base, bool project); ///< Begin merging content of a file into base or modified. /// diff --git a/apps/opencs/view/doc/loader.cpp b/apps/opencs/view/doc/loader.cpp index 713295d702..36cf42c245 100644 --- a/apps/opencs/view/doc/loader.cpp +++ b/apps/opencs/view/doc/loader.cpp @@ -17,7 +17,7 @@ void CSVDoc::LoadingDocument::closeEvent (QCloseEvent *event) } CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document) -: mDocument (document), mAborted (false), mMessages (0), mTotalRecords (0) +: mDocument (document), mAborted (false), mMessages (0), mRecordsLabel (0), mTotalRecordsLabel (0) { setWindowTitle (QString::fromUtf8((std::string("Opening ") + document->getSavePath().filename().string()).c_str())); @@ -25,26 +25,25 @@ CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document) mLayout = new QVBoxLayout (this); - // file progress - mFile = new QLabel (this); + // total progress + mTotalRecordsLabel = new QLabel (this); - mLayout->addWidget (mFile); + mLayout->addWidget (mTotalRecordsLabel); - mFileProgress = new QProgressBar (this); + mTotalProgress = new QProgressBar (this); - mLayout->addWidget (mFileProgress); + mLayout->addWidget (mTotalProgress); - int size = static_cast (document->getContentFiles().size())+1; - if (document->isNew()) - --size; + mTotalProgress->setMinimum (0); + mTotalProgress->setMaximum (document->getData().getTotalRecords(document->getContentFiles())); + mTotalProgress->setTextVisible (true); + mTotalProgress->setValue (0); + mTotalRecords = 0; - mFileProgress->setMinimum (0); - mFileProgress->setMaximum (size); - mFileProgress->setTextVisible (true); - mFileProgress->setValue (0); + mFilesLoaded = 0; // record progress - mLayout->addWidget (mRecords = new QLabel ("Records", this)); + mLayout->addWidget (mRecordsLabel = new QLabel ("Records", this)); mRecordProgress = new QProgressBar (this); @@ -74,29 +73,32 @@ CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document) connect (mButtons, SIGNAL (rejected()), this, SLOT (cancel())); } -void CSVDoc::LoadingDocument::nextStage (const std::string& name, int totalRecords) +void CSVDoc::LoadingDocument::nextStage (const std::string& name, int fileRecords) { - mFile->setText (QString::fromUtf8 (("Loading: " + name).c_str())); + ++mFilesLoaded; + size_t numFiles = mDocument->getContentFiles().size(); - mFileProgress->setValue (mFileProgress->value()+1); + mTotalRecordsLabel->setText (QString::fromUtf8 (("Loading: "+name + +" ("+std::to_string(mFilesLoaded)+" of "+std::to_string((numFiles))+")").c_str())); + + mTotalRecords = mTotalProgress->value(); mRecordProgress->setValue (0); - mRecordProgress->setMaximum (totalRecords>0 ? totalRecords : 1); + mRecordProgress->setMaximum (fileRecords>0 ? fileRecords : 1); - mTotalRecords = totalRecords; + mRecords = fileRecords; } void CSVDoc::LoadingDocument::nextRecord (int records) { - if (records<=mTotalRecords) + if (records <= mRecords) { - mRecordProgress->setValue (records); + mTotalProgress->setValue (mTotalRecords+records); - std::ostringstream stream; + mRecordProgress->setValue(records); - stream << "Records: " << records << " of " << mTotalRecords; - - mRecords->setText (QString::fromUtf8 (stream.str().c_str())); + mRecordsLabel->setText(QString::fromStdString( + "Records: "+std::to_string(records)+" of "+std::to_string(mRecords))); } } @@ -176,12 +178,12 @@ void CSVDoc::Loader::loadingStopped (CSMDoc::Document *document, bool completed, } void CSVDoc::Loader::nextStage (CSMDoc::Document *document, const std::string& name, - int totalRecords) + int fileRecords) { std::map::iterator iter = mDocuments.find (document); if (iter!=mDocuments.end()) - iter->second->nextStage (name, totalRecords); + iter->second->nextStage (name, fileRecords); } void CSVDoc::Loader::nextRecord (CSMDoc::Document *document, int records) diff --git a/apps/opencs/view/doc/loader.hpp b/apps/opencs/view/doc/loader.hpp index e004007c99..bda442cf15 100644 --- a/apps/opencs/view/doc/loader.hpp +++ b/apps/opencs/view/doc/loader.hpp @@ -25,16 +25,18 @@ namespace CSVDoc Q_OBJECT CSMDoc::Document *mDocument; - QLabel *mFile; - QLabel *mRecords; - QProgressBar *mFileProgress; + QLabel *mTotalRecordsLabel; + QLabel *mRecordsLabel; + QProgressBar *mTotalProgress; QProgressBar *mRecordProgress; bool mAborted; QDialogButtonBox *mButtons; QLabel *mError; QListWidget *mMessages; QVBoxLayout *mLayout; + int mRecords; int mTotalRecords; + int mFilesLoaded; private: From a967418a5a9468d0b994b55b354bda70ceed9886 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 6 Dec 2015 19:50:26 +1100 Subject: [PATCH 14/29] Fix Preview and Unpaged World subviews not rendering. Not sure if this is related to a later version of Ogre being used. --- apps/opencs/view/render/scenewidget.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/opencs/view/render/scenewidget.cpp b/apps/opencs/view/render/scenewidget.cpp index 55cf039fcd..832ccc70e1 100644 --- a/apps/opencs/view/render/scenewidget.cpp +++ b/apps/opencs/view/render/scenewidget.cpp @@ -74,6 +74,8 @@ namespace CSVRender /// \todo make shortcut configurable QShortcut *focusToolbar = new QShortcut (Qt::Key_T, this, 0, 0, Qt::WidgetWithChildrenShortcut); connect (focusToolbar, SIGNAL (activated()), this, SIGNAL (focusToolbarRequest())); + + updateOgreWindow(); } CSVWidget::SceneToolMode *SceneWidget::makeLightingSelector (CSVWidget::SceneToolbar *parent) From 68e16b6cee0f533027e1d8f1293b8a5582ac5a68 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sun, 6 Dec 2015 21:19:06 +1100 Subject: [PATCH 15/29] Convert RefNum index map to use find(). --- apps/opencs/model/world/data.hpp | 2 +- apps/opencs/model/world/refcollection.cpp | 15 +++++---------- apps/opencs/model/world/refcollection.hpp | 2 +- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index d59b836070..88a431fb74 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -104,7 +104,7 @@ namespace CSMWorld const ESM::Dialogue *mDialogue; // last loaded dialogue bool mBase; bool mProject; - std::map > mRefLoadCache; + std::map > mRefLoadCache; int mReaderIndex; std::vector > mReaders; diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 117aeb0121..f811131a19 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -12,7 +12,7 @@ #include "record.hpp" void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base, - std::map& cache, CSMDoc::Messages& messages) + std::map& cache, CSMDoc::Messages& messages) { Record cell = mCells.getRecord (cellIndex); @@ -71,13 +71,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool else ref.mCell = cell2.mId; - // ignore content file number - std::map::iterator iter = cache.begin(); - for (; iter != cache.end(); ++iter) - { - if (ref.mRefNum.mIndex == iter->first.mIndex) - break; - } + std::map::iterator iter = cache.find (ref.mRefNum.mIndex); if (isDeleted) { @@ -114,11 +108,12 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool 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)); - - cache.insert (std::make_pair (ref.mRefNum, ref.mId)); } else { diff --git a/apps/opencs/model/world/refcollection.hpp b/apps/opencs/model/world/refcollection.hpp index d031398d3f..42bb363570 100644 --- a/apps/opencs/model/world/refcollection.hpp +++ b/apps/opencs/model/world/refcollection.hpp @@ -27,7 +27,7 @@ namespace CSMWorld {} void load (ESM::ESMReader& reader, int cellIndex, bool base, - std::map& cache, CSMDoc::Messages& messages); + std::map& cache, CSMDoc::Messages& messages); ///< Load a sequence of references. std::string getNewId(); From 306bfcbdf21a0292775b644e57a0c1f8606d7226 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Tue, 8 Dec 2015 00:27:20 +1100 Subject: [PATCH 16/29] Fix exception when deleting a record while dialogue subview for that record is open. --- apps/opencs/model/world/idtable.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index ec666b3dfc..ec492a91ff 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -163,7 +163,11 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin, ///This method can return only indexes to the top level table cells QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const { - return index(mIdCollection->getIndex (id), column); + int idx = mIdCollection->searchId (id); + if (idx != -1) + return index(mIdCollection->getIndex (id), column); + else + return QModelIndex(); } void CSMWorld::IdTable::setRecord (const std::string& id, From 0de223c637d0820a97417d84796c1bfeb1836fb8 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 11 Dec 2015 07:18:54 +1100 Subject: [PATCH 17/29] Fix bug (#3067) where content file number was being ignored while searching for already loaded cell references. - Introduced by commits 49884f54f7f00e1d4413b77eae3d6091043aa016 and 896ab44d1e919852aae03be9ecb71378f031b6f5. - Also see https://github.com/OpenMW/openmw/pull/557 --- apps/opencs/model/world/data.cpp | 29 +++++++++++++++++++++++ apps/opencs/model/world/data.hpp | 1 + apps/opencs/model/world/refcollection.cpp | 10 ++++---- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index d8916935da..64d317bd41 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -927,6 +927,35 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base mReader->setIndex(mReaderIndex++); mReader->open (path.string()); + mLoadedFiles.push_back(path.filename().string()); + + // at this point mReader->mHeader.mMaster have been populated for the file being loaded + for (size_t f = 0; f < mReader->getGameFiles().size(); ++f) + { + ESM::Header::MasterData& m = const_cast(mReader->getGameFiles().at(f)); + + int index = -1; + for (size_t i = 0; i < mLoadedFiles.size()-1; ++i) // -1 to ignore the current file + { + if (Misc::StringUtils::ciEqual(m.name, mLoadedFiles.at(i))) + { + index = static_cast(i); + break; + } + } + + if (index == -1) + { + // Tried to load a parent file that has not been loaded yet. This is bad, + // the launcher should have taken care of this. + std::string fstring = "File " + mReader->getName() + " asks for parent file " + m.name + + ", but it has not been loaded yet. Please check your load order."; + mReader->fail(fstring); + } + + m.index = index; + } + mBase = base; mProject = project; diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 88a431fb74..d1c0f1f543 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -106,6 +106,7 @@ namespace CSMWorld bool mProject; std::map > mRefLoadCache; int mReaderIndex; + std::vector mLoadedFiles; std::vector > mReaders; diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index f811131a19..3542a30bd0 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -71,7 +71,10 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool else ref.mCell = cell2.mId; - std::map::iterator iter = cache.find (ref.mRefNum.mIndex); + unsigned int refNum = (ref.mRefNum.mIndex & 0x00ffffff) | + (ref.mRefNum.hasContentFile() ? ref.mRefNum.mContentFile : 0xff) << 24; + + std::map::iterator iter = cache.find(refNum); if (isDeleted) { @@ -106,11 +109,10 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool // new reference ref.mId = getNewId(); + cache.insert(std::make_pair(refNum, ref.mId)); + 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)); From 86945d19128468ad987b5bf4332602d613d25d9f Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 11 Dec 2015 21:00:13 +1100 Subject: [PATCH 18/29] Convert the CellRef record index lookup maps to use integer keys rather than strings. - Morrowind load over 300,000 references, so even small inefficiencies add up to longer loading times. - std::map is used, but should try others, std::unordered_map or even std::vector --- apps/opencs/model/world/data.hpp | 2 +- apps/opencs/model/world/ref.cpp | 2 + apps/opencs/model/world/ref.hpp | 2 + apps/opencs/model/world/refcollection.cpp | 178 +++++++++++++++++++++- apps/opencs/model/world/refcollection.hpp | 35 ++++- 5 files changed, 211 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index d1c0f1f543..47046d9de3 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -104,7 +104,7 @@ namespace CSMWorld const ESM::Dialogue *mDialogue; // last loaded dialogue bool mBase; bool mProject; - std::map > mRefLoadCache; + std::map > mRefLoadCache; int mReaderIndex; std::vector mLoadedFiles; diff --git a/apps/opencs/model/world/ref.cpp b/apps/opencs/model/world/ref.cpp index 8c6f1d8365..dbd9ae93d4 100644 --- a/apps/opencs/model/world/ref.cpp +++ b/apps/opencs/model/world/ref.cpp @@ -3,6 +3,7 @@ #include CSMWorld::CellRef::CellRef() + : mIdNum(0) { mId.clear(); mCell.clear(); @@ -22,6 +23,7 @@ CSMWorld::CellRef& CSMWorld::CellRef::operator= (CSMWorld::CellRef&& other) if (this != &other) { ESM::CellRef::operator= (other); + mIdNum = other.mIdNum; mId = std::move(other.mId); mCell = std::move(other.mCell); mOriginalCell = std::move(other.mOriginalCell); diff --git a/apps/opencs/model/world/ref.hpp b/apps/opencs/model/world/ref.hpp index c439e7ca5d..f9af5705f2 100644 --- a/apps/opencs/model/world/ref.hpp +++ b/apps/opencs/model/world/ref.hpp @@ -10,6 +10,8 @@ namespace CSMWorld /// \brief Wrapper for CellRef sub record struct CellRef : public ESM::CellRef { + unsigned int mIdNum; + std::string mId; std::string mCell; std::string mOriginalCell; diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 3542a30bd0..26ec507db2 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -11,8 +11,37 @@ #include "universalid.hpp" #include "record.hpp" +namespace CSMWorld +{ + template<> + void Collection >::removeRows (int index, int count) + { + mRecords.erase(mRecords.begin()+index, mRecords.begin()+index+count); + + // index map is updated in RefCollection::removeRows() + } + + template<> + void Collection >::insertRecord (std::unique_ptr record, int index, + UniversalId::Type type) + { + int size = static_cast(mRecords.size()); + if (index < 0 || index > size) + throw std::runtime_error("index out of range"); + + std::unique_ptr > record2(static_cast*>(record.release())); + + if (index == size) + mRecords.push_back(std::move(record2)); + else + mRecords.insert(mRecords.begin()+index, std::move(record2)); + + // index map is updated in RefCollection::insertRecord() + } +} + void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base, - std::map& cache, CSMDoc::Messages& messages) + std::map& cache, CSMDoc::Messages& messages) { Record cell = mCells.getRecord (cellIndex); @@ -74,7 +103,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool unsigned int refNum = (ref.mRefNum.mIndex & 0x00ffffff) | (ref.mRefNum.hasContentFile() ? ref.mRefNum.mContentFile : 0xff) << 24; - std::map::iterator iter = cache.find(refNum); + std::map::iterator iter = cache.find(refNum); if (isDeleted) { @@ -83,7 +112,11 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell, mCells.getId (cellIndex)); - messages.add (id, "Attempt to delete a non-existing reference"); + messages.add (id, "Attempt to delete a non-existing reference - RefNum index " + + std::to_string(ref.mRefNum.mIndex) + ", refID " + ref.mRefID + ", content file index " + + std::to_string(ref.mRefNum.mContentFile), + /*hint*/"", + CSMDoc::Message::Severity_Warning); continue; } @@ -107,9 +140,10 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool if (iter==cache.end()) { // new reference + ref.mIdNum = mNextId; // FIXME: fragile ref.mId = getNewId(); - cache.insert(std::make_pair(refNum, ref.mId)); + cache.insert(std::make_pair(refNum, ref.mIdNum)); std::unique_ptr > record(new Record); record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; @@ -120,9 +154,27 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool else { // old reference -> merge - ref.mId = iter->second; + int index = getIndex(iter->second); +#if 0 + // ref.mRefNum.mIndex : the key + // iter->second : previously cached idNum for the key + // index : position of the record for that idNum + // getRecord(index).get() : record in the index position + assert(iter->second != getRecord(index).get().mIdNum); // sanity check - int index = getIndex (ref.mId); + // check if the plugin used the same RefNum index for a different record + if (ref.mRefID != getRecord(index).get().mRefID) + { + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Cell, mCells.getId(cellIndex)); + messages.add(id, + "RefNum renamed from RefID \"" + getRecord(index).get().mRefID + "\" to \"" + + ref.mRefID + "\" (RefNum index " + std::to_string(ref.mRefNum.mIndex) + ")", + /*hint*/"", + CSMDoc::Message::Severity_Info); + } +#endif + ref.mId = getRecord(index).get().mId; + ref.mIdNum = extractIdNum(ref.mId); std::unique_ptr > record(new Record(getRecord(index))); record->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_Modified; @@ -139,3 +191,117 @@ std::string CSMWorld::RefCollection::getNewId() stream << "ref#" << mNextId++; return stream.str(); } + +unsigned int CSMWorld::RefCollection::extractIdNum (const std::string& id) const +{ + std::string::size_type separator = id.find_last_of('#'); + + if (separator == std::string::npos) + throw std::runtime_error("invalid ref ID: " + id); + + return static_cast(std::stoi(id.substr(separator+1))); +} + +int CSMWorld::RefCollection::getIndex (unsigned int id) const +{ + int index = searchId(id); + + if (index == -1) + throw std::runtime_error("invalid RefNum: " + std::to_string(id)); + + return index; +} + +int CSMWorld::RefCollection::searchId (unsigned int id) const +{ + std::map::const_iterator iter = mRefIndex.find(id); + + if (iter == mRefIndex.end()) + return -1; + + return iter->second; +} + +void CSMWorld::RefCollection::removeRows (int index, int count) +{ + Collection >::removeRows(index, count); // erase records only + + std::map::iterator iter = mRefIndex.begin(); + while (iter != mRefIndex.end()) + { + if (iter->second>=index) + { + if (iter->second >= index+count) + { + iter->second -= count; + ++iter; + } + else + mRefIndex.erase(iter++); + } + else + ++iter; + } +} + +void CSMWorld::RefCollection::appendBlankRecord (const std::string& id, UniversalId::Type type) +{ + std::unique_ptr > record2(new Record); + + record2->mState = Record::State_ModifiedOnly; + record2->mModified.blank(); + + record2->get().mId = id; + record2->get().mIdNum = extractIdNum(id); + + Collection >::appendRecord(std::move(record2)); +} + +void CSMWorld::RefCollection::cloneRecord (const std::string& origin, + const std::string& destination, + const UniversalId::Type type) +{ + std::unique_ptr > copy(new Record); + + copy->mModified = getRecord(origin).get(); + copy->mState = RecordBase::State_ModifiedOnly; + + copy->get().mId = destination; + copy->get().mIdNum = extractIdNum(destination); + + insertRecord(std::move(copy), getAppendIndex(destination, type)); // call RefCollection::insertRecord() +} + +int CSMWorld::RefCollection::searchId (const std::string& id) const +{ + return searchId(extractIdNum(id)); +} + +void CSMWorld::RefCollection::appendRecord (std::unique_ptr record, UniversalId::Type type) +{ + int index = getAppendIndex(/*id*/"", type); // for CellRef records id is ignored + + mRefIndex.insert(std::make_pair(static_cast*>(record.get())->get().mIdNum, index)); + + Collection >::insertRecord(std::move(record), index, type); // add records only +} + +void CSMWorld::RefCollection::insertRecord (std::unique_ptr record, int index, + UniversalId::Type type) +{ + int size = getAppendIndex(/*id*/"", type); // for CellRef records id is ignored + unsigned int idNum = static_cast*>(record.get())->get().mIdNum; + + Collection >::insertRecord(std::move(record), index, type); // add records only + + if (index < size-1) + { + for (std::map::iterator iter(mRefIndex.begin()); iter != mRefIndex.end(); ++iter) + { + if (iter->second >= index) + ++(iter->second); + } + } + + mRefIndex.insert(std::make_pair(idNum, index)); +} diff --git a/apps/opencs/model/world/refcollection.hpp b/apps/opencs/model/world/refcollection.hpp index 42bb363570..6b4704bc9b 100644 --- a/apps/opencs/model/world/refcollection.hpp +++ b/apps/opencs/model/world/refcollection.hpp @@ -14,12 +14,27 @@ namespace CSMWorld struct Cell; class UniversalId; + template<> + void Collection >::removeRows (int index, int count); + + template<> + void Collection >::insertRecord (std::unique_ptr record, int index, + UniversalId::Type type); + /// \brief References in cells class RefCollection : public Collection { Collection& mCells; + std::map mRefIndex; + int mNextId; + unsigned int extractIdNum(const std::string& id) const; + + int getIndex (unsigned int id) const; + + int searchId (unsigned int id) const; + public: // MSVC needs the constructor for a class inheriting a template to be defined in header RefCollection (Collection& cells) @@ -27,10 +42,28 @@ namespace CSMWorld {} void load (ESM::ESMReader& reader, int cellIndex, bool base, - std::map& cache, CSMDoc::Messages& messages); + std::map& cache, CSMDoc::Messages& messages); ///< Load a sequence of references. std::string getNewId(); + + virtual void removeRows (int index, int count); + + virtual void appendBlankRecord (const std::string& id, + UniversalId::Type type = UniversalId::Type_None); + + virtual void cloneRecord (const std::string& origin, + const std::string& destination, + const UniversalId::Type type); + + virtual int searchId (const std::string& id) const; + + virtual void appendRecord (std::unique_ptr record, + UniversalId::Type type = UniversalId::Type_None); + + virtual void insertRecord (std::unique_ptr record, + int index, + UniversalId::Type type = UniversalId::Type_None); }; } From 47b5fa9dae72c35546fd172c9e06929c6ec39042 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 11 Dec 2015 22:18:17 +1100 Subject: [PATCH 19/29] Convert some of ostringstream << operations to std::to_sting() calls. - At least with MSVC the latter is more efficient. - There are many more, but leave them until they show up during profiling (so far only loading was profiled, and mainly cell references) --- apps/opencs/model/world/refcollection.cpp | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 26ec507db2..607e5690dd 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -1,6 +1,5 @@ #include "refcollection.hpp" -#include #include #include @@ -63,10 +62,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool // 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(); + ref.mCell = "#" + std::to_string(index.first) + " " + std::to_string(index.second); if (!base && // don't try to update base records mref.mRefNum.mIndex != 0) // MVRF tag found @@ -91,9 +87,8 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool 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 + // overwrite + ref.mCell = "#" + std::to_string(mref.mTarget[0]) + " " + std::to_string(mref.mTarget[1]); } } } @@ -187,9 +182,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool std::string CSMWorld::RefCollection::getNewId() { - std::ostringstream stream; - stream << "ref#" << mNextId++; - return stream.str(); + return "ref#" + std::to_string(mNextId++); } unsigned int CSMWorld::RefCollection::extractIdNum (const std::string& id) const From de5ce7059e5916f29be7ebce16f47e7fe4986335 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 11 Dec 2015 22:20:21 +1100 Subject: [PATCH 20/29] Use load message system for moved ref target cell error logging. --- apps/opencs/model/world/refcollection.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 607e5690dd..0f894dc876 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -1,7 +1,5 @@ #include "refcollection.hpp" -#include - #include #include @@ -82,10 +80,13 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool // 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; + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell, mCells.getId (cellIndex)); + messages.add (id, "The Position of moved ref " + ref.mRefID + " (#" + + std::to_string(index.first) + " " + std::to_string(index.second) + + ") does not match the target cell (#" + + std::to_string(mref.mTarget[0]) + " " + std::to_string(mref.mTarget[1]) + ")", + /*hint*/"", + CSMDoc::Message::Severity_Warning); // overwrite ref.mCell = "#" + std::to_string(mref.mTarget[0]) + " " + std::to_string(mref.mTarget[1]); From f6d6138f4659ee4a016070abd53b6eb1103fc372 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 11 Dec 2015 22:39:03 +1100 Subject: [PATCH 21/29] Minor formatting change. --- apps/opencs/model/world/refcollection.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 0f894dc876..25c934c7f0 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -40,7 +40,7 @@ namespace CSMWorld void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base, std::map& cache, CSMDoc::Messages& messages) { - Record cell = mCells.getRecord (cellIndex); + Record cell = mCells.getRecord(cellIndex); Cell& cell2 = base ? cell.mBase : cell.mModified; @@ -80,8 +80,8 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool // message if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1]) { - CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell, mCells.getId (cellIndex)); - messages.add (id, "The Position of moved ref " + ref.mRefID + " (#" + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Cell, mCells.getId(cellIndex)); + messages.add(id, "The Position of moved ref " + ref.mRefID + " (#" + std::to_string(index.first) + " " + std::to_string(index.second) + ") does not match the target cell (#" + std::to_string(mref.mTarget[0]) + " " + std::to_string(mref.mTarget[1]) + ")", @@ -103,12 +103,10 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool if (isDeleted) { - if (iter==cache.end()) + if (iter == cache.end()) { - CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell, - mCells.getId (cellIndex)); - - messages.add (id, "Attempt to delete a non-existing reference - RefNum index " + CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_Cell, mCells.getId(cellIndex)); + messages.add(id, "Attempt to delete a non-existing reference - RefNum index " + std::to_string(ref.mRefNum.mIndex) + ", refID " + ref.mRefID + ", content file index " + std::to_string(ref.mRefNum.mContentFile), /*hint*/"", @@ -116,12 +114,12 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool continue; } - int index = getIndex (iter->second); + int index = getIndex(iter->second); if (base) { - removeRows (index, 1); - cache.erase (iter); + removeRows(index, 1); + cache.erase(iter); } else { @@ -133,7 +131,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool continue; } - if (iter==cache.end()) + if (iter == cache.end()) { // new reference ref.mIdNum = mNextId; // FIXME: fragile From 477e0ee912952a1e3b5c23534af826c0e2d9f636 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 18 Dec 2015 19:39:40 +1100 Subject: [PATCH 22/29] Convert std::to_string() calls to snprintf() for cell references. - Profiling indicates snprintf() is more efficient when using MSVC (not tested with linux) --- apps/opencs/model/world/refcollection.cpp | 16 ++++++++++-- libs/platform/strings.h | 31 +++++++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 25c934c7f0..5d128a3576 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "ref.hpp" #include "cell.hpp" #include "universalid.hpp" @@ -60,7 +62,12 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool // ignoring moved references sub-record; instead calculate cell from coordinates std::pair index = ref.getCellIndex(); - ref.mCell = "#" + std::to_string(index.first) + " " + std::to_string(index.second); + char buf[100]; + int res = snprintf(buf, 100, "#%d %d", index.first, index.second); + if (res > 0 && res < 100) + ref.mCell = std::string(buf); + else + throw std::runtime_error("getNewId possible buffer overflow"); if (!base && // don't try to update base records mref.mRefNum.mIndex != 0) // MVRF tag found @@ -181,7 +188,12 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool std::string CSMWorld::RefCollection::getNewId() { - return "ref#" + std::to_string(mNextId++); + char buf[100]; + int res = snprintf(buf, 100, "ref#%d", mNextId++); + if (res > 0 && res < 100) + return std::string(buf); + else + throw std::runtime_error("getNewId possible buffer overflow"); } unsigned int CSMWorld::RefCollection::extractIdNum (const std::string& id) const diff --git a/libs/platform/strings.h b/libs/platform/strings.h index 305705044e..027f903330 100644 --- a/libs/platform/strings.h +++ b/libs/platform/strings.h @@ -10,11 +10,38 @@ # pragma warning(disable: 4996) # define strcasecmp stricmp # if (_MSC_VER < 1900) -# define snprintf _snprintf +# include +# include +# define snprintf c99_snprintf +# define vsnprintf c99_vsnprintf +/* see http://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010 */ +inline int c99_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) +{ + int count = -1; + + if (size != 0) + count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + + return count; +} + +inline int c99_snprintf(char *outBuf, size_t size, const char *format, ...) +{ + int count; + va_list ap; + + va_start(ap, format); + count = c99_vsnprintf(outBuf, size, format, ap); + va_end(ap); + + return count; +} # endif #else # warning "Unable to determine your compiler, you should probably take a look here." # include // Just take a guess -#endif +#endif #endif /* _STRINGS_WRAPPER_H */ From 76e9a0359669371bdf06936224a2f7668104f3b5 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 18 Dec 2015 19:41:19 +1100 Subject: [PATCH 23/29] More efficient implementation of an earlier fix so that id is searched only once - see commit 306bfcbdf21a0292775b644e57a0c1f8606d7226 --- apps/opencs/model/world/idtable.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index ec492a91ff..6365a78c1a 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -163,9 +163,9 @@ void CSMWorld::IdTable::cloneRecord(const std::string& origin, ///This method can return only indexes to the top level table cells QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const { - int idx = mIdCollection->searchId (id); - if (idx != -1) - return index(mIdCollection->getIndex (id), column); + int row = mIdCollection->searchId (id); + if (row != -1) + return index(row, column); else return QModelIndex(); } From 226f7b69281b84d78d6aaef2be0ce7ef03d518dd Mon Sep 17 00:00:00 2001 From: cc9cii Date: Fri, 18 Dec 2015 19:42:19 +1100 Subject: [PATCH 24/29] Suppress additional MSVC warnings after Update 1 --- CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4395df940f..e8e0a26f70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -706,7 +706,13 @@ if (WIN32) # MSVC 2015 if (MSVC_VERSION GREATER 1899) - set(WARNINGS_DISABLE ${WARNINGS_DISABLE} 5026 5027) + set(WARNINGS_DISABLE ${WARNINGS_DISABLE} + 4464 # relative include path contains '..' + 5026 # move constructor was implicitly defined as deleted + 5027 # move assignment operator was implicitly defined as deleted + 5031 # #pragma warning(pop): likely mismatch, popping warning state pushed in different file + 5032 # detected #pragma warning(push) with no corresponding #pragma warning(pop) + ) endif() foreach(d ${WARNINGS_DISABLE}) From edf4e6ff954f6ee135c0d2c1a9955f7fca90e231 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 19 Dec 2015 17:22:09 +1100 Subject: [PATCH 25/29] Fix crash when repeatedly performing delete/undo due to empty moved record. - introduced in commit 23e7e3c165bb2631f9d8eb298f86da862e91cefa --- apps/opencs/model/world/commands.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 0f6681ba17..ccb41372c2 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -144,6 +144,7 @@ void CSMWorld::RevertCommand::redo() if (state==RecordBase::State_ModifiedOnly) { + mOld = std::move(mModel.getRecord (mId).clone()); mModel.removeRows (index.row(), 1); } else @@ -179,6 +180,7 @@ void CSMWorld::DeleteCommand::redo() if (state==RecordBase::State_ModifiedOnly) { + mOld = std::move(mModel.getRecord (mId).clone()); mModel.removeRows (index.row(), 1); } else From 06f9922822bf5a076894bce44bde37234d7ccee1 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 19 Dec 2015 17:30:55 +1100 Subject: [PATCH 26/29] Performance improvements for loading Info records. - The order of info records with the same topic are maintained in Collection::mRecords - The index lookup data structure are not ordered. The topic string is hashed. The infos for the topic are simply placed in a vector. - The index values for appending or inserting a record takes prev/next values (if exist) - FIXME: prev/next values are not adjusted for adding or removing records - FIXME: undo after reordering does not reset the modified flag --- CMakeLists.txt | 1 + apps/opencs/CMakeLists.txt | 1 + apps/opencs/model/world/collection.hpp | 8 - apps/opencs/model/world/collectionbase.cpp | 5 + apps/opencs/model/world/collectionbase.hpp | 6 + apps/opencs/model/world/idtable.cpp | 8 +- apps/opencs/model/world/infocollection.cpp | 345 ++++++++++---- apps/opencs/model/world/infocollection.hpp | 76 ++- extern/murmurhash/CMakeLists.txt | 17 + extern/murmurhash/MurmurHash2.cpp | 522 +++++++++++++++++++++ extern/murmurhash/MurmurHash2.h | 38 ++ 11 files changed, 933 insertions(+), 94 deletions(-) create mode 100644 extern/murmurhash/CMakeLists.txt create mode 100644 extern/murmurhash/MurmurHash2.cpp create mode 100644 extern/murmurhash/MurmurHash2.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e8e0a26f70..ff5191c462 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -576,6 +576,7 @@ add_subdirectory (extern/shiny) add_subdirectory (extern/ogre-ffmpeg-videoplayer) add_subdirectory (extern/oics) add_subdirectory (extern/sdl4ogre) +add_subdirectory (extern/murmurhash) # Components add_subdirectory (components) diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index 30c78e710b..7f3709b8a9 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -211,6 +211,7 @@ target_link_libraries(openmw-cs ${OGRE_Overlay_LIBRARIES} ${OGRE_STATIC_PLUGINS} ${SHINY_LIBRARIES} + ${MURMURHASH_LIBRARIES} ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 475a262632..594707cf2b 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -60,8 +60,6 @@ namespace CSMWorld protected: - const std::map& getIdMap() const; - const std::vector > >& getRecords() const; bool reorderRowsImp (int baseIndex, const std::vector& newOrder); @@ -159,12 +157,6 @@ namespace CSMWorld NestableColumn *getNestableColumn (int column) const; }; - template - const std::map& Collection::getIdMap() const - { - return mIndex; - } - template const std::vector > >& Collection::getRecords() const { diff --git a/apps/opencs/model/world/collectionbase.cpp b/apps/opencs/model/world/collectionbase.cpp index 6134dc1727..f20fc643e2 100644 --- a/apps/opencs/model/world/collectionbase.cpp +++ b/apps/opencs/model/world/collectionbase.cpp @@ -8,6 +8,11 @@ CSMWorld::CollectionBase::CollectionBase() {} CSMWorld::CollectionBase::~CollectionBase() {} +int CSMWorld::CollectionBase::getInsertIndex (const std::string& id, UniversalId::Type type, RecordBase *record) const +{ + return getAppendIndex(id, type); +} + int CSMWorld::CollectionBase::searchColumnIndex (Columns::ColumnId id) const { int columns = getColumns(); diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index fa0399e49e..ac047de27d 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -98,6 +98,12 @@ namespace CSMWorld /// /// \return Success? + virtual int getInsertIndex (const std::string& id, + UniversalId::Type type = UniversalId::Type_None, + RecordBase *record = nullptr) const; + ///< Works like getAppendIndex unless an overloaded method uses the record pointer + /// to get additional info about the record that results in an alternative index. + int searchColumnIndex (Columns::ColumnId id) const; ///< Return index of column with the given \a id. If no such column exists, -1 is returned. diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 6365a78c1a..bb3bb18378 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -177,7 +177,13 @@ void CSMWorld::IdTable::setRecord (const std::string& id, if (index==-1) { - int index2 = mIdCollection->getAppendIndex (id, type); + // For info records, appendRecord may use a different index than the one returned by + // getAppendIndex (because of prev/next links). This can result in the display not + // updating correctly after an undo + // + // Use an alternative method to get the correct index. For non-Info records the + // record pointer is ignored and internally calls getAppendIndex. + int index2 = mIdCollection->getInsertIndex (id, type, record.get()); beginInsertRows (QModelIndex(), index2, index2); diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index c331a2d730..7b7c274a09 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -8,42 +8,79 @@ #include +namespace CSMWorld +{ + template<> + void Collection >::removeRows (int index, int count) + { + mRecords.erase(mRecords.begin()+index, mRecords.begin()+index+count); + + // index map is updated in InfoCollection::removeRows() + } + + template<> + void Collection >::insertRecord (std::unique_ptr record, + int index, UniversalId::Type type) + { + int size = static_cast(mRecords.size()); + if (index < 0 || index > size) + throw std::runtime_error("index out of range"); + + std::unique_ptr > record2(static_cast*>(record.release())); + + if (index == size) + mRecords.push_back(std::move(record2)); + else + mRecords.insert(mRecords.begin()+index, std::move(record2)); + + // index map is updated in InfoCollection::insertRecord() + } + + template<> + bool Collection >::reorderRowsImp (int baseIndex, + const std::vector& newOrder) + { + if (!newOrder.empty()) + { + int size = static_cast(newOrder.size()); + + // check that all indices are present + std::vector test(newOrder); + std::sort(test.begin(), test.end()); + if (*test.begin() != 0 || *--test.end() != size-1) + return false; + + // reorder records + std::vector > > buffer(size); + + // FIXME: BUG: undo does not remove modified flag + for (int i = 0; i < size; ++i) + { + buffer[newOrder[i]] = std::move(mRecords[baseIndex+i]); + buffer[newOrder[i]]->setModified(buffer[newOrder[i]]->get()); + } + + std::move(buffer.begin(), buffer.end(), mRecords.begin()+baseIndex); + + // index map is updated in InfoCollection::reorderRows() + } + + return true; + } +} + void CSMWorld::InfoCollection::load (const Info& record, bool base) { - int index = searchId (record.mId); + int index = searchId(record.mId); - if (index==-1) + if (index == -1) { // new record std::unique_ptr > record2(new Record); record2->mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; (base ? record2->mBase : record2->mModified) = record; - int index2 = -1; - - std::string topic = Misc::StringUtils::lowerCase (record2->get().mTopicId); - - if (!record2->get().mPrev.empty()) - { - index2 = getInfoIndex (record2->get().mPrev, topic); - - if (index2!=-1) - ++index2; - } - - if (index2==-1 && !record2->get().mNext.empty()) - { - index2 = getInfoIndex (record2->get().mNext, topic); - } - - if (index2==-1) - { - Range range = getTopicRange (topic); - - index2 = std::distance (getRecords().begin(), range.second); - } - - insertRecord (std::move(record2), index2); + appendRecord(std::move(record2)); } else { @@ -61,30 +98,74 @@ void CSMWorld::InfoCollection::load (const Info& record, bool base) int CSMWorld::InfoCollection::getInfoIndex (const std::string& id, const std::string& topic) const { - std::string fullId = Misc::StringUtils::lowerCase (topic) + "#" + id; + // find the topic first + std::map > >::const_iterator iter + = mInfoIndex.find(StringHash(std::make_shared(Misc::StringUtils::lowerCase(topic)))); - std::pair range = getTopicRange (topic); + if (iter == mInfoIndex.end()) + return -1; - for (; range.first!=range.second; ++range.first) - if (Misc::StringUtils::ciEqual((*range.first).get()->get().mId, fullId)) - return std::distance (getRecords().begin(), range.first); + // brute force loop + for (std::vector >::const_iterator it = iter->second.begin(); + it != iter->second.end(); ++it) + { + if (Misc::StringUtils::ciEqual(it->first, id)) + return it->second; + } return -1; } -int CSMWorld::InfoCollection::getAppendIndex (const std::string& id, UniversalId::Type type) const +// Calling insertRecord() using index from getInsertIndex() needs to take into account of +// prev/next records; an example is deleting a record then undo +int CSMWorld::InfoCollection::getInsertIndex (const std::string& id, + UniversalId::Type type, RecordBase *record) const { - std::string::size_type separator = id.find_last_of ('#'); + if (record == nullptr) + { + std::string::size_type separator = id.find_last_of('#'); - if (separator==std::string::npos) - throw std::runtime_error ("invalid info ID: " + id); + if (separator == std::string::npos) + throw std::runtime_error("invalid info ID: " + id); - std::pair range = getTopicRange (id.substr (0, separator)); + std::pair range = getTopicRange(id.substr(0, separator)); - if (range.first==range.second) - return Collection >::getAppendIndex (id, type); + if (range.first == range.second) + return Collection >::getAppendIndex(id, type); - return std::distance (getRecords().begin(), range.second); + return std::distance(getRecords().begin(), range.second); + } + + int index = -1; + + const Info& info = static_cast*>(record)->get(); + std::string topic = info.mTopicId; + + // if the record has a prev, find its index value + if (!info.mPrev.empty()) + { + index = getInfoIndex(info.mPrev, topic); + + if (index != -1) + ++index; // if prev exists, set current index to one above prev + } + + // if prev doesn't exist or not found and the record has a next, find its index value + if (index == -1 && !info.mNext.empty()) + { + // if next exists, use its index as the current index + index = getInfoIndex(info.mNext, topic); + } + + // if next doesn't exist or not found (i.e. neither exist yet) then start a new one + if (index == -1) + { + Range range = getTopicRange(topic); // getTopicRange converts topic to lower case first + + index = std::distance(getRecords().begin(), range.second); + } + + return index; } bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector& newOrder) @@ -101,7 +182,23 @@ bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector >::reorderRowsImp(baseIndex, newOrder)) + return false; + + // adjust index + int size = static_cast(newOrder.size()); + for (std::map > >::iterator iter + = mInfoIndex.begin(); iter != mInfoIndex.end(); ++iter) + { + for (std::vector >::iterator it = iter->second.begin(); + it != iter->second.end(); ++it) + { + if (it->second >= baseIndex && it->second < baseIndex+size) + it->second = newOrder.at(it->second-baseIndex)+baseIndex; + } + } + + return true; } void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue) @@ -116,7 +213,7 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES { int index = searchId (id); - if (index==-1) + if (index == -1) { // deleting a record that does not exist // ignore it for now @@ -144,74 +241,54 @@ void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ES CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const std::string& topic) const { - std::string topic2 = Misc::StringUtils::lowerCase (topic); + std::string lowerTopic = Misc::StringUtils::lowerCase (topic); - std::map::const_iterator iter = getIdMap().lower_bound (topic2); + // find the topic + std::map > >::const_iterator iter + = mInfoIndex.find(StringHash(std::make_shared(lowerTopic))); - // Skip invalid records: The beginning of a topic string could be identical to another topic - // string. - for (; iter!=getIdMap().end(); ++iter) - { - std::string testTopicId = - Misc::StringUtils::lowerCase (getRecord (iter->second).get().mTopicId); - - if (testTopicId==topic2) - break; - - std::size_t size = topic2.size(); - - if (testTopicId.size()second; - - while (begin != getRecords().begin()) + // topic found, find the starting index + int low = INT_MAX; + for (std::vector >::const_iterator it = iter->second.begin(); + it != iter->second.end(); ++it) { - if (!Misc::StringUtils::ciEqual((*begin)->get().mTopicId, topic2)) - { - // we've gone one too far, go back - ++begin; - break; - } - --begin; + low = std::min(low, it->second); } - // Find end - RecordConstIterator end = begin; + RecordConstIterator begin = getRecords().begin() + low; - for (; end!=getRecords().end(); ++end) - if (!Misc::StringUtils::ciEqual((*end)->get().mTopicId, topic2)) - break; + // Find end (one past the range) + RecordConstIterator end = begin + iter->second.size(); + if (end != getRecords().end()) + ++end; return Range (begin, end); } void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId) { - std::string id = Misc::StringUtils::lowerCase(dialogueId); std::vector erasedRecords; - std::map::const_iterator current = getIdMap().lower_bound(id); - std::map::const_iterator end = getIdMap().end(); - for (; current != end; ++current) + Range range = getTopicRange(dialogueId); // getTopicRange converts dialogueId to lower case first + + for (; range.first != range.second; ++range.first) { - const Record& record = getRecord(current->second); + const Record& record = **range.first; if (Misc::StringUtils::ciEqual(dialogueId, record.get().mTopicId)) { if (record.mState == RecordBase::State_ModifiedOnly) { - erasedRecords.push_back(current->second); + erasedRecords.push_back(range.first - getRecords().begin()); } else { std::unique_ptr > record2(new Record(record)); record2->mState = RecordBase::State_Deleted; - setRecord(current->second, std::move(record2)); + setRecord(range.first - getRecords().begin(), std::move(record2)); } } else @@ -226,3 +303,105 @@ void CSMWorld::InfoCollection::removeDialogueInfos(const std::string& dialogueId erasedRecords.pop_back(); } } + +// FIXME: removing a record should adjust prev/next and mark those records as modified +// accordingly (also consider undo) +void CSMWorld::InfoCollection::removeRows (int index, int count) +{ + Collection >::removeRows(index, count); // erase records only + + for (std::map > >::iterator iter + = mInfoIndex.begin(); iter != mInfoIndex.end();) + { + for (std::vector >::iterator it = iter->second.begin(); + it != iter->second.end();) + { + if (it->second >= index) + { + if (it->second >= index+count) + { + it->second -= count; + ++it; + } + else + iter->second.erase(it); + } + else + ++it; + } + + // check for an empty vector + if (iter->second.empty()) + mInfoIndex.erase(iter++); + else + ++iter; + } +} + +void CSMWorld::InfoCollection::appendBlankRecord (const std::string& id, UniversalId::Type type) +{ + std::unique_ptr > record2(new Record); + + record2->mState = Record::State_ModifiedOnly; + record2->mModified.blank(); + + record2->get().mId = id; + + insertRecord(std::move(record2), getInsertIndex(id, type, nullptr), type); // call InfoCollection::insertRecord() +} + +int CSMWorld::InfoCollection::searchId (const std::string& id) const +{ + std::string::size_type separator = id.find_last_of('#'); + + if (separator == std::string::npos) + throw std::runtime_error("invalid info ID: " + id); + + return getInfoIndex(id.substr(separator+1), id.substr(0, separator)); +} + +void CSMWorld::InfoCollection::appendRecord (std::unique_ptr record, UniversalId::Type type) +{ + int index = getInsertIndex(static_cast*>(record.get())->get().mId, type, record.get()); + + insertRecord(std::move(record), index, type); +} + +void CSMWorld::InfoCollection::insertRecord (std::unique_ptr record, int index, + UniversalId::Type type) +{ + int size = static_cast(getRecords().size()); + + std::string id = static_cast*>(record.get())->get().mId; + std::string::size_type separator = id.find_last_of('#'); + + if (separator == std::string::npos) + throw std::runtime_error("invalid info ID: " + id); + + Collection >::insertRecord(std::move(record), index, type); // add records only + + // adjust index + if (index < size-1) + { + for (std::map > >::iterator iter + = mInfoIndex.begin(); iter != mInfoIndex.end(); ++iter) + { + for (std::vector >::iterator it = iter->second.begin(); + it != iter->second.end(); ++it) + { + if (it->second >= index) + ++(it->second); + } + } + } + + // get iterator for existing topic or a new topic + std::string lowerId = Misc::StringUtils::lowerCase(id); + std::pair > >::iterator, bool> res + = mInfoIndex.insert( + std::make_pair(StringHash(std::make_shared(lowerId.substr(0, separator))), + std::vector >())); // empty vector + + // insert info and index + res.first->second.push_back(std::make_pair(lowerId.substr(separator+1), index)); +} diff --git a/apps/opencs/model/world/infocollection.hpp b/apps/opencs/model/world/infocollection.hpp index da389763ca..73fbaf9ef3 100644 --- a/apps/opencs/model/world/infocollection.hpp +++ b/apps/opencs/model/world/infocollection.hpp @@ -1,6 +1,8 @@ #ifndef CSM_WOLRD_INFOCOLLECTION_H #define CSM_WOLRD_INFOCOLLECTION_H +#include + #include "collection.hpp" #include "info.hpp" @@ -11,6 +13,48 @@ namespace ESM namespace CSMWorld { + struct StringHash + { + uint64_t mHash; + std::shared_ptr mString; + + StringHash (std::shared_ptr str) : mString(str) + { + mHash = MurmurHash64A(str->c_str(), str->size(), /*seed*/1); + } + }; +} + +namespace std +{ + template<> struct less + { + bool operator() (const CSMWorld::StringHash& lhs, const CSMWorld::StringHash& rhs) const + { + if (lhs.mHash < rhs.mHash) + return true; + + if (lhs.mHash > rhs.mHash) + return false; + + return *lhs.mString < *rhs.mString; + } + }; +} + +namespace CSMWorld +{ + template<> + void Collection >::removeRows (int index, int count); + + template<> + void Collection >::insertRecord (std::unique_ptr record, + int index, UniversalId::Type type); + + template<> + bool Collection >::reorderRowsImp (int baseIndex, + const std::vector& newOrder); + class InfoCollection : public Collection > { public: @@ -20,18 +64,32 @@ namespace CSMWorld private: + // The general strategy is to keep the records in Collection kept in order (within + // a topic group) while the index lookup maps are not ordered. It is assumed that + // each topic has a small number of infos, which allows the use of vectors for + // iterating through them without too much penalty. + // + // NOTE: hashed topic string as well as id string are stored in lower case. + std::map > > mInfoIndex; + void load (const Info& record, bool base); int getInfoIndex (const std::string& id, const std::string& topic) const; ///< Return index for record \a id or -1 (if not present; deleted records are considered) /// /// \param id info ID without topic prefix + // + /// \attention id and topic are assumed to be in lower case public: - virtual int getAppendIndex (const std::string& id, - UniversalId::Type type = UniversalId::Type_None) const; + virtual int getInsertIndex (const std::string& id, + UniversalId::Type type = UniversalId::Type_None, + RecordBase *record = nullptr) const; ///< \param type Will be ignored, unless the collection supports multiple record types + /// + /// Works like getAppendIndex unless an overloaded method uses the record pointer + /// to get additional info about the record that results in an alternative index. virtual bool reorderRows (int baseIndex, const std::vector& newOrder); ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices @@ -46,6 +104,20 @@ namespace CSMWorld /// the given topic. void removeDialogueInfos(const std::string& dialogueId); + + virtual void removeRows (int index, int count); + + virtual void appendBlankRecord (const std::string& id, + UniversalId::Type type = UniversalId::Type_None); + + virtual int searchId (const std::string& id) const; + + virtual void appendRecord (std::unique_ptr record, + UniversalId::Type type = UniversalId::Type_None); + + virtual void insertRecord (std::unique_ptr record, + int index, + UniversalId::Type type = UniversalId::Type_None); }; } diff --git a/extern/murmurhash/CMakeLists.txt b/extern/murmurhash/CMakeLists.txt new file mode 100644 index 0000000000..cd8199ff06 --- /dev/null +++ b/extern/murmurhash/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 2.8) + +# This is NOT intended as a stand-alone build system! Instead, you should include this from the main CMakeLists of your project. + +set(MURMURHASH_LIBRARY "murmurhash") + +# Sources +set(SOURCE_FILES + MurmurHash2.cpp +) + +add_library(${MURMURHASH_LIBRARY} STATIC ${SOURCE_FILES}) + +set(MURMURHASH_LIBRARIES ${MURMURHASH_LIBRARY}) + +link_directories(${CMAKE_CURRENT_BINARY_DIR}) +set(MURMURHASH_LIBRARIES ${MURMURHASH_LIBRARIES} PARENT_SCOPE) diff --git a/extern/murmurhash/MurmurHash2.cpp b/extern/murmurhash/MurmurHash2.cpp new file mode 100644 index 0000000000..d1b6f476e8 --- /dev/null +++ b/extern/murmurhash/MurmurHash2.cpp @@ -0,0 +1,522 @@ +//----------------------------------------------------------------------------- +// MurmurHash2 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. + +// Note - This code makes a few assumptions about how your machine behaves - + +// 1. We can read a 4-byte value from any address without crashing +// 2. sizeof(int) == 4 + +// And it has a few limitations - + +// 1. It will not work incrementally. +// 2. It will not produce the same results on little-endian and big-endian +// machines. + +#include "MurmurHash2.h" + +//----------------------------------------------------------------------------- +// Platform-specific functions and macros + +// Microsoft Visual Studio + +#if defined(_MSC_VER) + +#define BIG_CONSTANT(x) (x) + +// Other compilers + +#else // defined(_MSC_VER) + +#define BIG_CONSTANT(x) (x##LLU) + +#endif // !defined(_MSC_VER) + +//----------------------------------------------------------------------------- + +uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed ) +{ + // 'm' and 'r' are mixing constants generated offline. + // They're not really 'magic', they just happen to work well. + + const uint32_t m = 0x5bd1e995; + const int r = 24; + + // Initialize the hash to a 'random' value + + uint32_t h = seed ^ len; + + // Mix 4 bytes at a time into the hash + + const unsigned char * data = (const unsigned char *)key; + + while(len >= 4) + { + uint32_t k = *(uint32_t*)data; + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + data += 4; + len -= 4; + } + + // Handle the last few bytes of the input array + + switch(len) + { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + }; + + // Do a few final mixes of the hash to ensure the last few + // bytes are well-incorporated. + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} + +//----------------------------------------------------------------------------- +// MurmurHash2, 64-bit versions, by Austin Appleby + +// The same caveats as 32-bit MurmurHash2 apply here - beware of alignment +// and endian-ness issues if used across multiple platforms. + +// 64-bit hash for 64-bit platforms + +uint64_t MurmurHash64A ( const void * key, int len, uint64_t seed ) +{ + const uint64_t m = BIG_CONSTANT(0xc6a4a7935bd1e995); + const int r = 47; + + uint64_t h = seed ^ (len * m); + + const uint64_t * data = (const uint64_t *)key; + const uint64_t * end = data + (len/8); + + while(data != end) + { + uint64_t k = *data++; + + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + } + + const unsigned char * data2 = (const unsigned char*)data; + + switch(len & 7) + { + case 7: h ^= uint64_t(data2[6]) << 48; + case 6: h ^= uint64_t(data2[5]) << 40; + case 5: h ^= uint64_t(data2[4]) << 32; + case 4: h ^= uint64_t(data2[3]) << 24; + case 3: h ^= uint64_t(data2[2]) << 16; + case 2: h ^= uint64_t(data2[1]) << 8; + case 1: h ^= uint64_t(data2[0]); + h *= m; + }; + + h ^= h >> r; + h *= m; + h ^= h >> r; + + return h; +} + + +// 64-bit hash for 32-bit platforms + +uint64_t MurmurHash64B ( const void * key, int len, uint64_t seed ) +{ + const uint32_t m = 0x5bd1e995; + const int r = 24; + + uint32_t h1 = uint32_t(seed) ^ len; + uint32_t h2 = uint32_t(seed >> 32); + + const uint32_t * data = (const uint32_t *)key; + + while(len >= 8) + { + uint32_t k1 = *data++; + k1 *= m; k1 ^= k1 >> r; k1 *= m; + h1 *= m; h1 ^= k1; + len -= 4; + + uint32_t k2 = *data++; + k2 *= m; k2 ^= k2 >> r; k2 *= m; + h2 *= m; h2 ^= k2; + len -= 4; + } + + if(len >= 4) + { + uint32_t k1 = *data++; + k1 *= m; k1 ^= k1 >> r; k1 *= m; + h1 *= m; h1 ^= k1; + len -= 4; + } + + switch(len) + { + case 3: h2 ^= ((unsigned char*)data)[2] << 16; + case 2: h2 ^= ((unsigned char*)data)[1] << 8; + case 1: h2 ^= ((unsigned char*)data)[0]; + h2 *= m; + }; + + h1 ^= h2 >> 18; h1 *= m; + h2 ^= h1 >> 22; h2 *= m; + h1 ^= h2 >> 17; h1 *= m; + h2 ^= h1 >> 19; h2 *= m; + + uint64_t h = h1; + + h = (h << 32) | h2; + + return h; +} + +//----------------------------------------------------------------------------- +// MurmurHash2A, by Austin Appleby + +// This is a variant of MurmurHash2 modified to use the Merkle-Damgard +// construction. Bulk speed should be identical to Murmur2, small-key speed +// will be 10%-20% slower due to the added overhead at the end of the hash. + +// This variant fixes a minor issue where null keys were more likely to +// collide with each other than expected, and also makes the function +// more amenable to incremental implementations. + +#define mmix(h,k) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; } + +uint32_t MurmurHash2A ( const void * key, int len, uint32_t seed ) +{ + const uint32_t m = 0x5bd1e995; + const int r = 24; + uint32_t l = len; + + const unsigned char * data = (const unsigned char *)key; + + uint32_t h = seed; + + while(len >= 4) + { + uint32_t k = *(uint32_t*)data; + + mmix(h,k); + + data += 4; + len -= 4; + } + + uint32_t t = 0; + + switch(len) + { + case 3: t ^= data[2] << 16; + case 2: t ^= data[1] << 8; + case 1: t ^= data[0]; + }; + + mmix(h,t); + mmix(h,l); + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} + +//----------------------------------------------------------------------------- +// CMurmurHash2A, by Austin Appleby + +// This is a sample implementation of MurmurHash2A designed to work +// incrementally. + +// Usage - + +// CMurmurHash2A hasher +// hasher.Begin(seed); +// hasher.Add(data1,size1); +// hasher.Add(data2,size2); +// ... +// hasher.Add(dataN,sizeN); +// uint32_t hash = hasher.End() + +class CMurmurHash2A +{ +public: + + void Begin ( uint32_t seed = 0 ) + { + m_hash = seed; + m_tail = 0; + m_count = 0; + m_size = 0; + } + + void Add ( const unsigned char * data, int len ) + { + m_size += len; + + MixTail(data,len); + + while(len >= 4) + { + uint32_t k = *(uint32_t*)data; + + mmix(m_hash,k); + + data += 4; + len -= 4; + } + + MixTail(data,len); + } + + uint32_t End ( void ) + { + mmix(m_hash,m_tail); + mmix(m_hash,m_size); + + m_hash ^= m_hash >> 13; + m_hash *= m; + m_hash ^= m_hash >> 15; + + return m_hash; + } + +private: + + static const uint32_t m = 0x5bd1e995; + static const int r = 24; + + void MixTail ( const unsigned char * & data, int & len ) + { + while( len && ((len<4) || m_count) ) + { + m_tail |= (*data++) << (m_count * 8); + + m_count++; + len--; + + if(m_count == 4) + { + mmix(m_hash,m_tail); + m_tail = 0; + m_count = 0; + } + } + } + + uint32_t m_hash; + uint32_t m_tail; + uint32_t m_count; + uint32_t m_size; +}; + +//----------------------------------------------------------------------------- +// MurmurHashNeutral2, by Austin Appleby + +// Same as MurmurHash2, but endian- and alignment-neutral. +// Half the speed though, alas. + +uint32_t MurmurHashNeutral2 ( const void * key, int len, uint32_t seed ) +{ + const uint32_t m = 0x5bd1e995; + const int r = 24; + + uint32_t h = seed ^ len; + + const unsigned char * data = (const unsigned char *)key; + + while(len >= 4) + { + uint32_t k; + + k = data[0]; + k |= data[1] << 8; + k |= data[2] << 16; + k |= data[3] << 24; + + k *= m; + k ^= k >> r; + k *= m; + + h *= m; + h ^= k; + + data += 4; + len -= 4; + } + + switch(len) + { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + }; + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; +} + +//----------------------------------------------------------------------------- +// MurmurHashAligned2, by Austin Appleby + +// Same algorithm as MurmurHash2, but only does aligned reads - should be safer +// on certain platforms. + +// Performance will be lower than MurmurHash2 + +#define MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; } + + +uint32_t MurmurHashAligned2 ( const void * key, int len, uint32_t seed ) +{ + const uint32_t m = 0x5bd1e995; + const int r = 24; + + const unsigned char * data = (const unsigned char *)key; + + uint32_t h = seed ^ len; + + int align = (uint64_t)data & 3; + + if(align && (len >= 4)) + { + // Pre-load the temp registers + + uint32_t t = 0, d = 0; + + switch(align) + { + case 1: t |= data[2] << 16; + case 2: t |= data[1] << 8; + case 3: t |= data[0]; + } + + t <<= (8 * align); + + data += 4-align; + len -= 4-align; + + int sl = 8 * (4-align); + int sr = 8 * align; + + // Mix + + while(len >= 4) + { + d = *(uint32_t *)data; + t = (t >> sr) | (d << sl); + + uint32_t k = t; + + MIX(h,k,m); + + t = d; + + data += 4; + len -= 4; + } + + // Handle leftover data in temp registers + + d = 0; + + if(len >= align) + { + switch(align) + { + case 3: d |= data[2] << 16; + case 2: d |= data[1] << 8; + case 1: d |= data[0]; + } + + uint32_t k = (t >> sr) | (d << sl); + MIX(h,k,m); + + data += align; + len -= align; + + //---------- + // Handle tail bytes + + switch(len) + { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + }; + } + else + { + switch(len) + { + case 3: d |= data[2] << 16; + case 2: d |= data[1] << 8; + case 1: d |= data[0]; + case 0: h ^= (t >> sr) | (d << sl); + h *= m; + } + } + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; + } + else + { + while(len >= 4) + { + uint32_t k = *(uint32_t *)data; + + MIX(h,k,m); + + data += 4; + len -= 4; + } + + //---------- + // Handle tail bytes + + switch(len) + { + case 3: h ^= data[2] << 16; + case 2: h ^= data[1] << 8; + case 1: h ^= data[0]; + h *= m; + }; + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + + return h; + } +} + +//----------------------------------------------------------------------------- diff --git a/extern/murmurhash/MurmurHash2.h b/extern/murmurhash/MurmurHash2.h new file mode 100644 index 0000000000..e6d0c36924 --- /dev/null +++ b/extern/murmurhash/MurmurHash2.h @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------------- +// MurmurHash2 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. + +#ifndef _MURMURHASH2_H_ +#define _MURMURHASH2_H_ + +//----------------------------------------------------------------------------- +// Platform-specific functions and macros + +// Microsoft Visual Studio + +#if defined(_MSC_VER) && (_MSC_VER < 1600) + +typedef unsigned char uint8_t; +typedef unsigned int uint32_t; +typedef unsigned __int64 uint64_t; + +// Other compilers + +#else // defined(_MSC_VER) + +#include + +#endif // !defined(_MSC_VER) + +//----------------------------------------------------------------------------- + +uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed ); +uint64_t MurmurHash64A ( const void * key, int len, uint64_t seed ); +uint64_t MurmurHash64B ( const void * key, int len, uint64_t seed ); +uint32_t MurmurHash2A ( const void * key, int len, uint32_t seed ); +uint32_t MurmurHashNeutral2 ( const void * key, int len, uint32_t seed ); +uint32_t MurmurHashAligned2 ( const void * key, int len, uint32_t seed ); + +//----------------------------------------------------------------------------- + +#endif // _MURMURHASH2_H_ From 003b0d48be3bff632e4c941c8a1dbf536293f4a4 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 19 Dec 2015 17:39:02 +1100 Subject: [PATCH 27/29] Move resource names listing code out of CSMWorld::Resources ctor in order to avoid multiple scan of the same resources. - The resources are still scanned twice, once when the archive/directory is added and another time when the names are listed. --- apps/opencs/model/world/resources.cpp | 20 ++------- apps/opencs/model/world/resources.hpp | 3 ++ apps/opencs/model/world/resourcesmanager.cpp | 45 +++++++++++++++++--- components/bsa/bsa_archive.cpp | 6 ++- 4 files changed, 50 insertions(+), 24 deletions(-) diff --git a/apps/opencs/model/world/resources.cpp b/apps/opencs/model/world/resources.cpp index fc5b127dc5..c5f5d6c769 100644 --- a/apps/opencs/model/world/resources.cpp +++ b/apps/opencs/model/world/resources.cpp @@ -4,30 +4,18 @@ #include #include -#include - #include CSMWorld::Resources::Resources (const std::string& baseDirectory, UniversalId::Type type, - const char * const *extensions) + std::vector resources, const char * const *extensions) : mBaseDirectory (baseDirectory), mType (type) { int baseSize = mBaseDirectory.size(); - Ogre::StringVector resourcesGroups = - Ogre::ResourceGroupManager::getSingleton().getResourceGroups(); - - for (Ogre::StringVector::iterator iter (resourcesGroups.begin()); - iter!=resourcesGroups.end(); ++iter) + for (std::vector::iterator iter(resources.begin()); iter != resources.end(); ++iter) { - if (*iter=="General" || *iter=="Internal" || *iter=="Autodetect") - continue; - - Ogre::StringVectorPtr resources = - Ogre::ResourceGroupManager::getSingleton().listResourceNames (*iter); - - for (Ogre::StringVector::const_iterator iter2 (resources->begin()); - iter2!=resources->end(); ++iter2) + // populate mFiles and mIndex + for (Ogre::StringVector::const_iterator iter2 ((*iter)->begin()); iter2 != (*iter)->end(); ++iter2) { if (static_cast (iter2->size())substr (0, baseSize)!=mBaseDirectory || diff --git a/apps/opencs/model/world/resources.hpp b/apps/opencs/model/world/resources.hpp index 9c1c76b6f2..6e318e0927 100644 --- a/apps/opencs/model/world/resources.hpp +++ b/apps/opencs/model/world/resources.hpp @@ -5,6 +5,8 @@ #include #include +#include + #include "universalid.hpp" namespace CSMWorld @@ -20,6 +22,7 @@ namespace CSMWorld /// \param type Type of resources in this table. Resources (const std::string& baseDirectory, UniversalId::Type type, + std::vector resources, const char * const *extensions = 0); int getSize() const; diff --git a/apps/opencs/model/world/resourcesmanager.cpp b/apps/opencs/model/world/resourcesmanager.cpp index 1b47701979..ba1d9629ca 100644 --- a/apps/opencs/model/world/resourcesmanager.cpp +++ b/apps/opencs/model/world/resourcesmanager.cpp @@ -2,6 +2,8 @@ #include +#include + void CSMWorld::ResourcesManager::addResources (const Resources& resources) { mResources.insert (std::make_pair (resources.getType(), resources)); @@ -11,14 +13,45 @@ void CSMWorld::ResourcesManager::addResources (const Resources& resources) void CSMWorld::ResourcesManager::listResources() { + // Following code was taken out of Resources ctor, since it was being executed each time + // and slow enough to showe up in the profiler. + // + // See Editor ctor which calls Bsa::registerResources() + // + // resourceGroups include those from config files, e.g.: + // + // C:/Program Files\OpenMW\data + // C:/Program Files (x86)\Bethesda Softworks\Morrowind\Data Files + // + // and from archives: + // + // C:/Program Files (x86)\Bethesda Softworks\Morrowind\Data Files\Morrowind.bsa + // C:/Program Files (x86)\Bethesda Softworks\Morrowind\Data Files\Tribunal.bsa + // C:/Program Files (x86)\Bethesda Softworks\Morrowind\Data Files\Bloodmoon.bsa + // + std::vector resources; + + Ogre::StringVector resourcesGroups = + Ogre::ResourceGroupManager::getSingleton().getResourceGroups(); + + for (Ogre::StringVector::iterator iter (resourcesGroups.begin()); + iter!=resourcesGroups.end(); ++iter) + { + if (*iter=="General" || *iter=="Internal" || *iter=="Autodetect") + continue; + + resources.push_back( + Ogre::ResourceGroupManager::getSingleton().listResourceNames (*iter)); + } + static const char * const sMeshTypes[] = { "nif", 0 }; - addResources (Resources ("meshes", UniversalId::Type_Mesh, sMeshTypes)); - addResources (Resources ("icons", UniversalId::Type_Icon)); - addResources (Resources ("music", UniversalId::Type_Music)); - addResources (Resources ("sound", UniversalId::Type_SoundRes)); - addResources (Resources ("textures", UniversalId::Type_Texture)); - addResources (Resources ("videos", UniversalId::Type_Video)); + addResources (Resources ("meshes", UniversalId::Type_Mesh, resources, sMeshTypes)); + addResources (Resources ("icons", UniversalId::Type_Icon, resources)); + addResources (Resources ("music", UniversalId::Type_Music, resources)); + addResources (Resources ("sound", UniversalId::Type_SoundRes, resources)); + addResources (Resources ("textures", UniversalId::Type_Texture, resources)); + addResources (Resources ("videos", UniversalId::Type_Video, resources)); } const CSMWorld::Resources& CSMWorld::ResourcesManager::get (UniversalId::Type type) const diff --git a/components/bsa/bsa_archive.cpp b/components/bsa/bsa_archive.cpp index 35fbf62a8a..12836ea70b 100644 --- a/components/bsa/bsa_archive.cpp +++ b/components/bsa/bsa_archive.cpp @@ -49,6 +49,8 @@ using namespace Ogre; static bool fsstrict = false; +static const std::ctype& facet = std::use_facet >(std::locale::classic()); + static char strict_normalize_char(char ch) { return ch == '\\' ? '/' : ch; @@ -56,7 +58,7 @@ static char strict_normalize_char(char ch) static char nonstrict_normalize_char(char ch) { - return ch == '\\' ? '/' : std::tolower(ch,std::locale::classic()); + return ch == '\\' ? '/' : facet.tolower(ch); } template @@ -245,7 +247,7 @@ public: time_t getModifiedTime(const String&) { return 0; } - // This is never called as far as I can see. + // This is never called as far as I can see. (actually called from CSMWorld::Resources ctor) StringVectorPtr list(bool recursive = true, bool dirs = false) { return find ("*", recursive, dirs); From abe6904a9494d9acf52d5bc512e921f77b7dccef Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 19 Dec 2015 17:54:45 +1100 Subject: [PATCH 28/29] Minor performance gains on loading cell references. - Use integers where possible - Unfortunately many functions are simply duplicated for now, but over time the deprecated ones will be removed --- components/esm/cellref.cpp | 12 +++--- components/esm/defs.hpp | 4 +- components/esm/esmreader.cpp | 81 ++++++++++++++++++++++++++++++++++++ components/esm/esmreader.hpp | 20 +++++++++ 4 files changed, 110 insertions(+), 7 deletions(-) diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index b3fb410e38..2aa36867b3 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -6,9 +6,9 @@ void ESM::RefNum::load (ESMReader& esm, bool wide) { if (wide) - esm.getHNT (*this, "FRMR", 8); + esm.getHNT (*this, SREC_FRMR, 8); else - esm.getHNT (mIndex, "FRMR"); + esm.getHNT (mIndex, SREC_FRMR); } void ESM::RefNum::save (ESMWriter &esm, bool wide, const std::string& tag) const @@ -36,14 +36,14 @@ void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum) // the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system. // Its only purpose is a performance optimization for "immovable" things. We don't need this, and it's problematic anyway, // because any item can theoretically be moved by a script. - if (esm.isNextSub ("NAM0")) + if (esm.isNextSub (SREC_NAM0)) esm.skipHSub(); blank(); mRefNum.load (esm, wideRefNum); - mRefID = esm.getHNString ("NAME"); + esm.getHNString (SREC_NAME, mRefID); } void ESM::CellRef::loadData(ESMReader &esm, bool &isDeleted) @@ -180,7 +180,7 @@ void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory, bool void ESM::CellRef::blank() { mRefNum.unset(); - mRefID.clear(); + mRefID.clear(); mScale = 1; mOwner.clear(); mGlobalVariable.clear(); @@ -196,7 +196,7 @@ void ESM::CellRef::blank() mTrap.clear(); mReferenceBlocked = -1; mTeleport = false; - + for (int i=0; i<3; ++i) { mDoorDest.pos[i] = 0; diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 9dc088596a..853c63c065 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -128,7 +128,9 @@ enum RecNameInts enum SubRecNameInts { SREC_DELE = ESM::FourCC<'D','E','L','E'>::value, - SREC_NAME = ESM::FourCC<'N','A','M','E'>::value + SREC_NAME = ESM::FourCC<'N','A','M','E'>::value, + SREC_NAM0 = ESM::FourCC<'N','A','M','0'>::value, + SREC_FRMR = ESM::FourCC<'F','R','M','R'>::value }; } diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 400ead4e27..b4b44f8765 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -109,6 +109,12 @@ std::string ESMReader::getHNString(const char* name) return getHString(); } +void ESMReader::getHNString(const int name, std::string& str) +{ + getSubNameIs(name); + getHString(str); +} + std::string ESMReader::getHString() { getSubHeader(); @@ -130,6 +136,28 @@ std::string ESMReader::getHString() return getString(mCtx.leftSub); } +void ESMReader::getHString(std::string& str) +{ + getSubHeader(); + + // Hack to make MultiMark.esp load. Zero-length strings do not + // occur in any of the official mods, but MultiMark makes use of + // them. For some reason, they break the rules, and contain a byte + // (value 0) even if the header says there is no data. If + // Morrowind accepts it, so should we. + if (mCtx.leftSub == 0) + { + // Skip the following zero byte + mCtx.leftRec--; + char c; + getExact(&c, 1); + str = ""; + return; + } + + getString(str, mCtx.leftSub); +} + void ESMReader::getHExact(void*p, int size) { getSubHeader(); @@ -159,6 +187,24 @@ void ESMReader::getSubNameIs(const char* name) + mCtx.subName.toString()); } +void ESMReader::getSubNameIs(const int name) +{ + getSubName(); + if (mCtx.subName != name) + { + unsigned char typeName[4]; + typeName[0] = name & 0xff; + typeName[1] = (name >> 8) & 0xff; + typeName[2] = (name >> 16) & 0xff; + typeName[3] = (name >> 24) & 0xff; + + std::string subName = std::string((char*)typeName, 4); + + fail("Expected subrecord " + subName + " but got " + + mCtx.subName.toString()); + } +} + bool ESMReader::isNextSub(const char* name) { if (!mCtx.leftRec) @@ -174,6 +220,21 @@ bool ESMReader::isNextSub(const char* name) return !mCtx.subCached; } +bool ESMReader::isNextSub(const int name) +{ + if (!mCtx.leftRec) + return false; + + getSubName(); + + // If the name didn't match, then mark the it as 'cached' so it's + // available for the next call to getSubName. + mCtx.subCached = (mCtx.subName != name); + + // If subCached is false, then subName == name. + return !mCtx.subCached; +} + bool ESMReader::peekNextSub(const char *name) { if (!mCtx.leftRec) @@ -347,6 +408,26 @@ std::string ESMReader::getString(int size) return std::string (ptr, size); } +void ESMReader::getString(std::string& str, int size) +{ + size_t s = size; + if (mBuffer.size() <= s) + // Add some extra padding to reduce the chance of having to resize again later. + mBuffer.resize(3*s); + + mBuffer[s] = 0; // And make sure the string is zero terminated + + char *ptr = &mBuffer[0]; + getExact(ptr, size); // read ESM data + + size = static_cast(strnlen(ptr, size)); + + if (mEncoder) + str = mEncoder->getUtf8(ptr, size); // Convert to UTF8 and return + else + str = std::string (ptr, size); +} + void ESMReader::fail(const std::string &msg) { using namespace std; diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index 66ef981306..3a2f326405 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -103,6 +103,13 @@ public: getHT(x); } + template + void getHNT(X &x, const int name) + { + getSubNameIs(name); + getHT(x); + } + // Optional version of getHNT template void getHNOT(X &x, const char* name) @@ -121,6 +128,14 @@ public: getHT(x); } + template + void getHNT(X &x, const int name, int size) + { + assert(sizeof(X) == size); + getSubNameIs(name); + getHT(x); + } + template void getHNOT(X &x, const char* name, int size) { @@ -159,9 +174,11 @@ public: // Read a string with the given sub-record name std::string getHNString(const char* name); + void getHNString(const int name, std::string& str); // Read a string, including the sub-record header (but not the name) std::string getHString(); + void getHString(std::string& str); // Read the given number of bytes from a subrecord void getHExact(void*p, int size); @@ -177,6 +194,7 @@ public: // Get the next subrecord name and check if it matches the parameter void getSubNameIs(const char* name); + void getSubNameIs(const int name); /** Checks if the next sub record name matches the parameter. If it does, it is read into 'subName' just as if getSubName() was @@ -184,6 +202,7 @@ public: calls to getSubName(), isNextSub() and getSubNameIs(). */ bool isNextSub(const char* name); + bool isNextSub(const int name); bool peekNextSub(const char* name); @@ -256,6 +275,7 @@ public: // Read the next 'size' bytes and return them as a string. Converts // them from native encoding to UTF8 in the process. std::string getString(int size); + void getString(std::string& str, int size); void skip(int bytes) { mEsm->seek(mEsm->tell()+bytes); } uint64_t getOffset() { return mEsm->tell(); } From 90b76801f6ebe95dba721c2b6ccc997ca43a7593 Mon Sep 17 00:00:00 2001 From: cc9cii Date: Sat, 19 Dec 2015 18:50:07 +1100 Subject: [PATCH 29/29] Reduce the call to tolower() for each character when the string is already in lower case. - only a minor performance gain according to the MSVC profiler --- apps/opencs/model/world/infocollection.cpp | 3 ++- components/misc/stringops.hpp | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index 7b7c274a09..f508b683ee 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -106,10 +106,11 @@ int CSMWorld::InfoCollection::getInfoIndex (const std::string& id, const std::st return -1; // brute force loop + std::string lowerId = Misc::StringUtils::lowerCase (id); for (std::vector >::const_iterator it = iter->second.begin(); it != iter->second.end(); ++it) { - if (Misc::StringUtils::ciEqual(it->first, id)) + if (Misc::StringUtils::cEqual(it->first, lowerId)) return it->second; } diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index d6bc190695..1f6da37c7a 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -35,6 +35,20 @@ public: return true; } + static bool cEqual(const std::string &x, const std::string &y) { + if (x.size() != y.size()) { + return false; + } + std::string::const_iterator xit = x.begin(); + std::string::const_iterator yit = y.begin(); + for (; xit != x.end(); ++xit, ++yit) { + if (*xit != *yit) { + return false; + } + } + return true; + } + static int ciCompareLen(const std::string &x, const std::string &y, size_t len) { std::string::const_iterator xit = x.begin();