#include "store.hpp" #include #include #include #include #include #include #include namespace { template 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); } }; 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; } bool operator()(const ESM::Land *x, const std::pair& y) { if (x->mX == y.first) { return x->mY < y.second; } return x->mX < y.first; } }; } namespace MWWorld { RecordId::RecordId(const std::string &id, bool isDeleted) : mId(id), mIsDeleted(isDeleted) {} template IndexedStore::IndexedStore() { } template typename IndexedStore::iterator IndexedStore::begin() const { return mStatic.begin(); } template typename IndexedStore::iterator IndexedStore::end() const { return mStatic.end(); } template void IndexedStore::load(ESM::ESMReader &esm) { T record; bool isDeleted = false; record.load(esm, isDeleted); // Try to overwrite existing record std::pair ret = mStatic.insert(std::make_pair(record.mIndex, record)); if (!ret.second) ret.first->second = record; } template int IndexedStore::getSize() const { return mStatic.size(); } template void IndexedStore::setUp() { } template const T *IndexedStore::search(int index) const { typename Static::const_iterator it = mStatic.find(index); if (it != mStatic.end()) return &(it->second); return NULL; } template const T *IndexedStore::find(int index) const { const T *ptr = search(index); if (ptr == 0) { std::ostringstream msg; msg << T::getRecordType() << " with index " << index << " not found"; throw std::runtime_error(msg.str()); } return ptr; } // Need to instantiate these before they're used template class IndexedStore; template class IndexedStore; template Store::Store() { } template Store::Store(const Store& orig) : mStatic(orig.mStatic) { } template void Store::clearDynamic() { // remove the dynamic part of mShared assert(mShared.size() >= mStatic.size()); mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); mDynamic.clear(); } template const T *Store::search(const std::string &id) const { std::string idLower = Misc::StringUtils::lowerCase(id); typename Dynamic::const_iterator dit = mDynamic.find(idLower); if (dit != mDynamic.end()) { return &dit->second; } typename std::map::const_iterator it = mStatic.find(idLower); if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) { return &(it->second); } return 0; } template bool Store::isDynamic(const std::string &id) const { typename Dynamic::const_iterator dit = mDynamic.find(id); return (dit != mDynamic.end()); } template const T *Store::searchRandom(const std::string &id) const { std::vector results; std::for_each(mShared.begin(), mShared.end(), GetRecords(id, &results)); if(!results.empty()) return results[Misc::Rng::rollDice(results.size())]; return NULL; } template const T *Store::find(const std::string &id) const { const T *ptr = search(id); if (ptr == 0) { std::ostringstream msg; msg << T::getRecordType() << " '" << id << "' not found"; throw std::runtime_error(msg.str()); } return ptr; } template const T *Store::findRandom(const std::string &id) const { const T *ptr = searchRandom(id); if(ptr == 0) { std::ostringstream msg; msg << T::getRecordType() << " starting with '"< RecordId Store::load(ESM::ESMReader &esm) { T record; bool isDeleted = false; record.load(esm, isDeleted); Misc::StringUtils::lowerCaseInPlace(record.mId); std::pair inserted = mStatic.insert(std::make_pair(record.mId, record)); if (inserted.second) mShared.push_back(&inserted.first->second); else inserted.first->second = record; return RecordId(record.mId, isDeleted); } template void Store::setUp() { } template typename Store::iterator Store::begin() const { return mShared.begin(); } template typename Store::iterator Store::end() const { return mShared.end(); } template size_t Store::getSize() const { return mShared.size(); } template int Store::getDynamicSize() const { return mDynamic.size(); } template void Store::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); } } template T *Store::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; } template T *Store::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; } template bool Store::eraseStatic(const std::string &id) { std::string idLower = Misc::StringUtils::lowerCase(id); typename std::map::iterator it = mStatic.find(idLower); 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 == idLower) { mShared.erase(sharedIter); break; } ++sharedIter; } mStatic.erase(it); } return true; } template bool Store::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 assert(mShared.size() >= mStatic.size()); mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); for (it = mDynamic.begin(); it != mDynamic.end(); ++it) { mShared.push_back(&it->second); } return true; } template bool Store::erase(const T &item) { return erase(item.mId); } template void Store::write (ESM::ESMWriter& writer, Loading::Listener& progress) const { for (typename Dynamic::const_iterator iter (mDynamic.begin()); iter!=mDynamic.end(); ++iter) { writer.startRecord (T::sRecordId); iter->second.save (writer); writer.endRecord (T::sRecordId); } } template RecordId Store::read(ESM::ESMReader& reader) { T record; bool isDeleted = false; record.load (reader, isDeleted); insert (record); return RecordId(record.mId, isDeleted); } // LandTexture //========================================================================= Store::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); } const ESM::LandTexture *Store::search(size_t index, size_t plugin) const { assert(plugin < mStatic.size()); const LandTextureList <exl = mStatic[plugin]; if (index >= ltexl.size()) return NULL; return <exl[index]; } const ESM::LandTexture *Store::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 Store::getSize() const { return mStatic.size(); } size_t Store::getSize(size_t plugin) const { assert(plugin < mStatic.size()); return mStatic[plugin].size(); } RecordId Store::load(ESM::ESMReader &esm, size_t plugin) { ESM::LandTexture lt; bool isDeleted = false; lt.load(esm, isDeleted); assert(plugin < mStatic.size()); LandTextureList <exl = mStatic[plugin]; if(lt.mIndex + 1 > (int)ltexl.size()) ltexl.resize(lt.mIndex+1); // Store it ltexl[lt.mIndex] = lt; return RecordId(lt.mId, isDeleted); } RecordId Store::load(ESM::ESMReader &esm) { return load(esm, esm.getIndex()); } Store::iterator Store::begin(size_t plugin) const { assert(plugin < mStatic.size()); return mStatic[plugin].begin(); } Store::iterator Store::end(size_t plugin) const { assert(plugin < mStatic.size()); return mStatic[plugin].end(); } void Store::resize(size_t num) { if (mStatic.size() < num) mStatic.resize(num); } // Land //========================================================================= Store::~Store() { for (std::vector::const_iterator it = mStatic.begin(); it != mStatic.end(); ++it) { delete *it; } } size_t Store::getSize() const { return mStatic.size(); } Store::iterator Store::begin() const { return iterator(mStatic.begin()); } Store::iterator Store::end() const { return iterator(mStatic.end()); } const ESM::Land *Store::search(int x, int y) const { std::pair comp(x,y); std::vector::const_iterator it = std::lower_bound(mStatic.begin(), mStatic.end(), comp, Compare()); if (it != mStatic.end() && (*it)->mX == x && (*it)->mY == y) { return *it; } return 0; } const ESM::Land *Store::find(int x, int y) const { 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; } RecordId Store::load(ESM::ESMReader &esm) { ESM::Land *ptr = new ESM::Land(); bool isDeleted = false; ptr->load(esm, isDeleted); // 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); return RecordId("", isDeleted); } void Store::setUp() { std::sort(mStatic.begin(), mStatic.end(), Compare()); } // Cell //========================================================================= const ESM::Cell *Store::search(const ESM::Cell &cell) const { if (cell.isExterior()) { return search(cell.getGridX(), cell.getGridY()); } return search(cell.mName); } void Store::handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell) { //Handling MovedCellRefs, there is no way to do it inside loadcell while (esm.isNextSub("MVRF")) { ESM::CellRef ref; ESM::MovedCellRef cMRef; cell->getNextMVRF(esm, cMRef); ESM::Cell *cellAlt = const_cast(searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1])); // Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following // implementation when the oher implementation works as well. bool deleted = false; cell->getNextRef(esm, ref, deleted); // Add data required to make reference appear in the correct cell. // We should not need to test for duplicates, as this part of the code is pre-cell merge. cell->mMovedRefs.push_back(cMRef); // But there may be duplicates here! ESM::CellRefTracker::iterator iter = std::find_if(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ESM::CellRefTrackerPredicate(ref.mRefNum)); if (iter == cellAlt->mLeasedRefs.end()) cellAlt->mLeasedRefs.push_back(std::make_pair(ref, deleted)); else *iter = std::make_pair(ref, deleted); } } const ESM::Cell *Store::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 *Store::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 *Store::searchOrCreate(int x, int 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; newCell.mData.mX = x; newCell.mData.mY = y; newCell.mData.mFlags = ESM::Cell::HasWater; newCell.mAmbi.mAmbient = 0; newCell.mAmbi.mSunlight = 0; newCell.mAmbi.mFog = 0; newCell.mAmbi.mFogDensity = 0; return &mExt.insert(std::make_pair(key, newCell)).first->second; } const ESM::Cell *Store::find(const std::string &id) const { const ESM::Cell *ptr = search(id); if (ptr == 0) { std::ostringstream msg; msg << "Cell '" << id << "' not found"; throw std::runtime_error(msg.str()); } return ptr; } const ESM::Cell *Store::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 Store::clearDynamic() { setUp(); } void Store::setUp() { typedef DynamicExt::iterator ExtIterator; typedef std::map::iterator IntIterator; mSharedInt.clear(); mSharedInt.reserve(mInt.size()); for (IntIterator it = mInt.begin(); it != mInt.end(); ++it) { mSharedInt.push_back(&(it->second)); } mSharedExt.clear(); mSharedExt.reserve(mExt.size()); for (ExtIterator it = mExt.begin(); it != mExt.end(); ++it) { mSharedExt.push_back(&(it->second)); } } RecordId Store::load(ESM::ESMReader &esm) { // Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell, // and we merge all this data into one Cell object. However, we can't simply search for the cell id, // as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they // are not available until both cells have been loaded at least partially! // All cells have a name record, even nameless exterior cells. ESM::Cell cell; bool isDeleted = false; // Load the (x,y) coordinates of the cell, if it is an exterior cell, // so we can find the cell we need to merge with cell.loadNameAndData(esm, isDeleted); std::string idLower = Misc::StringUtils::lowerCase(cell.mName); if(cell.mData.mFlags & ESM::Cell::Interior) { // Store interior cell by name, try to merge with existing parent data. ESM::Cell *oldcell = const_cast(search(idLower)); if (oldcell) { // merge new cell into old cell // push the new references on the list of references to manage (saveContext = true) oldcell->mData = cell.mData; oldcell->mName = cell.mName; // merge name just to be sure (ID will be the same, but case could have been changed) oldcell->loadCell(esm, true); } else { // spawn a new cell cell.loadCell(esm, true); mInt[idLower] = cell; } } else { // Store exterior cells by grid position, try to merge with existing parent data. ESM::Cell *oldcell = const_cast(search(cell.getGridX(), cell.getGridY())); if (oldcell) { // merge new cell into old cell oldcell->mData = cell.mData; oldcell->mName = cell.mName; oldcell->loadCell(esm, false); // handle moved ref (MVRF) subrecords handleMovedCellRefs (esm, &cell); // push the new references on the list of references to manage oldcell->postLoad(esm); // merge lists of leased references, use newer data in case of conflict for (ESM::MovedCellRefTracker::const_iterator it = cell.mMovedRefs.begin(); it != cell.mMovedRefs.end(); ++it) { // remove reference from current leased ref tracker and add it to new cell ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefNum); if (itold != oldcell->mMovedRefs.end()) { if (it->mTarget[0] != itold->mTarget[0] || it->mTarget[1] != itold->mTarget[1]) { ESM::Cell *wipecell = const_cast(search(itold->mTarget[0], itold->mTarget[1])); ESM::CellRefTracker::iterator it_lease = std::find_if(wipecell->mLeasedRefs.begin(), wipecell->mLeasedRefs.end(), ESM::CellRefTrackerPredicate(it->mRefNum)); if (it_lease != wipecell->mLeasedRefs.end()) wipecell->mLeasedRefs.erase(it_lease); else std::cerr << "Error: can't find " << it->mRefNum.mIndex << " " << it->mRefNum.mContentFile << " in leasedRefs " << std::endl; } *itold = *it; } else oldcell->mMovedRefs.push_back(*it); } // We don't need to merge mLeasedRefs of cell / oldcell. This list is filled when another cell moves a // reference to this cell, so the list for the new cell should be empty. The list for oldcell, // however, could have leased refs in it and so should be kept. } else { // spawn a new cell cell.loadCell(esm, false); // handle moved ref (MVRF) subrecords handleMovedCellRefs (esm, &cell); // push the new references on the list of references to manage cell.postLoad(esm); mExt[std::make_pair(cell.mData.mX, cell.mData.mY)] = cell; } } return RecordId(cell.mName, isDeleted); } Store::iterator Store::intBegin() const { return iterator(mSharedInt.begin()); } Store::iterator Store::intEnd() const { return iterator(mSharedInt.end()); } Store::iterator Store::extBegin() const { return iterator(mSharedExt.begin()); } Store::iterator Store::extEnd() const { return iterator(mSharedExt.end()); } const ESM::Cell *Store::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; } const ESM::Cell *Store::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 Store::getSize() const { return mSharedInt.size() + mSharedExt.size(); } void Store::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 *Store::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 Store::erase(const ESM::Cell &cell) { if (cell.isExterior()) { return erase(cell.getGridX(), cell.getGridY()); } return erase(cell.mName); } bool Store::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 Store::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; } // Pathgrid //========================================================================= Store::Store() : mCells(NULL) { } void Store::setCells(Store& cells) { mCells = &cells; } RecordId Store::load(ESM::ESMReader &esm) { ESM::Pathgrid pathgrid; bool isDeleted = false; pathgrid.load(esm, isDeleted); // Unfortunately the Pathgrid record model does not specify whether the pathgrid belongs to an interior or exterior cell. // For interior cells, mCell is the cell name, but for exterior cells it is either the cell name or if that doesn't exist, the cell's region name. // mX and mY will be (0,0) for interior cells, but there is also an exterior cell with the coordinates of (0,0), so that doesn't help. // Check whether mCell is an interior cell. This isn't perfect, will break if a Region with the same name as an interior cell is created. // A proper fix should be made for future versions of the file format. bool interior = mCells->search(pathgrid.mCell) != NULL; // Try to overwrite existing record if (interior) { std::pair ret = mInt.insert(std::make_pair(pathgrid.mCell, pathgrid)); if (!ret.second) ret.first->second = pathgrid; } else { std::pair ret = mExt.insert(std::make_pair(std::make_pair(pathgrid.mData.mX, pathgrid.mData.mY), pathgrid)); if (!ret.second) ret.first->second = pathgrid; } return RecordId("", isDeleted); } size_t Store::getSize() const { return mInt.size() + mExt.size(); } void Store::setUp() { } const ESM::Pathgrid *Store::search(int x, int y) const { Exterior::const_iterator it = mExt.find(std::make_pair(x,y)); if (it != mExt.end()) return &(it->second); return NULL; } const ESM::Pathgrid *Store::search(const std::string& name) const { Interior::const_iterator it = mInt.find(name); if (it != mInt.end()) return &(it->second); return NULL; } const ESM::Pathgrid *Store::find(int x, int y) const { const ESM::Pathgrid* pathgrid = search(x,y); if (!pathgrid) { std::ostringstream msg; msg << "Pathgrid in cell '" << x << " " << y << "' not found"; throw std::runtime_error(msg.str()); } return pathgrid; } const ESM::Pathgrid* Store::find(const std::string& name) const { const ESM::Pathgrid* pathgrid = search(name); if (!pathgrid) { std::ostringstream msg; msg << "Pathgrid in cell '" << name << "' not found"; throw std::runtime_error(msg.str()); } return pathgrid; } const ESM::Pathgrid *Store::search(const ESM::Cell &cell) const { if (!(cell.mData.mFlags & ESM::Cell::Interior)) return search(cell.mData.mX, cell.mData.mY); else return search(cell.mName); } const ESM::Pathgrid *Store::find(const ESM::Cell &cell) const { if (!(cell.mData.mFlags & ESM::Cell::Interior)) return find(cell.mData.mX, cell.mData.mY); else return find(cell.mName); } // Skill //========================================================================= Store::Store() { } // Magic effect //========================================================================= Store::Store() { } // Attribute //========================================================================= Store::Store() { mStatic.reserve(ESM::Attribute::Length); } const ESM::Attribute *Store::search(size_t index) const { if (index >= mStatic.size()) { return 0; } return &mStatic.at(index); } const ESM::Attribute *Store::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 Store::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 Store::getSize() const { return mStatic.size(); } Store::iterator Store::begin() const { return mStatic.begin(); } Store::iterator Store::end() const { return mStatic.end(); } // Dialogue //========================================================================= template<> void Store::setUp() { // DialInfos marked as deleted are kept during the loading phase, so that the linked list // structure is kept intact for inserting further INFOs. Delete them now that loading is done. for (Static::iterator it = mStatic.begin(); it != mStatic.end(); ++it) { ESM::Dialogue& dial = it->second; dial.clearDeletedInfos(); } mShared.clear(); mShared.reserve(mStatic.size()); std::map::iterator it = mStatic.begin(); for (; it != mStatic.end(); ++it) { mShared.push_back(&(it->second)); } } template<> void Store::setUp() { // Load default marker definitions, if game files do not have them for some reason std::pair markers[] = { std::make_pair("divinemarker", "marker_divine.nif"), std::make_pair("doormarker", "marker_arrow.nif"), std::make_pair("northmarker", "marker_north.nif"), std::make_pair("templemarker", "marker_temple.nif"), std::make_pair("travelmarker", "marker_travel.nif") }; for (const std::pair marker : markers) { if (search(marker.first) == 0) { ESM::Static newMarker = ESM::Static(marker.first, marker.second); std::pair ret = mStatic.insert(std::make_pair(marker.first, newMarker)); if (ret.first != mStatic.end()) { mShared.push_back(&ret.first->second); } } } } template <> inline RecordId Store::load(ESM::ESMReader &esm) { // The original letter case of a dialogue ID is saved, because it's printed ESM::Dialogue dialogue; bool isDeleted = false; dialogue.loadId(esm); std::string idLower = Misc::StringUtils::lowerCase(dialogue.mId); std::map::iterator found = mStatic.find(idLower); if (found == mStatic.end()) { dialogue.loadData(esm, isDeleted); mStatic.insert(std::make_pair(idLower, dialogue)); } else { found->second.loadData(esm, isDeleted); dialogue = found->second; } return RecordId(dialogue.mId, isDeleted); } } template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; //template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; //template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; //template class MWWorld::Store; //template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; //template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; //template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; //template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store; template class MWWorld::Store;