diff --git a/CommonLibSF/include/RE/N/NiSmartPointer.h b/CommonLibSF/include/RE/N/NiSmartPointer.h new file mode 100644 index 00000000..7cdc0d72 --- /dev/null +++ b/CommonLibSF/include/RE/N/NiSmartPointer.h @@ -0,0 +1,179 @@ +#pragma once + +namespace RE +{ + template + class NiPointer + { + public: + using element_type = T; + + constexpr NiPointer() noexcept = default; + + constexpr NiPointer(std::nullptr_t) noexcept {} + + template + explicit NiPointer(Y* a_rhs) // + requires(std::convertible_to) + : + _ptr(static_cast(a_rhs)) + { + TryAttach(); + } + + NiPointer(const NiPointer& a_rhs) : + _ptr(a_rhs._ptr) + { + TryAttach(); + } + + template + NiPointer(const NiPointer& a_rhs) // + requires(std::convertible_to) + : + _ptr(static_cast(a_rhs._ptr)) + { + TryAttach(); + } + + NiPointer(NiPointer&& a_rhs) noexcept : + _ptr(std::exchange(a_rhs._ptr, nullptr)) + {} + + template + NiPointer(NiPointer&& a_rhs) noexcept // + requires(std::convertible_to) + : + _ptr(static_cast(std::exchange(a_rhs._ptr, nullptr))) + {} + + ~NiPointer() noexcept { reset(); } + + NiPointer& operator=(const NiPointer& a_rhs) + { + if (this != std::addressof(a_rhs)) { + TryDetach(); + _ptr = a_rhs._ptr; + TryAttach(); + } + return *this; + } + + template + NiPointer& operator=(const NiPointer& a_rhs) // + requires(std::convertible_to) + { + TryDetach(); + _ptr = static_cast(a_rhs._ptr); + TryAttach(); + return *this; + } + + NiPointer& operator=(NiPointer&& a_rhs) noexcept + { + if (this != std::addressof(a_rhs)) { + TryDetach(); + _ptr = std::exchange(a_rhs._ptr, nullptr); + } + return *this; + } + + template + NiPointer& operator=(NiPointer&& a_rhs) noexcept // + requires(std::convertible_to) + { + TryDetach(); + _ptr = std::exchange(a_rhs._ptr, nullptr); + return *this; + } + + void reset() { TryDetach(); } + + template + void reset(Y* a_ptr) // + requires(std::convertible_to) + { + if (_ptr != a_ptr) { + TryDetach(); + _ptr = static_cast(a_ptr); + TryAttach(); + } + } + + [[nodiscard]] constexpr element_type* get() const noexcept { return _ptr; } + + [[nodiscard]] explicit constexpr operator bool() const noexcept { return _ptr != nullptr; } + + [[nodiscard]] constexpr element_type& operator*() const noexcept + { + assert(static_cast(*this)); + return *_ptr; + } + + [[nodiscard]] constexpr element_type* operator->() const noexcept + { + assert(static_cast(*this)); + return _ptr; + } + + protected: + template + friend class NiPointer; + + void TryAttach() + { + if (_ptr) { + _ptr->IncRefCount(); + } + } + + void TryDetach() + { + if (_ptr) { + _ptr->DecRefCount(); + _ptr = nullptr; + } + } + + // members + element_type* _ptr{ nullptr }; // 0 + }; + static_assert(sizeof(NiPointer) == 0x8); + + template + [[nodiscard]] NiPointer make_nismart(Args&&... a_args) + { + return NiPointer{ new T(std::forward(a_args)...) }; + } + + template + [[nodiscard]] constexpr bool operator==(const NiPointer& a_lhs, const NiPointer& a_rhs) noexcept + { + return a_lhs.get() == a_rhs.get(); + } + + template + [[nodiscard]] constexpr std::strong_ordering operator<=>(const NiPointer& a_lhs, const NiPointer& a_rhs) noexcept + { + return std::compare_three_way{}(a_lhs.get(), a_rhs.get()); + } + + template + [[nodiscard]] constexpr bool operator==(const NiPointer& a_lhs, std::nullptr_t) noexcept + { + return !a_lhs; + } + + template + [[nodiscard]] constexpr std::strong_ordering operator<=>(const NiPointer& a_lhs, std::nullptr_t) noexcept + { + return std::compare_three_way{}(a_lhs.get(), static_cast::element_type*>(nullptr)); + } + + template + NiPointer(T*) -> NiPointer; +} + +#define NiSmartPointer(className) \ + class className; \ + using className##Ptr = RE::NiPointer diff --git a/CommonLibSF/include/RE/T/TESForm.h b/CommonLibSF/include/RE/T/TESForm.h index 81d9a448..2c7f0bcf 100644 --- a/CommonLibSF/include/RE/T/TESForm.h +++ b/CommonLibSF/include/RE/T/TESForm.h @@ -120,6 +120,13 @@ namespace RE virtual void Unk_60(); // 60 virtual void Unk_61(); // 61 + std::uint64_t DecRefCount() const + { + using func_t = decltype(&TESForm::DecRefCount); + REL::Relocation func{ REL::ID(35164) }; + return func(this); + } + [[nodiscard]] static TESForm* LookupByID(std::uint32_t a_formID) { using func_t = decltype(&TESForm::LookupByID); diff --git a/CommonLibSF/include/RE/T/TESFormRefCount.h b/CommonLibSF/include/RE/T/TESFormRefCount.h index f727fe51..3b5a226a 100644 --- a/CommonLibSF/include/RE/T/TESFormRefCount.h +++ b/CommonLibSF/include/RE/T/TESFormRefCount.h @@ -9,6 +9,14 @@ namespace RE public: SF_RTTI(TESFormRefCount); + enum + { + kRefCountMask = 0x3FFFFF, + }; + + std::uint64_t IncRefCount() const; + [[nodiscard]] std::uint64_t QRefCount() const; + // members volatile mutable std::uint64_t refCount; // 08 }; diff --git a/CommonLibSF/src/RE/T/TESFormRefCount.cpp b/CommonLibSF/src/RE/T/TESFormRefCount.cpp index f6483401..8eac967f 100644 --- a/CommonLibSF/src/RE/T/TESFormRefCount.cpp +++ b/CommonLibSF/src/RE/T/TESFormRefCount.cpp @@ -1,4 +1,15 @@ #include "RE/T/TESFormRefCount.h" + namespace RE { + std::uint64_t TESFormRefCount::IncRefCount() const + { + stl::atomic_ref myRefCount{ refCount }; + return ++myRefCount; + } + + std::uint64_t TESFormRefCount::QRefCount() const + { + return refCount & kRefCountMask; + } }