From 1677fcf324f23c298bcd21a37d764dd22f3cf8c7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 20 Feb 2014 16:59:20 +0100 Subject: [PATCH 1/3] Dialogue merging --- apps/esmtool/record.cpp | 2 +- apps/openmw/mwdialogue/dialoguemanagerimp.cpp | 4 +- apps/openmw/mwdialogue/filter.cpp | 6 +-- apps/openmw/mwdialogue/journalentry.cpp | 4 +- apps/openmw/mwdialogue/journalimp.cpp | 2 +- apps/openmw/mwdialogue/quest.cpp | 8 ++-- apps/openmw/mwworld/esmstore.cpp | 7 ++-- components/esm/loaddial.cpp | 38 +++++++++++++++++++ components/esm/loaddial.hpp | 10 ++++- 9 files changed, 62 insertions(+), 19 deletions(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 25b2a45050..fd4b97acb9 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -651,7 +651,7 @@ void Record::print() // Sadly, there are no DialInfos, because the loader dumps as it // loads, rather than loading and then dumping. :-( Anyone mind if // I change this? - std::vector::iterator iit; + ESM::Dialogue::InfoContainer::iterator iit; for (iit = mData.mInfo.begin(); iit != mData.mInfo.end(); iit++) std::cout << "INFO!" << iit->mId << std::endl; } diff --git a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp index 323b450647..fe37f37ff7 100644 --- a/apps/openmw/mwdialogue/dialoguemanagerimp.cpp +++ b/apps/openmw/mwdialogue/dialoguemanagerimp.cpp @@ -289,7 +289,7 @@ namespace MWDialogue // Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group, // in which case it should not be added to the journal. - for (std::vector::const_iterator iter = dialogue.mInfo.begin(); + for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); iter!=dialogue.mInfo.end(); ++iter) { if (iter->mId == info->mId) @@ -467,7 +467,7 @@ namespace MWDialogue // Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group, // in which case it should not be added to the journal. - for (std::vector::const_iterator iter = mDialogueMap[mLastTopic].mInfo.begin(); + for (ESM::Dialogue::InfoContainer::const_iterator iter = mDialogueMap[mLastTopic].mInfo.begin(); iter!=mDialogueMap[mLastTopic].mInfo.end(); ++iter) { if (iter->mId == info->mId) diff --git a/apps/openmw/mwdialogue/filter.cpp b/apps/openmw/mwdialogue/filter.cpp index b1c700e315..8b9ee9184f 100644 --- a/apps/openmw/mwdialogue/filter.cpp +++ b/apps/openmw/mwdialogue/filter.cpp @@ -621,7 +621,7 @@ std::vector MWDialogue::Filter::list (const ESM::Dialogue bool infoRefusal = false; // Iterate over topic responses to find a matching one - for (std::vector::const_iterator iter = dialogue.mInfo.begin(); + for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); iter!=dialogue.mInfo.end(); ++iter) { if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter)) @@ -646,7 +646,7 @@ std::vector MWDialogue::Filter::list (const ESM::Dialogue const ESM::Dialogue& infoRefusalDialogue = *dialogues.find ("Info Refusal"); - for (std::vector::const_iterator iter = infoRefusalDialogue.mInfo.begin(); + for (ESM::Dialogue::InfoContainer::const_iterator iter = infoRefusalDialogue.mInfo.begin(); iter!=infoRefusalDialogue.mInfo.end(); ++iter) if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter) && testDisposition(*iter, invertDisposition)) { infos.push_back(&*iter); @@ -660,7 +660,7 @@ std::vector MWDialogue::Filter::list (const ESM::Dialogue bool MWDialogue::Filter::responseAvailable (const ESM::Dialogue& dialogue) const { - for (std::vector::const_iterator iter = dialogue.mInfo.begin(); + for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue.mInfo.begin(); iter!=dialogue.mInfo.end(); ++iter) { if (testActor (*iter) && testPlayer (*iter) && testSelectStructs (*iter)) diff --git a/apps/openmw/mwdialogue/journalentry.cpp b/apps/openmw/mwdialogue/journalentry.cpp index 9463e4c45b..55574bf3eb 100644 --- a/apps/openmw/mwdialogue/journalentry.cpp +++ b/apps/openmw/mwdialogue/journalentry.cpp @@ -20,7 +20,7 @@ namespace MWDialogue const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().get().find (topic); - for (std::vector::const_iterator iter (dialogue->mInfo.begin()); + for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) if (iter->mId == mInfoId) { @@ -73,7 +73,7 @@ namespace MWDialogue const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().get().find (topic); - for (std::vector::const_iterator iter (dialogue->mInfo.begin()); + for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) if (iter->mData.mDisposition==index) /// \todo cleanup info structure { diff --git a/apps/openmw/mwdialogue/journalimp.cpp b/apps/openmw/mwdialogue/journalimp.cpp index 724d531cfb..04aa0534dd 100644 --- a/apps/openmw/mwdialogue/journalimp.cpp +++ b/apps/openmw/mwdialogue/journalimp.cpp @@ -56,7 +56,7 @@ namespace MWDialogue if (infoId.empty()) return true; - for (std::vector::const_iterator iter (dialogue->mInfo.begin()); + for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) if (iter->mId == infoId) return true; diff --git a/apps/openmw/mwdialogue/quest.cpp b/apps/openmw/mwdialogue/quest.cpp index 8ecb4bd13a..a699286a15 100644 --- a/apps/openmw/mwdialogue/quest.cpp +++ b/apps/openmw/mwdialogue/quest.cpp @@ -27,7 +27,7 @@ namespace MWDialogue const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().get().find (mTopic); - for (std::vector::const_iterator iter (dialogue->mInfo.begin()); + for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) if (iter->mQuestStatus==ESM::DialInfo::QS_Name) return iter->mResponse; @@ -45,7 +45,7 @@ namespace MWDialogue const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().get().find (mTopic); - for (std::vector::const_iterator iter (dialogue->mInfo.begin()); + for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) if (iter->mData.mDisposition==index && iter->mQuestStatus!=ESM::DialInfo::QS_Name) { @@ -53,8 +53,6 @@ namespace MWDialogue mFinished = true; else if (iter->mQuestStatus==ESM::DialInfo::QS_Restart) mFinished = false; - - // Don't return here. Quest status may actually be in a different info record, since we don't merge these (yet?) } // The index must be set even if no related journal entry was found @@ -73,7 +71,7 @@ namespace MWDialogue const ESM::Dialogue *dialogue = MWBase::Environment::get().getWorld()->getStore().get().find (entry.mTopic); - for (std::vector::const_iterator iter (dialogue->mInfo.begin()); + for (ESM::Dialogue::InfoContainer::const_iterator iter (dialogue->mInfo.begin()); iter!=dialogue->mInfo.end(); ++iter) if (iter->mId == entry.mInfoId) { diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index ed140434b5..ce8c800b9a 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -72,9 +72,10 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) if (n.val == ESM::REC_INFO) { std::string id = esm.getHNOString("INAM"); if (dialogue) { - dialogue->mInfo.push_back(ESM::DialInfo()); - dialogue->mInfo.back().mId = id; - dialogue->mInfo.back().load(esm); + ESM::DialInfo info; + info.mId = id; + info.load(esm); + dialogue->addInfo(info, esm.getIndex() != 0); } else { std::cerr << "error: info record without dialog" << std::endl; esm.skipRecord(); diff --git a/components/esm/loaddial.cpp b/components/esm/loaddial.cpp index fb43ee8586..4a2f17e26c 100644 --- a/components/esm/loaddial.cpp +++ b/components/esm/loaddial.cpp @@ -1,5 +1,7 @@ #include "loaddial.hpp" +#include + #include "esmreader.hpp" #include "esmwriter.hpp" #include "defs.hpp" @@ -43,4 +45,40 @@ void Dialogue::save(ESMWriter &esm) const mInfo.clear(); } +void Dialogue::addInfo(const ESM::DialInfo& info, bool merge) +{ + // TODO: use std::move + if (!merge || mInfo.empty() || info.mNext.empty()) + { + mInfo.push_back(info); + return; + } + if (info.mPrev.empty()) + { + mInfo.push_front(info); + return; + } + int i=0; + for (ESM::Dialogue::InfoContainer::iterator it = mInfo.begin(); it != mInfo.end(); ++it) + { + if (info.mPrev == it->mId) + { + mInfo.insert(++it, info); + return; + } + if (info.mNext == it->mId) + { + mInfo.insert(it, info); + return; + } + if (info.mId == it->mId) + { + *it = info; + return; + } + ++i; + } + std::cerr << "Failed to insert info " << info.mId << std::endl; +} + } diff --git a/components/esm/loaddial.hpp b/components/esm/loaddial.hpp index 63d78833e1..612c4fee6b 100644 --- a/components/esm/loaddial.hpp +++ b/components/esm/loaddial.hpp @@ -2,7 +2,7 @@ #define OPENMW_ESM_DIAL_H #include -#include +#include #include "loadinfo.hpp" @@ -33,11 +33,17 @@ struct Dialogue std::string mId; signed char mType; - std::vector mInfo; + + typedef std::list InfoContainer; + + InfoContainer mInfo; void load(ESMReader &esm); void save(ESMWriter &esm) const; + /// @param merge Merge with existing list, or just push each record to the end of the list? + void addInfo (const ESM::DialInfo& info, bool merge); + void blank(); ///< Set record to default state (does not touch the ID and does not change the type). }; From 0aa1042fd79488aa8f51bb80492b246b199cb481 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 18 May 2014 15:59:45 +0200 Subject: [PATCH 2/3] Optimized dialogue merging with a lookup map --- components/esm/loaddial.cpp | 51 ++++++++++++++++++++++--------------- components/esm/loaddial.hpp | 6 +++++ 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/components/esm/loaddial.cpp b/components/esm/loaddial.cpp index 4a2f17e26c..5adf409bba 100644 --- a/components/esm/loaddial.cpp +++ b/components/esm/loaddial.cpp @@ -47,37 +47,46 @@ void Dialogue::save(ESMWriter &esm) const void Dialogue::addInfo(const ESM::DialInfo& info, bool merge) { - // TODO: use std::move if (!merge || mInfo.empty() || info.mNext.empty()) { - mInfo.push_back(info); + mLookup[info.mId] = mInfo.insert(mInfo.end(), info); return; } if (info.mPrev.empty()) { - mInfo.push_front(info); + mLookup[info.mId] = mInfo.insert(mInfo.begin(), info); return; } - int i=0; - for (ESM::Dialogue::InfoContainer::iterator it = mInfo.begin(); it != mInfo.end(); ++it) + + ESM::Dialogue::InfoContainer::iterator it = mInfo.end(); + + std::map::iterator lookup; + lookup = mLookup.find(info.mPrev); + if (lookup != mLookup.end()) { - if (info.mPrev == it->mId) - { - mInfo.insert(++it, info); - return; - } - if (info.mNext == it->mId) - { - mInfo.insert(it, info); - return; - } - if (info.mId == it->mId) - { - *it = info; - return; - } - ++i; + it = lookup->second; + + mLookup[info.mId] = mInfo.insert(++it, info); + return; } + + lookup = mLookup.find(info.mNext); + if (lookup != mLookup.end()) + { + it = lookup->second; + + mLookup[info.mId] = mInfo.insert(it, info); + return; + } + + lookup = mLookup.find(info.mId); + if (lookup != mLookup.end()) + { + it = lookup->second; + *it = info; + return; + } + std::cerr << "Failed to insert info " << info.mId << std::endl; } diff --git a/components/esm/loaddial.hpp b/components/esm/loaddial.hpp index 612c4fee6b..6ec5527f9f 100644 --- a/components/esm/loaddial.hpp +++ b/components/esm/loaddial.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "loadinfo.hpp" @@ -36,8 +37,13 @@ struct Dialogue typedef std::list InfoContainer; + typedef std::map LookupMap; + InfoContainer mInfo; + // This is only used during the loading phase to speed up DialInfo merging. + LookupMap mLookup; + void load(ESMReader &esm); void save(ESMWriter &esm) const; From a87b64d2da4795d9d6accb8c7f63cbdc06f91002 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 18 May 2014 16:29:24 +0200 Subject: [PATCH 3/3] Fix some ancient code --- apps/openmw/mwworld/esmstore.cpp | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index ce8c800b9a..1fdf3c5a50 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -27,8 +27,6 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) { listener->setProgressRange(1000); - std::set missing; - ESM::Dialogue *dialogue = 0; /// \todo Move this to somewhere else. ESMReader? @@ -85,9 +83,9 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) } else if (n.val == ESM::REC_SKIL) { mSkills.load (esm); } else { - // Not found (this would be an error later) - esm.skipRecord(); - missing.insert(n.toString()); + std::stringstream error; + error << "Unknown record: " << n.toString(); + throw std::runtime_error(error.str()); } } else { // Load it @@ -114,19 +112,6 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) } listener->setProgress(esm.getFileOffset() / (float)esm.getFileSize() * 1000); } - - /* This information isn't needed on screen. But keep the code around - for debugging purposes later. - - cout << "\n" << mStores.size() << " record types:\n"; - for(RecListList::iterator it = mStores.begin(); it != mStores.end(); it++) - cout << " " << toStr(it->first) << ": " << it->second->getSize() << endl; - cout << "\nNot implemented yet: "; - for(set::iterator it = missing.begin(); - it != missing.end(); it++ ) - cout << *it << " "; - cout << endl; - */ } void ESMStore::setUp()