2012-11-05 18:09:14 +04:00
|
|
|
#ifndef OPENMW_MWWORLD_ESMSTORE_H
|
|
|
|
#define OPENMW_MWWORLD_ESMSTORE_H
|
2012-10-01 19:17:04 +04:00
|
|
|
|
2022-06-19 13:28:33 +02:00
|
|
|
#include <filesystem>
|
2020-07-28 08:33:28 +02:00
|
|
|
#include <memory>
|
2012-11-05 18:09:14 +04:00
|
|
|
#include <stdexcept>
|
2022-09-05 18:38:59 +02:00
|
|
|
#include <tuple>
|
2022-06-19 13:28:33 +02:00
|
|
|
#include <unordered_map>
|
2012-10-01 19:17:04 +04:00
|
|
|
|
2021-09-26 15:21:46 +02:00
|
|
|
#include <components/esm/luascripts.hpp>
|
2022-09-05 19:35:15 +02:00
|
|
|
#include <components/esm3/loadgmst.hpp>
|
2022-09-08 15:03:01 +03:00
|
|
|
#include <components/misc/tuplemeta.hpp>
|
2022-09-05 17:57:13 +02:00
|
|
|
|
2012-11-05 18:09:14 +04:00
|
|
|
#include "store.hpp"
|
2012-10-01 19:17:04 +04:00
|
|
|
|
2013-08-27 15:48:13 +02:00
|
|
|
namespace Loading
|
|
|
|
{
|
|
|
|
class Listener;
|
|
|
|
}
|
|
|
|
|
2020-07-28 08:33:28 +02:00
|
|
|
namespace MWMechanics
|
|
|
|
{
|
|
|
|
class SpellList;
|
|
|
|
}
|
|
|
|
|
2022-06-01 22:53:18 +02:00
|
|
|
namespace ESM
|
|
|
|
{
|
|
|
|
class ReadersCache;
|
2022-09-05 19:35:15 +02:00
|
|
|
struct Activator;
|
|
|
|
struct Potion;
|
|
|
|
struct Apparatus;
|
|
|
|
struct Armor;
|
|
|
|
struct BodyPart;
|
|
|
|
struct Book;
|
|
|
|
struct BirthSign;
|
|
|
|
struct Class;
|
|
|
|
struct Clothing;
|
|
|
|
struct Container;
|
|
|
|
struct Creature;
|
|
|
|
struct Dialogue;
|
|
|
|
struct Door;
|
|
|
|
struct Enchantment;
|
|
|
|
struct Faction;
|
|
|
|
struct Global;
|
|
|
|
struct Ingredient;
|
|
|
|
struct CreatureLevList;
|
|
|
|
struct ItemLevList;
|
|
|
|
struct Light;
|
|
|
|
struct Lockpick;
|
|
|
|
struct Miscellaneous;
|
|
|
|
struct NPC;
|
|
|
|
struct Probe;
|
|
|
|
struct Race;
|
|
|
|
struct Region;
|
|
|
|
struct Repair;
|
|
|
|
struct SoundGenerator;
|
|
|
|
struct Sound;
|
|
|
|
struct Spell;
|
|
|
|
struct StartScript;
|
|
|
|
struct Static;
|
|
|
|
struct Weapon;
|
|
|
|
struct GameSetting;
|
|
|
|
class Script;
|
|
|
|
struct Cell;
|
|
|
|
struct Land;
|
|
|
|
struct LandTexture;
|
|
|
|
struct Pathgrid;
|
|
|
|
struct MagicEffect;
|
|
|
|
struct Skill;
|
|
|
|
struct Attribute;
|
2022-06-01 22:53:18 +02:00
|
|
|
}
|
|
|
|
|
2012-12-23 23:23:24 +04:00
|
|
|
namespace MWWorld
|
2012-10-01 19:17:04 +04:00
|
|
|
{
|
2022-07-03 11:47:46 +02:00
|
|
|
struct ESMStoreImp;
|
|
|
|
|
2012-11-05 18:09:14 +04:00
|
|
|
class ESMStore
|
2012-10-01 19:17:04 +04:00
|
|
|
{
|
2022-07-03 13:17:55 +02:00
|
|
|
friend struct ESMStoreImp; // This allows StoreImp to extend esmstore without beeing included everywhere
|
2022-09-22 21:26:05 +03:00
|
|
|
|
2022-09-05 18:38:59 +02:00
|
|
|
using StoreTuple = std::tuple<Store<ESM::Activator>, Store<ESM::Potion>, Store<ESM::Apparatus>,
|
|
|
|
Store<ESM::Armor>, Store<ESM::BodyPart>, Store<ESM::Book>, Store<ESM::BirthSign>, Store<ESM::Class>,
|
|
|
|
Store<ESM::Clothing>, Store<ESM::Container>, Store<ESM::Creature>, Store<ESM::Dialogue>, Store<ESM::Door>,
|
|
|
|
Store<ESM::Enchantment>, Store<ESM::Faction>, Store<ESM::Global>, Store<ESM::Ingredient>,
|
|
|
|
Store<ESM::CreatureLevList>, Store<ESM::ItemLevList>, Store<ESM::Light>, Store<ESM::Lockpick>,
|
|
|
|
Store<ESM::Miscellaneous>, Store<ESM::NPC>, Store<ESM::Probe>, Store<ESM::Race>, Store<ESM::Region>,
|
|
|
|
Store<ESM::Repair>, Store<ESM::SoundGenerator>, Store<ESM::Sound>, Store<ESM::Spell>,
|
|
|
|
Store<ESM::StartScript>, Store<ESM::Static>, Store<ESM::Weapon>, Store<ESM::GameSetting>,
|
|
|
|
Store<ESM::Script>,
|
|
|
|
|
|
|
|
// Lists that need special rules
|
|
|
|
Store<ESM::Cell>, Store<ESM::Land>, Store<ESM::LandTexture>, Store<ESM::Pathgrid>,
|
|
|
|
|
|
|
|
Store<ESM::MagicEffect>, Store<ESM::Skill>,
|
|
|
|
|
|
|
|
// Special entry which is hardcoded and not loaded from an ESM
|
|
|
|
Store<ESM::Attribute>>;
|
|
|
|
|
2022-09-03 11:08:23 +02:00
|
|
|
template <typename T>
|
2022-09-08 00:31:29 +03:00
|
|
|
static constexpr std::size_t getTypeIndex()
|
2022-09-03 11:08:23 +02:00
|
|
|
{
|
2022-09-08 00:13:50 +03:00
|
|
|
static_assert(Misc::TupleHasType<Store<T>, StoreTuple>::value);
|
2022-09-08 00:31:29 +03:00
|
|
|
return Misc::TupleTypeIndex<Store<T>, StoreTuple>::value;
|
2022-09-03 11:08:23 +02:00
|
|
|
}
|
|
|
|
|
2022-07-19 17:58:21 +02:00
|
|
|
std::unique_ptr<ESMStoreImp> mStoreImp;
|
2012-11-05 18:09:14 +04:00
|
|
|
|
2021-11-02 13:46:41 +00:00
|
|
|
std::unordered_map<std::string, int> mRefCount;
|
2020-07-26 11:07:18 +02:00
|
|
|
|
2022-09-03 11:08:23 +02:00
|
|
|
std::vector<StoreBase*> mStores;
|
|
|
|
std::vector<DynamicStore*> mDynamicStores;
|
2012-11-05 18:09:14 +04:00
|
|
|
|
2012-11-06 17:51:38 +04:00
|
|
|
unsigned int mDynamicCount;
|
|
|
|
|
2021-10-30 17:27:57 +00:00
|
|
|
mutable std::unordered_map<std::string, std::weak_ptr<MWMechanics::SpellList>, Misc::StringUtils::CiHash,
|
|
|
|
Misc::StringUtils::CiEqual>
|
|
|
|
mSpellListCache;
|
2020-07-28 08:33:28 +02:00
|
|
|
|
2022-07-03 18:11:20 +02:00
|
|
|
template <class T>
|
2022-09-03 11:08:23 +02:00
|
|
|
Store<T>& getWritable()
|
|
|
|
{
|
|
|
|
return static_cast<Store<T>&>(*mStores[getTypeIndex<T>()]);
|
|
|
|
}
|
2022-07-03 18:11:20 +02:00
|
|
|
|
2018-06-09 20:47:17 +04:00
|
|
|
/// Validate entries in store after setup
|
|
|
|
void validate();
|
|
|
|
|
2022-09-30 12:16:45 +02:00
|
|
|
void countAllCellRefsAndMarkKeys(ESM::ReadersCache& readers);
|
2021-07-12 20:51:13 +02:00
|
|
|
|
|
|
|
template <class T>
|
|
|
|
void removeMissingObjects(Store<T>& store);
|
2021-09-26 15:21:46 +02:00
|
|
|
|
2022-09-07 18:45:15 +02:00
|
|
|
void setIdType(const std::string& id, ESM::RecNameInts type);
|
2022-09-06 14:19:55 +02:00
|
|
|
|
2021-09-26 15:21:46 +02:00
|
|
|
using LuaContent = std::variant<ESM::LuaScriptsCfg, // data from an omwaddon
|
2022-06-19 13:28:33 +02:00
|
|
|
std::filesystem::path>; // path to an omwscripts file
|
2021-09-26 15:21:46 +02:00
|
|
|
std::vector<LuaContent> mLuaContent;
|
|
|
|
|
2012-11-05 18:09:14 +04:00
|
|
|
public:
|
2022-06-19 13:28:33 +02:00
|
|
|
void addOMWScripts(std::filesystem::path filePath) { mLuaContent.push_back(std::move(filePath)); }
|
2021-09-26 15:21:46 +02:00
|
|
|
ESM::LuaScriptsCfg getLuaScriptsCfg() const;
|
|
|
|
|
2012-11-06 00:34:11 +04:00
|
|
|
/// \todo replace with SharedIterator<StoreBase>
|
2022-09-03 11:08:23 +02:00
|
|
|
typedef std::vector<DynamicStore*>::const_iterator iterator;
|
2012-11-06 00:34:11 +04:00
|
|
|
|
2022-09-03 11:08:23 +02:00
|
|
|
iterator begin() const { return mDynamicStores.begin(); }
|
2012-11-06 00:34:11 +04:00
|
|
|
|
2022-09-03 11:08:23 +02:00
|
|
|
iterator end() const { return mDynamicStores.end(); }
|
2012-11-06 00:34:11 +04:00
|
|
|
|
2014-05-22 15:29:36 +02:00
|
|
|
/// Look up the given ID in 'all'. Returns 0 if not found.
|
2022-09-06 23:10:58 +02:00
|
|
|
int find(const std::string_view id) const;
|
2022-07-20 19:28:06 +02:00
|
|
|
|
2022-09-06 23:10:58 +02:00
|
|
|
int findStatic(const std::string_view id) const;
|
2022-07-20 19:28:06 +02:00
|
|
|
|
2022-07-03 11:47:46 +02:00
|
|
|
ESMStore();
|
|
|
|
~ESMStore();
|
2012-11-05 18:09:14 +04:00
|
|
|
|
2022-07-03 14:40:37 +02:00
|
|
|
void clearDynamic();
|
2013-05-15 17:54:18 +02:00
|
|
|
|
2022-07-03 11:47:46 +02:00
|
|
|
void movePlayerRecord();
|
2013-05-15 17:54:18 +02:00
|
|
|
|
2021-03-23 21:07:57 +01:00
|
|
|
/// Validate entries in store after loading a save
|
|
|
|
void validateDynamic();
|
|
|
|
|
2022-04-16 16:28:39 +02:00
|
|
|
void load(ESM::ESMReader& esm, Loading::Listener* listener, ESM::Dialogue*& dialogue);
|
2012-11-05 18:09:14 +04:00
|
|
|
|
|
|
|
template <class T>
|
2022-09-03 11:08:23 +02:00
|
|
|
const Store<T>& get() const
|
|
|
|
{
|
|
|
|
return static_cast<const Store<T>&>(*mStores[getTypeIndex<T>()]);
|
|
|
|
}
|
2012-11-06 17:51:38 +04:00
|
|
|
|
2014-12-17 01:05:32 +01:00
|
|
|
/// Insert a custom record (i.e. with a generated ID that will not clash will pre-existing records)
|
2012-11-08 00:45:45 +04:00
|
|
|
template <class T>
|
2022-09-06 14:19:55 +02:00
|
|
|
const T* insert(const T& x)
|
|
|
|
{
|
|
|
|
const std::string id = "$dynamic" + std::to_string(mDynamicCount++);
|
|
|
|
|
|
|
|
Store<T>& store = getWritable<T>();
|
|
|
|
if (store.search(id) != nullptr)
|
|
|
|
{
|
|
|
|
const std::string msg = "Try to override existing record '" + id + "'";
|
|
|
|
throw std::runtime_error(msg);
|
|
|
|
}
|
|
|
|
T record = x;
|
|
|
|
|
|
|
|
record.mId = id;
|
|
|
|
|
|
|
|
T* ptr = store.insert(record);
|
2022-09-06 23:10:58 +02:00
|
|
|
if constexpr (std::is_convertible_v<Store<T>*, DynamicStore*>)
|
2022-09-06 14:19:55 +02:00
|
|
|
{
|
2022-09-07 18:45:15 +02:00
|
|
|
setIdType(ptr->mId, T::sRecordId);
|
2022-09-06 14:19:55 +02:00
|
|
|
}
|
|
|
|
return ptr;
|
|
|
|
}
|
2012-11-06 17:51:38 +04:00
|
|
|
|
2014-12-17 01:05:32 +01:00
|
|
|
/// Insert a record with set ID, and allow it to override a pre-existing static record.
|
|
|
|
template <class T>
|
2022-09-06 14:19:55 +02:00
|
|
|
const T* overrideRecord(const T& x)
|
|
|
|
{
|
|
|
|
Store<T>& store = getWritable<T>();
|
|
|
|
|
|
|
|
T* ptr = store.insert(x);
|
2022-09-06 23:10:58 +02:00
|
|
|
if constexpr (std::is_convertible_v<Store<T>*, DynamicStore*>)
|
2022-09-06 14:19:55 +02:00
|
|
|
{
|
2022-09-07 18:45:15 +02:00
|
|
|
setIdType(ptr->mId, T::sRecordId);
|
2022-09-06 14:19:55 +02:00
|
|
|
}
|
|
|
|
return ptr;
|
|
|
|
}
|
2014-12-17 01:05:32 +01:00
|
|
|
|
2013-05-01 12:47:50 +02:00
|
|
|
template <class T>
|
2022-09-06 14:19:55 +02:00
|
|
|
const T* insertStatic(const T& x)
|
|
|
|
{
|
|
|
|
Store<T>& store = getWritable<T>();
|
|
|
|
if (store.search(x.mId) != nullptr)
|
|
|
|
{
|
|
|
|
const std::string msg = "Try to override existing record '" + x.mId + "'";
|
|
|
|
throw std::runtime_error(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
T* ptr = store.insertStatic(x);
|
2022-09-06 23:10:58 +02:00
|
|
|
if constexpr (std::is_convertible_v<Store<T>*, DynamicStore*>)
|
2022-09-06 14:19:55 +02:00
|
|
|
{
|
2022-09-07 18:45:15 +02:00
|
|
|
setIdType(ptr->mId, T::sRecordId);
|
2022-09-06 14:19:55 +02:00
|
|
|
}
|
|
|
|
return ptr;
|
|
|
|
}
|
2013-05-01 12:47:50 +02:00
|
|
|
|
2012-11-25 19:07:16 +01:00
|
|
|
// This method must be called once, after loading all master/plugin files. This can only be done
|
|
|
|
// from the outside, so it must be public.
|
2022-06-01 22:53:18 +02:00
|
|
|
void setUp();
|
|
|
|
void validateRecords(ESM::ReadersCache& readers);
|
2013-12-07 13:17:28 +01:00
|
|
|
|
|
|
|
int countSavedGameRecords() const;
|
|
|
|
|
2014-04-28 11:29:57 +02:00
|
|
|
void write(ESM::ESMWriter& writer, Loading::Listener& progress) const;
|
2013-12-07 13:17:28 +01:00
|
|
|
|
2015-01-22 19:04:59 +01:00
|
|
|
bool readRecord(ESM::ESMReader& reader, uint32_t type);
|
2013-12-07 13:17:28 +01:00
|
|
|
///< \return Known type?
|
2020-06-02 21:59:37 +02:00
|
|
|
|
|
|
|
// To be called when we are done with dynamic record loading
|
|
|
|
void checkPlayer();
|
2020-07-26 11:07:18 +02:00
|
|
|
|
2020-07-30 21:39:04 +02:00
|
|
|
/// @return The number of instances defined in the base files. Excludes changes from the save file.
|
2022-06-13 18:21:29 +02:00
|
|
|
int getRefCount(std::string_view id) const;
|
2020-07-28 08:33:28 +02:00
|
|
|
|
2020-07-30 21:39:04 +02:00
|
|
|
/// Actors with the same ID share spells, abilities, etc.
|
|
|
|
/// @return The shared spell list to use for this actor and whether or not it has already been initialized.
|
2020-07-28 08:33:28 +02:00
|
|
|
std::pair<std::shared_ptr<MWMechanics::SpellList>, bool> getSpellList(const std::string& id) const;
|
2012-11-05 18:09:14 +04:00
|
|
|
};
|
2022-09-06 14:33:03 +02:00
|
|
|
template <>
|
|
|
|
const ESM::Cell* ESMStore::insert<ESM::Cell>(const ESM::Cell& cell);
|
|
|
|
|
|
|
|
template <>
|
|
|
|
const ESM::NPC* ESMStore::insert<ESM::NPC>(const ESM::NPC& npc);
|
2012-10-01 19:17:04 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|