1 #ifndef OPENMW_MWWORLD_ESMSTORE_H
2 #define OPENMW_MWWORLD_ESMSTORE_H
3 
4 #include <memory>
5 #include <sstream>
6 #include <stdexcept>
7 #include <unordered_map>
8 
9 #include <components/esm/records.hpp>
10 #include "store.hpp"
11 
12 namespace Loading
13 {
14     class Listener;
15 }
16 
17 namespace MWMechanics
18 {
19     class SpellList;
20 }
21 
22 namespace MWWorld
23 {
24     class ESMStore
25     {
26         Store<ESM::Activator>       mActivators;
27         Store<ESM::Potion>          mPotions;
28         Store<ESM::Apparatus>       mAppas;
29         Store<ESM::Armor>           mArmors;
30         Store<ESM::BodyPart>        mBodyParts;
31         Store<ESM::Book>            mBooks;
32         Store<ESM::BirthSign>       mBirthSigns;
33         Store<ESM::Class>           mClasses;
34         Store<ESM::Clothing>        mClothes;
35         Store<ESM::Container>       mContainers;
36         Store<ESM::Creature>        mCreatures;
37         Store<ESM::Dialogue>        mDialogs;
38         Store<ESM::Door>            mDoors;
39         Store<ESM::Enchantment>     mEnchants;
40         Store<ESM::Faction>         mFactions;
41         Store<ESM::Global>          mGlobals;
42         Store<ESM::Ingredient>      mIngreds;
43         Store<ESM::CreatureLevList> mCreatureLists;
44         Store<ESM::ItemLevList>     mItemLists;
45         Store<ESM::Light>           mLights;
46         Store<ESM::Lockpick>        mLockpicks;
47         Store<ESM::Miscellaneous>   mMiscItems;
48         Store<ESM::NPC>             mNpcs;
49         Store<ESM::Probe>           mProbes;
50         Store<ESM::Race>            mRaces;
51         Store<ESM::Region>          mRegions;
52         Store<ESM::Repair>          mRepairs;
53         Store<ESM::SoundGenerator>  mSoundGens;
54         Store<ESM::Sound>           mSounds;
55         Store<ESM::Spell>           mSpells;
56         Store<ESM::StartScript>     mStartScripts;
57         Store<ESM::Static>          mStatics;
58         Store<ESM::Weapon>          mWeapons;
59 
60         Store<ESM::GameSetting>     mGameSettings;
61         Store<ESM::Script>          mScripts;
62 
63         // Lists that need special rules
64         Store<ESM::Cell>        mCells;
65         Store<ESM::Land>        mLands;
66         Store<ESM::LandTexture> mLandTextures;
67         Store<ESM::Pathgrid>    mPathgrids;
68 
69         Store<ESM::MagicEffect> mMagicEffects;
70         Store<ESM::Skill>       mSkills;
71 
72         // Special entry which is hardcoded and not loaded from an ESM
73         Store<ESM::Attribute>   mAttributes;
74 
75         // Lookup of all IDs. Makes looking up references faster. Just
76         // maps the id name to the record type.
77         std::map<std::string, int> mIds;
78         std::map<std::string, int> mStaticIds;
79 
80         std::unordered_map<std::string, int> mRefCount;
81 
82         std::map<int, StoreBase *> mStores;
83 
84         unsigned int mDynamicCount;
85 
86         mutable std::map<std::string, std::weak_ptr<MWMechanics::SpellList> > mSpellListCache;
87 
88         /// Validate entries in store after setup
89         void validate();
90 
91         void countRecords();
92     public:
93         /// \todo replace with SharedIterator<StoreBase>
94         typedef std::map<int, StoreBase *>::const_iterator iterator;
95 
begin() const96         iterator begin() const {
97             return mStores.begin();
98         }
99 
end() const100         iterator end() const {
101             return mStores.end();
102         }
103 
104         /// Look up the given ID in 'all'. Returns 0 if not found.
105         /// \note id must be in lower case.
find(const std::string & id) const106         int find(const std::string &id) const
107         {
108             std::map<std::string, int>::const_iterator it = mIds.find(id);
109             if (it == mIds.end()) {
110                 return 0;
111             }
112             return it->second;
113         }
findStatic(const std::string & id) const114         int findStatic(const std::string &id) const
115         {
116             std::map<std::string, int>::const_iterator it = mStaticIds.find(id);
117             if (it == mStaticIds.end()) {
118                 return 0;
119             }
120             return it->second;
121         }
122 
ESMStore()123         ESMStore()
124           : mDynamicCount(0)
125         {
126             mStores[ESM::REC_ACTI] = &mActivators;
127             mStores[ESM::REC_ALCH] = &mPotions;
128             mStores[ESM::REC_APPA] = &mAppas;
129             mStores[ESM::REC_ARMO] = &mArmors;
130             mStores[ESM::REC_BODY] = &mBodyParts;
131             mStores[ESM::REC_BOOK] = &mBooks;
132             mStores[ESM::REC_BSGN] = &mBirthSigns;
133             mStores[ESM::REC_CELL] = &mCells;
134             mStores[ESM::REC_CLAS] = &mClasses;
135             mStores[ESM::REC_CLOT] = &mClothes;
136             mStores[ESM::REC_CONT] = &mContainers;
137             mStores[ESM::REC_CREA] = &mCreatures;
138             mStores[ESM::REC_DIAL] = &mDialogs;
139             mStores[ESM::REC_DOOR] = &mDoors;
140             mStores[ESM::REC_ENCH] = &mEnchants;
141             mStores[ESM::REC_FACT] = &mFactions;
142             mStores[ESM::REC_GLOB] = &mGlobals;
143             mStores[ESM::REC_GMST] = &mGameSettings;
144             mStores[ESM::REC_INGR] = &mIngreds;
145             mStores[ESM::REC_LAND] = &mLands;
146             mStores[ESM::REC_LEVC] = &mCreatureLists;
147             mStores[ESM::REC_LEVI] = &mItemLists;
148             mStores[ESM::REC_LIGH] = &mLights;
149             mStores[ESM::REC_LOCK] = &mLockpicks;
150             mStores[ESM::REC_LTEX] = &mLandTextures;
151             mStores[ESM::REC_MISC] = &mMiscItems;
152             mStores[ESM::REC_NPC_] = &mNpcs;
153             mStores[ESM::REC_PGRD] = &mPathgrids;
154             mStores[ESM::REC_PROB] = &mProbes;
155             mStores[ESM::REC_RACE] = &mRaces;
156             mStores[ESM::REC_REGN] = &mRegions;
157             mStores[ESM::REC_REPA] = &mRepairs;
158             mStores[ESM::REC_SCPT] = &mScripts;
159             mStores[ESM::REC_SNDG] = &mSoundGens;
160             mStores[ESM::REC_SOUN] = &mSounds;
161             mStores[ESM::REC_SPEL] = &mSpells;
162             mStores[ESM::REC_SSCR] = &mStartScripts;
163             mStores[ESM::REC_STAT] = &mStatics;
164             mStores[ESM::REC_WEAP] = &mWeapons;
165 
166             mPathgrids.setCells(mCells);
167         }
168 
clearDynamic()169         void clearDynamic ()
170         {
171             for (std::map<int, StoreBase *>::iterator it = mStores.begin(); it != mStores.end(); ++it)
172                 it->second->clearDynamic();
173 
174             movePlayerRecord();
175         }
176 
movePlayerRecord()177         void movePlayerRecord ()
178         {
179             auto player = mNpcs.find("player");
180             mNpcs.insert(*player);
181         }
182 
183         /// Validate entries in store after loading a save
184         void validateDynamic();
185 
186         void load(ESM::ESMReader &esm, Loading::Listener* listener);
187 
188         template <class T>
get() const189         const Store<T> &get() const {
190             throw std::runtime_error("Storage for this type not exist");
191         }
192 
193         /// Insert a custom record (i.e. with a generated ID that will not clash will pre-existing records)
194         template <class T>
insert(const T & x)195         const T *insert(const T &x)
196         {
197             const std::string id = "$dynamic" + std::to_string(mDynamicCount++);
198 
199             Store<T> &store = const_cast<Store<T> &>(get<T>());
200             if (store.search(id) != nullptr)
201             {
202                 const std::string msg = "Try to override existing record '" + id + "'";
203                 throw std::runtime_error(msg);
204             }
205             T record = x;
206 
207             record.mId = id;
208 
209             T *ptr = store.insert(record);
210             for (iterator it = mStores.begin(); it != mStores.end(); ++it) {
211                 if (it->second == &store) {
212                     mIds[ptr->mId] = it->first;
213                 }
214             }
215             return ptr;
216         }
217 
218         /// Insert a record with set ID, and allow it to override a pre-existing static record.
219         template <class T>
overrideRecord(const T & x)220         const T *overrideRecord(const T &x) {
221             Store<T> &store = const_cast<Store<T> &>(get<T>());
222 
223             T *ptr = store.insert(x);
224             for (iterator it = mStores.begin(); it != mStores.end(); ++it) {
225                 if (it->second == &store) {
226                     mIds[ptr->mId] = it->first;
227                 }
228             }
229             return ptr;
230         }
231 
232         template <class T>
insertStatic(const T & x)233         const T *insertStatic(const T &x)
234         {
235             const std::string id = "$dynamic" + std::to_string(mDynamicCount++);
236 
237             Store<T> &store = const_cast<Store<T> &>(get<T>());
238             if (store.search(id) != nullptr)
239             {
240                 const std::string msg = "Try to override existing record '" + id + "'";
241                 throw std::runtime_error(msg);
242             }
243             T record = x;
244 
245             T *ptr = store.insertStatic(record);
246             for (iterator it = mStores.begin(); it != mStores.end(); ++it) {
247                 if (it->second == &store) {
248                     mIds[ptr->mId] = it->first;
249                 }
250             }
251             return ptr;
252         }
253 
254         // This method must be called once, after loading all master/plugin files. This can only be done
255         //  from the outside, so it must be public.
256         void setUp(bool validateRecords = false);
257 
258         int countSavedGameRecords() const;
259 
260         void write (ESM::ESMWriter& writer, Loading::Listener& progress) const;
261 
262         bool readRecord (ESM::ESMReader& reader, uint32_t type);
263         ///< \return Known type?
264 
265         // To be called when we are done with dynamic record loading
266         void checkPlayer();
267 
268         /// @return The number of instances defined in the base files. Excludes changes from the save file.
269         int getRefCount(const std::string& id) const;
270 
271         /// Actors with the same ID share spells, abilities, etc.
272         /// @return The shared spell list to use for this actor and whether or not it has already been initialized.
273         std::pair<std::shared_ptr<MWMechanics::SpellList>, bool> getSpellList(const std::string& id) const;
274     };
275 
276     template <>
insert(const ESM::Cell & cell)277     inline const ESM::Cell *ESMStore::insert<ESM::Cell>(const ESM::Cell &cell) {
278         return mCells.insert(cell);
279     }
280 
281     template <>
insert(const ESM::NPC & npc)282     inline const ESM::NPC *ESMStore::insert<ESM::NPC>(const ESM::NPC &npc)
283     {
284         const std::string id = "$dynamic" + std::to_string(mDynamicCount++);
285 
286         if (Misc::StringUtils::ciEqual(npc.mId, "player"))
287         {
288             return mNpcs.insert(npc);
289         }
290         else if (mNpcs.search(id) != nullptr)
291         {
292             const std::string msg = "Try to override existing record '" + id + "'";
293             throw std::runtime_error(msg);
294         }
295         ESM::NPC record = npc;
296 
297         record.mId = id;
298 
299         ESM::NPC *ptr = mNpcs.insert(record);
300         mIds[ptr->mId] = ESM::REC_NPC_;
301         return ptr;
302     }
303 
304     template <>
get() const305     inline const Store<ESM::Activator> &ESMStore::get<ESM::Activator>() const {
306         return mActivators;
307     }
308 
309     template <>
get() const310     inline const Store<ESM::Potion> &ESMStore::get<ESM::Potion>() const {
311         return mPotions;
312     }
313 
314     template <>
get() const315     inline const Store<ESM::Apparatus> &ESMStore::get<ESM::Apparatus>() const {
316         return mAppas;
317     }
318 
319     template <>
get() const320     inline const Store<ESM::Armor> &ESMStore::get<ESM::Armor>() const {
321         return mArmors;
322     }
323 
324     template <>
get() const325     inline const Store<ESM::BodyPart> &ESMStore::get<ESM::BodyPart>() const {
326         return mBodyParts;
327     }
328 
329     template <>
get() const330     inline const Store<ESM::Book> &ESMStore::get<ESM::Book>() const {
331         return mBooks;
332     }
333 
334     template <>
get() const335     inline const Store<ESM::BirthSign> &ESMStore::get<ESM::BirthSign>() const {
336         return mBirthSigns;
337     }
338 
339     template <>
get() const340     inline const Store<ESM::Class> &ESMStore::get<ESM::Class>() const {
341         return mClasses;
342     }
343 
344     template <>
get() const345     inline const Store<ESM::Clothing> &ESMStore::get<ESM::Clothing>() const {
346         return mClothes;
347     }
348 
349     template <>
get() const350     inline const Store<ESM::Container> &ESMStore::get<ESM::Container>() const {
351         return mContainers;
352     }
353 
354     template <>
get() const355     inline const Store<ESM::Creature> &ESMStore::get<ESM::Creature>() const {
356         return mCreatures;
357     }
358 
359     template <>
get() const360     inline const Store<ESM::Dialogue> &ESMStore::get<ESM::Dialogue>() const {
361         return mDialogs;
362     }
363 
364     template <>
get() const365     inline const Store<ESM::Door> &ESMStore::get<ESM::Door>() const {
366         return mDoors;
367     }
368 
369     template <>
get() const370     inline const Store<ESM::Enchantment> &ESMStore::get<ESM::Enchantment>() const {
371         return mEnchants;
372     }
373 
374     template <>
get() const375     inline const Store<ESM::Faction> &ESMStore::get<ESM::Faction>() const {
376         return mFactions;
377     }
378 
379     template <>
get() const380     inline const Store<ESM::Global> &ESMStore::get<ESM::Global>() const {
381         return mGlobals;
382     }
383 
384     template <>
get() const385     inline const Store<ESM::Ingredient> &ESMStore::get<ESM::Ingredient>() const {
386         return mIngreds;
387     }
388 
389     template <>
get() const390     inline const Store<ESM::CreatureLevList> &ESMStore::get<ESM::CreatureLevList>() const {
391         return mCreatureLists;
392     }
393 
394     template <>
get() const395     inline const Store<ESM::ItemLevList> &ESMStore::get<ESM::ItemLevList>() const {
396         return mItemLists;
397     }
398 
399     template <>
get() const400     inline const Store<ESM::Light> &ESMStore::get<ESM::Light>() const {
401         return mLights;
402     }
403 
404     template <>
get() const405     inline const Store<ESM::Lockpick> &ESMStore::get<ESM::Lockpick>() const {
406         return mLockpicks;
407     }
408 
409     template <>
get() const410     inline const Store<ESM::Miscellaneous> &ESMStore::get<ESM::Miscellaneous>() const {
411         return mMiscItems;
412     }
413 
414     template <>
get() const415     inline const Store<ESM::NPC> &ESMStore::get<ESM::NPC>() const {
416         return mNpcs;
417     }
418 
419     template <>
get() const420     inline const Store<ESM::Probe> &ESMStore::get<ESM::Probe>() const {
421         return mProbes;
422     }
423 
424     template <>
get() const425     inline const Store<ESM::Race> &ESMStore::get<ESM::Race>() const {
426         return mRaces;
427     }
428 
429     template <>
get() const430     inline const Store<ESM::Region> &ESMStore::get<ESM::Region>() const {
431         return mRegions;
432     }
433 
434     template <>
get() const435     inline const Store<ESM::Repair> &ESMStore::get<ESM::Repair>() const {
436         return mRepairs;
437     }
438 
439     template <>
get() const440     inline const Store<ESM::SoundGenerator> &ESMStore::get<ESM::SoundGenerator>() const {
441         return mSoundGens;
442     }
443 
444     template <>
get() const445     inline const Store<ESM::Sound> &ESMStore::get<ESM::Sound>() const {
446         return mSounds;
447     }
448 
449     template <>
get() const450     inline const Store<ESM::Spell> &ESMStore::get<ESM::Spell>() const {
451         return mSpells;
452     }
453 
454     template <>
get() const455     inline const Store<ESM::StartScript> &ESMStore::get<ESM::StartScript>() const {
456         return mStartScripts;
457     }
458 
459     template <>
get() const460     inline const Store<ESM::Static> &ESMStore::get<ESM::Static>() const {
461         return mStatics;
462     }
463 
464     template <>
get() const465     inline const Store<ESM::Weapon> &ESMStore::get<ESM::Weapon>() const {
466         return mWeapons;
467     }
468 
469     template <>
get() const470     inline const Store<ESM::GameSetting> &ESMStore::get<ESM::GameSetting>() const {
471         return mGameSettings;
472     }
473 
474     template <>
get() const475     inline const Store<ESM::Script> &ESMStore::get<ESM::Script>() const {
476         return mScripts;
477     }
478 
479     template <>
get() const480     inline const Store<ESM::Cell> &ESMStore::get<ESM::Cell>() const {
481         return mCells;
482     }
483 
484     template <>
get() const485     inline const Store<ESM::Land> &ESMStore::get<ESM::Land>() const {
486         return mLands;
487     }
488 
489     template <>
get() const490     inline const Store<ESM::LandTexture> &ESMStore::get<ESM::LandTexture>() const {
491         return mLandTextures;
492     }
493 
494     template <>
get() const495     inline const Store<ESM::Pathgrid> &ESMStore::get<ESM::Pathgrid>() const {
496         return mPathgrids;
497     }
498 
499     template <>
get() const500     inline const Store<ESM::MagicEffect> &ESMStore::get<ESM::MagicEffect>() const {
501         return mMagicEffects;
502     }
503 
504     template <>
get() const505     inline const Store<ESM::Skill> &ESMStore::get<ESM::Skill>() const {
506         return mSkills;
507     }
508 
509     template <>
get() const510     inline const Store<ESM::Attribute> &ESMStore::get<ESM::Attribute>() const {
511         return mAttributes;
512     }
513 }
514 
515 #endif
516