#ifndef OPENMW_MWWORLD_STORE_H #define OPENMW_MWWORLD_STORE_H #include #include #include #include #include #include "recordcmp.hpp" namespace MWWorld { struct StoreBase { virtual ~StoreBase() {} virtual void setUp() {} virtual void listIdentifier(std::vector &list) const {} virtual size_t getSize() const = 0; virtual int getDynamicSize() const { return 0; } virtual void load(ESM::ESMReader &esm, const std::string &id) = 0; virtual bool eraseStatic(const std::string &id) {return false;} virtual void clearDynamic() {} virtual void write (ESM::ESMWriter& writer) const {} virtual void read (ESM::ESMReader& reader) {} ///< Read into dynamic storage }; template class SharedIterator { typedef typename std::vector::const_iterator Iter; Iter mIter; public: SharedIterator() {} SharedIterator(const SharedIterator &orig) : mIter(orig.mIter) {} SharedIterator(const Iter &iter) : mIter(iter) {} SharedIterator &operator++() { ++mIter; return *this; } SharedIterator operator++(int) { SharedIterator iter = *this; ++mIter; return iter; } SharedIterator &operator--() { --mIter; return *this; } SharedIterator operator--(int) { SharedIterator iter = *this; --mIter; return iter; } bool operator==(const SharedIterator &x) const { return mIter == x.mIter; } bool operator!=(const SharedIterator &x) const { return !(*this == x); } const T &operator*() const { return **mIter; } const T *operator->() const { return &(**mIter); } }; class ESMStore; template class Store : public StoreBase { std::map mStatic; std::vector mShared; std::map mDynamic; typedef std::map Dynamic; typedef std::map Static; class GetRecords { const std::string mFind; std::vector *mRecords; public: GetRecords(const std::string &str, std::vector *records) : mFind(Misc::StringUtils::lowerCase(str)), mRecords(records) { } void operator()(const T *item) { if(Misc::StringUtils::ciCompareLen(mFind, item->mId, mFind.size()) == 0) mRecords->push_back(item); } }; friend class ESMStore; public: Store() {} Store(const Store &orig) : mStatic(orig.mData) {} typedef SharedIterator iterator; // setUp needs to be called again after virtual void clearDynamic() { mDynamic.clear(); mShared.clear(); } const T *search(const std::string &id) const { T item; item.mId = Misc::StringUtils::lowerCase(id); typename std::map::const_iterator it = mStatic.find(item.mId); if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { return &(it->second); } typename Dynamic::const_iterator dit = mDynamic.find(item.mId); if (dit != mDynamic.end()) { return &dit->second; } return 0; } /** Returns a random record that starts with the named ID, or NULL if not found. */ const T *searchRandom(const std::string &id) const { std::vector results; std::for_each(mShared.begin(), mShared.end(), GetRecords(id, &results)); if(!results.empty()) return results[int(std::rand()/((double)RAND_MAX+1)*results.size())]; return NULL; } const T *find(const std::string &id) const { const T *ptr = search(id); if (ptr == 0) { std::ostringstream msg; msg << "Object '" << id << "' not found (const)"; throw std::runtime_error(msg.str()); } return ptr; } /** Returns a random record that starts with the named ID. An exception is thrown if none * are found. */ const T *findRandom(const std::string &id) const { const T *ptr = searchRandom(id); if(ptr == 0) { std::ostringstream msg; msg << "Object starting with '"<::iterator it = mStatic.begin(); for (; it != mStatic.end(); ++it) { mShared.push_back(&(it->second)); } } iterator begin() const { return mShared.begin(); } iterator end() const { return mShared.end(); } size_t getSize() const { return mShared.size(); } int getDynamicSize() const { return mDynamic.size(); } void listIdentifier(std::vector &list) const { list.reserve(list.size() + getSize()); typename std::vector::const_iterator it = mShared.begin(); for (; it != mShared.end(); ++it) { list.push_back((*it)->mId); } } T *insert(const T &item) { std::string id = Misc::StringUtils::lowerCase(item.mId); std::pair result = mDynamic.insert(std::pair(id, item)); T *ptr = &result.first->second; if (result.second) { mShared.push_back(ptr); } else { *ptr = item; } return ptr; } T *insertStatic(const T &item) { std::string id = Misc::StringUtils::lowerCase(item.mId); std::pair result = mStatic.insert(std::pair(id, item)); T *ptr = &result.first->second; if (result.second) { mShared.push_back(ptr); } else { *ptr = item; } return ptr; } bool eraseStatic(const std::string &id) { T item; item.mId = Misc::StringUtils::lowerCase(id); typename std::map::iterator it = mStatic.find(item.mId); if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { // delete from the static part of mShared typename std::vector::iterator sharedIter = mShared.begin(); typename std::vector::iterator end = sharedIter + mStatic.size(); while (sharedIter != mShared.end() && sharedIter != end) { if((*sharedIter)->mId == item.mId) { mShared.erase(sharedIter); break; } ++sharedIter; } mStatic.erase(it); } return true; } bool erase(const std::string &id) { std::string key = Misc::StringUtils::lowerCase(id); typename Dynamic::iterator it = mDynamic.find(key); if (it == mDynamic.end()) { return false; } mDynamic.erase(it); // have to reinit the whole shared part mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); for (it = mDynamic.begin(); it != mDynamic.end(); ++it) { mShared.push_back(&it->second); } return true; } bool erase(const T &item) { return erase(item.mId); } void write (ESM::ESMWriter& writer) const { for (typename Dynamic::const_iterator iter (mDynamic.begin()); iter!=mDynamic.end(); ++iter) { writer.startRecord (T::sRecordId); writer.writeHNString ("NAME", iter->second.mId); iter->second.save (writer); writer.endRecord (T::sRecordId); } } void read (ESM::ESMReader& reader) { T record; record.mId = reader.getHNString ("NAME"); record.load (reader); insert (record); } }; template <> inline void Store::clearDynamic() { std::map::iterator iter = mDynamic.begin(); while (iter!=mDynamic.end()) if (iter->first=="player") ++iter; else mDynamic.erase (iter++); mShared.clear(); } template <> inline void Store::load(ESM::ESMReader &esm, const std::string &id) { std::string idLower = Misc::StringUtils::lowerCase(id); std::map::iterator it = mStatic.find(idLower); if (it == mStatic.end()) { it = mStatic.insert( std::make_pair( idLower, ESM::Dialogue() ) ).first; it->second.mId = id; // don't smash case here, as this line is printed... I think } //I am not sure is it need to load the dialog from a plugin if it was already loaded from prevois plugins it->second.load(esm); } template <> inline void Store::load(ESM::ESMReader &esm, const std::string &id) { ESM::Script scpt; scpt.load(esm); Misc::StringUtils::toLower(scpt.mId); mStatic[scpt.mId] = scpt; } template <> inline void Store::load(ESM::ESMReader &esm, const std::string &id) { ESM::StartScript s; s.load(esm); s.mId = Misc::StringUtils::toLower(s.mScript); mStatic[s.mId] = s; } template <> class Store : public StoreBase { // For multiple ESM/ESP files we need one list per file. typedef std::vector LandTextureList; std::vector mStatic; public: Store() { mStatic.push_back(LandTextureList()); LandTextureList <exl = mStatic[0]; // More than enough to hold Morrowind.esm. Extra lists for plugins will we // added on-the-fly in a different method. ltexl.reserve(128); } typedef std::vector::const_iterator iterator; const ESM::LandTexture *search(size_t index, size_t plugin) const { assert(plugin < mStatic.size()); const LandTextureList <exl = mStatic[plugin]; assert(index < ltexl.size()); return <exl.at(index); } const ESM::LandTexture *find(size_t index, size_t plugin) const { const ESM::LandTexture *ptr = search(index, plugin); if (ptr == 0) { std::ostringstream msg; msg << "Land texture with index " << index << " not found"; throw std::runtime_error(msg.str()); } return ptr; } size_t getSize() const { return mStatic.size(); } size_t getSize(size_t plugin) const { assert(plugin < mStatic.size()); return mStatic[plugin].size(); } void load(ESM::ESMReader &esm, const std::string &id, size_t plugin) { ESM::LandTexture lt; lt.load(esm); lt.mId = id; // Make sure we have room for the structure if (plugin >= mStatic.size()) { mStatic.resize(plugin+1); } LandTextureList <exl = mStatic[plugin]; if(lt.mIndex + 1 > (int)ltexl.size()) ltexl.resize(lt.mIndex+1); // Store it ltexl[lt.mIndex] = lt; } void load(ESM::ESMReader &esm, const std::string &id) { load(esm, id, esm.getIndex()); } iterator begin(size_t plugin) const { assert(plugin < mStatic.size()); return mStatic[plugin].begin(); } iterator end(size_t plugin) const { assert(plugin < mStatic.size()); return mStatic[plugin].end(); } }; template <> class Store : public StoreBase { std::vector mStatic; struct Compare { bool operator()(const ESM::Land *x, const ESM::Land *y) { if (x->mX == y->mX) { return x->mY < y->mY; } return x->mX < y->mX; } }; public: typedef SharedIterator iterator; virtual ~Store() { for (std::vector::const_iterator it = mStatic.begin(); it != mStatic.end(); ++it) { delete *it; } } size_t getSize() const { return mStatic.size(); } iterator begin() const { return iterator(mStatic.begin()); } iterator end() const { return iterator(mStatic.end()); } ESM::Land *search(int x, int y) const { ESM::Land land; land.mX = x, land.mY = y; std::vector::const_iterator it = std::lower_bound(mStatic.begin(), mStatic.end(), &land, Compare()); if (it != mStatic.end() && (*it)->mX == x && (*it)->mY == y) { return const_cast(*it); } return 0; } ESM::Land *find(int x, int y) const{ ESM::Land *ptr = search(x, y); if (ptr == 0) { std::ostringstream msg; msg << "Land at (" << x << ", " << y << ") not found"; throw std::runtime_error(msg.str()); } return ptr; } void load(ESM::ESMReader &esm, const std::string &id) { ESM::Land *ptr = new ESM::Land(); ptr->load(esm); // Same area defined in multiple plugins? -> last plugin wins // Can't use search() because we aren't sorted yet - is there any other way to speed this up? for (std::vector::iterator it = mStatic.begin(); it != mStatic.end(); ++it) { if ((*it)->mX == ptr->mX && (*it)->mY == ptr->mY) { delete *it; mStatic.erase(it); break; } } mStatic.push_back(ptr); } void setUp() { std::sort(mStatic.begin(), mStatic.end(), Compare()); } }; template <> class Store : public StoreBase { struct ExtCmp { bool operator()(const ESM::Cell &x, const ESM::Cell &y) { if (x.mData.mX == y.mData.mX) { return x.mData.mY < y.mData.mY; } return x.mData.mX < y.mData.mX; } }; struct DynamicExtCmp { bool operator()(const std::pair &left, const std::pair &right) const { if (left.first == right.first) { return left.second < right.second; } return left.first < right.first; } }; typedef std::map DynamicInt; typedef std::map, ESM::Cell, DynamicExtCmp> DynamicExt; DynamicInt mInt; DynamicExt mExt; std::vector mSharedInt; std::vector mSharedExt; DynamicInt mDynamicInt; DynamicExt mDynamicExt; const ESM::Cell *search(const ESM::Cell &cell) const { if (cell.isExterior()) { return search(cell.getGridX(), cell.getGridY()); } return search(cell.mName); } public: ESMStore *mEsmStore; typedef SharedIterator iterator; Store() {} const ESM::Cell *search(const std::string &id) const { ESM::Cell cell; cell.mName = Misc::StringUtils::lowerCase(id); std::map::const_iterator it = mInt.find(cell.mName); if (it != mInt.end() && Misc::StringUtils::ciEqual(it->second.mName, id)) { return &(it->second); } DynamicInt::const_iterator dit = mDynamicInt.find(cell.mName); if (dit != mDynamicInt.end()) { return &dit->second; } return 0; } const ESM::Cell *search(int x, int y) const { ESM::Cell cell; cell.mData.mX = x, cell.mData.mY = y; std::pair key(x, y); DynamicExt::const_iterator it = mExt.find(key); if (it != mExt.end()) { return &(it->second); } DynamicExt::const_iterator dit = mDynamicExt.find(key); if (dit != mDynamicExt.end()) { return &dit->second; } return 0; } const ESM::Cell *searchOrCreate(int x, int y) { ESM::Cell cell; cell.mData.mX = x, cell.mData.mY = y; std::pair key(x, y); DynamicExt::const_iterator it = mExt.find(key); if (it != mExt.end()) { return &(it->second); } DynamicExt::const_iterator dit = mDynamicExt.find(key); if (dit != mDynamicExt.end()) { return &dit->second; } ESM::Cell *newCell = new ESM::Cell; newCell->mData.mX = x; newCell->mData.mY = y; mExt[std::make_pair(x, y)] = *newCell; delete newCell; return &mExt[std::make_pair(x, y)]; } const ESM::Cell *find(const std::string &id) const { const ESM::Cell *ptr = search(id); if (ptr == 0) { std::ostringstream msg; msg << "Interior cell '" << id << "' not found"; throw std::runtime_error(msg.str()); } return ptr; } const ESM::Cell *find(int x, int y) const { const ESM::Cell *ptr = search(x, y); if (ptr == 0) { std::ostringstream msg; msg << "Exterior at (" << x << ", " << y << ") not found"; throw std::runtime_error(msg.str()); } return ptr; } void setUp() { //typedef std::vector::iterator Iterator; typedef DynamicExt::iterator ExtIterator; typedef std::map::iterator IntIterator; //std::sort(mInt.begin(), mInt.end(), RecordCmp()); mSharedInt.reserve(mInt.size()); for (IntIterator it = mInt.begin(); it != mInt.end(); ++it) { mSharedInt.push_back(&(it->second)); } //std::sort(mExt.begin(), mExt.end(), ExtCmp()); mSharedExt.reserve(mExt.size()); for (ExtIterator it = mExt.begin(); it != mExt.end(); ++it) { mSharedExt.push_back(&(it->second)); } } // HACK: Method implementation had to be moved to a separate cpp file, as we would otherwise get // errors related to the compare operator used in std::find for ESM::MovedCellRefTracker::find. // There some nasty three-way cyclic header dependency involved, which I could only fix by moving // this method. void load(ESM::ESMReader &esm, const std::string &id); iterator intBegin() const { return iterator(mSharedInt.begin()); } iterator intEnd() const { return iterator(mSharedInt.end()); } iterator extBegin() const { return iterator(mSharedExt.begin()); } iterator extEnd() const { return iterator(mSharedExt.end()); } // Return the northernmost cell in the easternmost column. const ESM::Cell *searchExtByName(const std::string &id) const { ESM::Cell *cell = 0; std::vector::const_iterator it = mSharedExt.begin(); for (; it != mSharedExt.end(); ++it) { if (Misc::StringUtils::ciEqual((*it)->mName, id)) { if ( cell == 0 || ( (*it)->mData.mX > cell->mData.mX ) || ( (*it)->mData.mX == cell->mData.mX && (*it)->mData.mY > cell->mData.mY ) ) { cell = *it; } } } return cell; } // Return the northernmost cell in the easternmost column. const ESM::Cell *searchExtByRegion(const std::string &id) const { ESM::Cell *cell = 0; std::vector::const_iterator it = mSharedExt.begin(); for (; it != mSharedExt.end(); ++it) { if (Misc::StringUtils::ciEqual((*it)->mRegion, id)) { if ( cell == 0 || ( (*it)->mData.mX > cell->mData.mX ) || ( (*it)->mData.mX == cell->mData.mX && (*it)->mData.mY > cell->mData.mY ) ) { cell = *it; } } } return cell; } size_t getSize() const { return mSharedInt.size() + mSharedExt.size(); } void listIdentifier(std::vector &list) const { list.reserve(list.size() + mSharedInt.size()); std::vector::const_iterator it = mSharedInt.begin(); for (; it != mSharedInt.end(); ++it) { list.push_back((*it)->mName); } } ESM::Cell *insert(const ESM::Cell &cell) { if (search(cell) != 0) { std::ostringstream msg; msg << "Failed to create "; msg << ((cell.isExterior()) ? "exterior" : "interior"); msg << " cell"; throw std::runtime_error(msg.str()); } ESM::Cell *ptr; if (cell.isExterior()) { std::pair key(cell.getGridX(), cell.getGridY()); // duplicate insertions are avoided by search(ESM::Cell &) std::pair result = mDynamicExt.insert(std::make_pair(key, cell)); ptr = &result.first->second; mSharedExt.push_back(ptr); } else { std::string key = Misc::StringUtils::lowerCase(cell.mName); // duplicate insertions are avoided by search(ESM::Cell &) std::pair result = mDynamicInt.insert(std::make_pair(key, cell)); ptr = &result.first->second; mSharedInt.push_back(ptr); } return ptr; } bool erase(const ESM::Cell &cell) { if (cell.isExterior()) { return erase(cell.getGridX(), cell.getGridY()); } return erase(cell.mName); } bool erase(const std::string &id) { std::string key = Misc::StringUtils::lowerCase(id); DynamicInt::iterator it = mDynamicInt.find(key); if (it == mDynamicInt.end()) { return false; } mDynamicInt.erase(it); mSharedInt.erase( mSharedInt.begin() + mSharedInt.size(), mSharedInt.end() ); for (it = mDynamicInt.begin(); it != mDynamicInt.end(); ++it) { mSharedInt.push_back(&it->second); } return true; } bool erase(int x, int y) { std::pair key(x, y); DynamicExt::iterator it = mDynamicExt.find(key); if (it == mDynamicExt.end()) { return false; } mDynamicExt.erase(it); mSharedExt.erase( mSharedExt.begin() + mSharedExt.size(), mSharedExt.end() ); for (it = mDynamicExt.begin(); it != mDynamicExt.end(); ++it) { mSharedExt.push_back(&it->second); } return true; } }; template <> class Store : public StoreBase { public: typedef std::vector::const_iterator iterator; private: std::vector mStatic; std::vector::iterator mIntBegin, mIntEnd, mExtBegin, mExtEnd; struct IntExtOrdering { bool operator()(const ESM::Pathgrid &x, const ESM::Pathgrid &y) const { // interior pathgrids precedes exterior ones (x < y) if ((x.mData.mX == 0 && x.mData.mY == 0) && (y.mData.mX != 0 || y.mData.mY != 0)) { return true; } return false; } }; struct ExtCompare { bool operator()(const ESM::Pathgrid &x, const ESM::Pathgrid &y) const { if (x.mData.mX == y.mData.mX) { return x.mData.mY < y.mData.mY; } return x.mData.mX < y.mData.mX; } }; public: void load(ESM::ESMReader &esm, const std::string &id) { mStatic.push_back(ESM::Pathgrid()); mStatic.back().load(esm); } size_t getSize() const { return mStatic.size(); } void setUp() { IntExtOrdering cmp; std::sort(mStatic.begin(), mStatic.end(), cmp); ESM::Pathgrid pg; pg.mData.mX = pg.mData.mY = 1; mExtBegin = std::lower_bound(mStatic.begin(), mStatic.end(), pg, cmp); mExtEnd = mStatic.end(); mIntBegin = mStatic.begin(); mIntEnd = mExtBegin; std::sort(mIntBegin, mIntEnd, RecordCmp()); std::sort(mExtBegin, mExtEnd, ExtCompare()); } const ESM::Pathgrid *search(int x, int y) const { ESM::Pathgrid pg; pg.mData.mX = x; pg.mData.mY = y; iterator it = std::lower_bound(mExtBegin, mExtEnd, pg, ExtCompare()); if (it != mExtEnd && it->mData.mX == x && it->mData.mY == y) { return &(*it); } return 0; } const ESM::Pathgrid *find(int x, int y) const { const ESM::Pathgrid *ptr = search(x, y); if (ptr == 0) { std::ostringstream msg; msg << "Pathgrid at (" << x << ", " << y << ") not found"; throw std::runtime_error(msg.str()); } return ptr; } const ESM::Pathgrid *search(const std::string &name) const { ESM::Pathgrid pg; pg.mCell = name; iterator it = std::lower_bound(mIntBegin, mIntEnd, pg, RecordCmp()); if (it != mIntEnd && Misc::StringUtils::ciEqual(it->mCell, name)) { return &(*it); } return 0; } const ESM::Pathgrid *find(const std::string &name) const { const ESM::Pathgrid *ptr = search(name); if (ptr == 0) { std::ostringstream msg; msg << "Pathgrid in cell '" << name << "' not found"; throw std::runtime_error(msg.str()); } return ptr; } const ESM::Pathgrid *search(const ESM::Cell &cell) const { if (cell.mData.mFlags & ESM::Cell::Interior) { return search(cell.mName); } return search(cell.mData.mX, cell.mData.mY); } const ESM::Pathgrid *find(const ESM::Cell &cell) const { if (cell.mData.mFlags & ESM::Cell::Interior) { return find(cell.mName); } return find(cell.mData.mX, cell.mData.mY); } iterator begin() const { return mStatic.begin(); } iterator end() const { return mStatic.end(); } iterator interiorPathsBegin() const { return mIntBegin; } iterator interiorPathsEnd() const { return mIntEnd; } iterator exteriorPathsBegin() const { return mExtBegin; } iterator exteriorPathsEnd() const { return mExtEnd; } }; template class IndexedStore { struct Compare { bool operator()(const T &x, const T &y) const { return x.mIndex < y.mIndex; } }; protected: std::vector mStatic; public: typedef typename std::vector::const_iterator iterator; IndexedStore() {} IndexedStore(unsigned int size) { mStatic.reserve(size); } iterator begin() const { return mStatic.begin(); } iterator end() const { return mStatic.end(); } /// \todo refine loading order void load(ESM::ESMReader &esm) { mStatic.push_back(T()); mStatic.back().load(esm); } int getSize() const { return mStatic.size(); } void setUp() { /// \note This method sorts indexed values for further /// searches. Every loaded item is present in storage, but /// latest loaded shadows any previous while searching. /// If memory cost will be too high, it is possible to remove /// unused values. Compare cmp; std::stable_sort(mStatic.begin(), mStatic.end(), cmp); typename std::vector::iterator first, next; next = first = mStatic.begin(); while (first != mStatic.end() && ++next != mStatic.end()) { while (next != mStatic.end() && !cmp(*first, *next)) { ++next; } if (first != --next) { std::swap(*first, *next); } first = ++next; } } const T *search(int index) const { T item; item.mIndex = index; iterator it = std::lower_bound(mStatic.begin(), mStatic.end(), item, Compare()); if (it != mStatic.end() && it->mIndex == index) { return &(*it); } return 0; } const T *find(int index) const { const T *ptr = search(index); if (ptr == 0) { std::ostringstream msg; msg << "Object with index " << index << " not found"; throw std::runtime_error(msg.str()); } return ptr; } }; template <> struct Store : public IndexedStore { Store() {} Store(unsigned int size) : IndexedStore(size) {} }; template <> struct Store : public IndexedStore { Store() {} Store(unsigned int size) : IndexedStore(size) {} }; template <> class Store : public IndexedStore { std::vector mStatic; public: typedef std::vector::const_iterator iterator; Store() { mStatic.reserve(ESM::Attribute::Length); } const ESM::Attribute *search(size_t index) const { if (index >= mStatic.size()) { return 0; } return &mStatic.at(index); } const ESM::Attribute *find(size_t index) const { const ESM::Attribute *ptr = search(index); if (ptr == 0) { std::ostringstream msg; msg << "Attribute with index " << index << " not found"; throw std::runtime_error(msg.str()); } return ptr; } void setUp() { for (int i = 0; i < ESM::Attribute::Length; ++i) { mStatic.push_back( ESM::Attribute( ESM::Attribute::sAttributeIds[i], ESM::Attribute::sGmstAttributeIds[i], ESM::Attribute::sGmstAttributeDescIds[i] ) ); } } size_t getSize() const { return mStatic.size(); } iterator begin() const { return mStatic.begin(); } iterator end() const { return mStatic.end(); } }; } //end namespace #endif