#pragma once namespace RE { class BSStringPool { public: class Entry { public: enum : std::uint16_t { kShallow = 1 << 14, kWide = 1 << 15, kRefCountMask = 0x3FFF }; static void release(Entry*& a_entry) { using func_t = decltype(&Entry::release); static REL::Relocation func{ ID::BSStringPool::Entry::Release }; return func(a_entry); } void acquire() { REX::TAtomicRef flags{ _flags }; std::uint16_t expected{ 0 }; do { expected = flags; if ((expected & kRefCountMask) >= kRefCountMask) { break; } } while (!flags.compare_exchange_weak(expected, static_cast(expected + 1))); } [[nodiscard]] std::uint16_t crc() const noexcept { return _crc; } template [[nodiscard]] const T* data() const noexcept; template <> [[nodiscard]] const char* data() const noexcept { return u8(); } template <> [[nodiscard]] const wchar_t* data() const noexcept { return u16(); } [[nodiscard]] const Entry* leaf() const noexcept { auto iter = this; while (iter && iter->shallow()) { iter = iter->_right; } return iter; } [[nodiscard]] std::uint32_t length() const noexcept { const auto entry = leaf(); return entry ? entry->_length : 0; } [[nodiscard]] bool shallow() const noexcept { return _flags & kShallow; } [[nodiscard]] std::uint32_t size() const noexcept { return length(); } [[nodiscard]] const char* u8() const noexcept { const auto entry = leaf(); if (entry) { assert(!entry->wide()); return reinterpret_cast(entry + 1); } else { return nullptr; } } [[nodiscard]] const wchar_t* u16() const noexcept { const auto entry = leaf(); if (entry) { assert(entry->wide()); return reinterpret_cast(entry + 1); } else { return nullptr; } } [[nodiscard]] bool wide() const noexcept { return _flags & kWide; } // members Entry* _left; // 00 std::uint16_t _flags; // 08 volatile std::uint16_t _crc; // 08 union { std::uint32_t _length; Entry* _right; }; // 10 }; static_assert(sizeof(Entry) == 0x18); }; static_assert(std::is_empty_v); template void GetEntry(BSStringPool::Entry*& a_result, const T* a_string, bool a_caseSensitive); template <> inline void GetEntry(BSStringPool::Entry*& a_result, const char* a_string, bool a_caseSensitive) { using func_t = decltype(&GetEntry); static REL::Relocation func{ ID::BSStringPool::GetEntry_char }; return func(a_result, a_string, a_caseSensitive); } template <> inline void GetEntry(BSStringPool::Entry*& a_result, const wchar_t* a_string, bool a_caseSensitive) { using func_t = decltype(&GetEntry); static REL::Relocation func{ ID::BSStringPool::GetEntry_wchar }; return func(a_result, a_string, a_caseSensitive); } }