#ifndef OPENMW_COMPONENTS_ESM_REFID_HPP #define OPENMW_COMPONENTS_ESM_REFID_HPP #include #include #include #include #include #include #include #include "esm3exteriorcellrefid.hpp" #include "formid.hpp" #include "generatedrefid.hpp" #include "indexrefid.hpp" #include "stringrefid.hpp" namespace ESM { struct EmptyRefId { constexpr bool operator==(EmptyRefId /*rhs*/) const { return true; } constexpr bool operator<(EmptyRefId /*rhs*/) const { return false; } std::string toString() const { return ""; } std::string toDebugString() const { return "Empty{}"; } }; enum class RefIdType : std::uint8_t { Empty = 0, SizedString = 1, UnsizedString = 2, FormId = 3, Generated = 4, Index = 5, ESM3ExteriorCell = 6, }; // RefId is used to represent an Id that identifies an ESM record. These Ids can then be used in // ESM::Stores to find the actual record. These Ids can be serialized/de-serialized, stored on disk and remain // valid. They are used by ESM files, by records to reference other ESM records. class RefId { public: using Value = std::variant; // Constructs RefId from a serialized string containing byte by byte copy of RefId::mValue. static ESM::RefId deserialize(std::string_view value); // Constructs RefId from a serialized text. static ESM::RefId deserializeText(std::string_view value); // Constructs RefId from a string using a pointer to a static set of strings. static RefId stringRefId(std::string_view value); // Constructs RefId from FormId storing the value in-place. static RefId formIdRefId(FormId value) { return RefId(value); } // Constructs RefId from uint64 storing the value in-place. Should be used for generated records where id is a // global counter. static RefId generated(std::uint64_t value) { return RefId(GeneratedRefId{ value }); } // Constructs RefId from record type and 32bit index storing the value in-place. Should be used for records // identified by index (i.e. ESM3 SKIL). static RefId index(RecNameInts recordType, std::uint32_t value) { return RefId(IndexRefId(recordType, value)); } static RefId esm3ExteriorCell(int32_t x, int32_t y) { return RefId(ESM3ExteriorCellRefId(x, y)); } constexpr RefId() = default; constexpr RefId(EmptyRefId value) noexcept : mValue(value) { } RefId(StringRefId value) noexcept : mValue(value) { } constexpr RefId(FormId value) { if (value.isZeroOrUnset()) mValue = EmptyRefId(); else if (value.hasContentFile()) mValue = value; else throw std::logic_error("RefId can't be a generated FormId"); } constexpr RefId(GeneratedRefId value) noexcept : mValue(value) { } constexpr RefId(IndexRefId value) noexcept : mValue(value) { } constexpr RefId(ESM3ExteriorCellRefId value) noexcept : mValue(value) { } // Returns a reference to the value of StringRefId if it's the underlying value or throws an exception. const std::string& getRefIdString() const; // Returns a string with serialized underlying value. std::string toString() const; // Returns a string with serialized underlying value with information about underlying type. std::string toDebugString() const; constexpr bool empty() const noexcept { return std::holds_alternative(mValue); } // Returns true if underlying value is StringRefId and its underlying std::string starts with given prefix. // Otherwise returns false. bool startsWith(std::string_view prefix) const; // Returns true if underlying value is StringRefId and its underlying std::string ends with given suffix. // Otherwise returns false. bool endsWith(std::string_view suffix) const; // Returns true if underlying value is StringRefId and its underlying std::string contains given subString. // Otherwise returns false. bool contains(std::string_view subString) const; // Copy mValue byte by byte into a string. Use result only during within the same process. std::string serialize() const; // Serialize into stable text format. std::string serializeText() const; template const T* getIf() const { return std::get_if(&mValue); } template bool is() const { return std::holds_alternative(mValue); } friend constexpr bool operator==(const RefId& l, const RefId& r) { return l.mValue == r.mValue; } bool operator==(std::string_view rhs) const; friend constexpr bool operator<(const RefId& l, const RefId& r) { return l.mValue < r.mValue; } friend bool operator<(RefId lhs, std::string_view rhs); friend bool operator<(std::string_view lhs, RefId rhs); friend std::ostream& operator<<(std::ostream& stream, RefId value) { return stream << value.toDebugString(); } template friend constexpr auto visit(F&& f, T&&... v) -> std::enable_if_t<(std::is_same_v, RefId> && ...), decltype(std::visit(std::forward(f), std::forward(v).mValue...))> { return std::visit(std::forward(f), std::forward(v).mValue...); } friend struct std::hash; private: Value mValue{ EmptyRefId{} }; }; } namespace std { template <> struct hash { std::size_t operator()(ESM::EmptyRefId /*value*/) const noexcept { return 0; } }; template <> struct hash { std::size_t operator()(ESM::RefId value) const noexcept { return std::hash{}(value.mValue); } }; } #endif