diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index bbc44f5e1..3e23679b1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -190,8 +190,9 @@ namespace MWWorld // This parses the ESM file ESM::ESMReader lEsm; lEsm.setEncoding(encoding); - lEsm.open (masterPath.string()); lEsm.setIndex(idx); + lEsm.setGlobalReaderList(&mEsm); + lEsm.open (masterPath.string()); mEsm[idx] = lEsm; mStore.load (mEsm[idx]); } @@ -205,8 +206,9 @@ namespace MWWorld // This parses the ESP file ESM::ESMReader lEsm; lEsm.setEncoding(encoding); - lEsm.open (pluginPath.string()); lEsm.setIndex(idx); + lEsm.setGlobalReaderList(&mEsm); + lEsm.open (pluginPath.string()); mEsm[idx] = lEsm; mStore.load (mEsm[idx]); } diff --git a/components/esm/esmcommon.hpp b/components/esm/esmcommon.hpp index d61564c67..335d12337 100644 --- a/components/esm/esmcommon.hpp +++ b/components/esm/esmcommon.hpp @@ -89,6 +89,7 @@ struct MasterData { std::string name; uint64_t size; + int index; // Position of the parent file in the global list of loaded files }; // Data that is only present in save game files diff --git a/components/esm/esmreader.cpp b/components/esm/esmreader.cpp index 2915a1ce7..d1703a2ac 100644 --- a/components/esm/esmreader.cpp +++ b/components/esm/esmreader.cpp @@ -1,5 +1,6 @@ #include "esmreader.hpp" #include +#include namespace ESM { @@ -61,6 +62,7 @@ void ESMReader::openRaw(Ogre::DataStreamPtr _esm, const std::string &name) void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name) { openRaw(_esm, name); + std::string fname = boost::filesystem::path(name).filename().string(); if (getRecName() != "TES3") fail("Not a valid Morrowind file"); @@ -78,6 +80,29 @@ void ESMReader::open(Ogre::DataStreamPtr _esm, const std::string &name) MasterData m; m.name = getHString(); m.size = getHNLong("DATA"); + // Cache parent esX files by tracking their indices in the global list of + // all files/readers used by the engine. This will greaty help to accelerate + // parsing of reference IDs. + size_t index = ~0; + // TODO: check for case mismatch, it might be required on Windows. + size_t i = 0; + // FIXME: This is ugly! Make it nicer! + for (; i < idx; i++) { + const std::string &candidate = mGlobalReaderList->at(i).getContext().filename; + std::string fnamecandidate = boost::filesystem::path(candidate).filename().string(); + if (m.name == fnamecandidate) { + index = i; + break; + } + } + if (index == (size_t)~0) { + // 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 " + fname + " asks for parent file " + m.name + + ", but it has not been loaded yet. Please check your load order."; + fail(fstring); + } + m.index = index; mMasters.push_back(m); } diff --git a/components/esm/esmreader.hpp b/components/esm/esmreader.hpp index f09442a57..b415009d3 100644 --- a/components/esm/esmreader.hpp +++ b/components/esm/esmreader.hpp @@ -83,6 +83,8 @@ public: int idx; void setIndex(const int index) {idx = index; mCtx.index = index;} const int getIndex() {return idx;} + + void setGlobalReaderList(std::vector *list) {mGlobalReaderList = list;} /************************************************************************* * @@ -254,6 +256,7 @@ private: SaveData mSaveData; MasterList mMasters; + std::vector *mGlobalReaderList; ToUTF8::FromType mEncoding; }; } diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index beedd3cac..1a1811a90 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -170,8 +170,6 @@ std::string Cell::getDescription() const bool Cell::getNextRef(ESMReader &esm, CellRef &ref) { - // TODO: Add support for multiple plugins. This requires a tricky renaming scheme for "ref.mRefnum". - // I'll probably add something to "ESMReader", we will need one per plugin anyway. // TODO: Try and document reference numbering, I don't think this has been done anywhere else. if (!esm.hasMoreSubs()) return false; @@ -194,6 +192,26 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref) // This may require some not-so-small behing-the-scenes updates. esm.getHNT(ref.mRefnum, "FRMR"); ref.mRefID = esm.getHNString("NAME"); + + // Identify references belonging to a parent file and adapt the ID accordingly. + int local = (ref.mRefnum & 0xff000000) >> 24; + size_t global = esm.getIndex() + 1; + if (local) + { + // If the most significant 8 bits are used, then this reference already exists. + // In this case, do not spawn a new reference, but overwrite the old one. + ref.mRefnum &= 0x00ffffff; // delete old plugin ID + const ESM::ESMReader::MasterList &masters = esm.getMasters(); + // TODO: Test how Morrowind does it! Maybe the first entry in the list should be "1"... + global = masters[local-1].index + 1; + ref.mRefnum |= global << 24; // insert global plugin ID + std::cout << "Refnum_old = " << ref.mRefnum << " " << local << " " << global << std::endl; + } + else + { + // This is an addition by the present plugin. Set the corresponding plugin index. + ref.mRefnum |= global << 24; // insert global plugin ID + } // getHNOT will not change the existing value if the subrecord is // missing