#pragma once namespace RE { class NiRTTI { public: NiRTTI(const char* a_name, const NiRTTI* a_baseRTTI) : name(a_name), baseRTTI(a_baseRTTI) {} [[nodiscard]] constexpr const char* GetName() const noexcept { return name; } [[nodiscard]] constexpr const NiRTTI* GetBaseRTTI() const noexcept { return baseRTTI; } [[nodiscard]] constexpr bool IsKindOf(const NiRTTI* a_rtti) const noexcept { for (auto iter = this; iter; iter = iter->GetBaseRTTI()) { if (iter == a_rtti) { return true; } } return false; } // members const char* name; // 00 const NiRTTI* baseRTTI; // 08 }; static_assert(sizeof(NiRTTI) == 0x10); namespace Ni_Impl { template using remove_cvpr_t = std::remove_pointer_t< std::remove_reference_t< std::remove_cv_t>>; template struct types_are_compat : std::false_type {}; template struct types_are_compat : std::is_lvalue_reference< std::remove_cv_t> {}; template struct types_are_compat : std::is_pointer< std::remove_cv_t> {}; template struct is_base_of_no_cvpr : std::is_base_of< remove_cvpr_t, remove_cvpr_t> {}; template struct _has_rtti : std::false_type {}; template struct _has_rtti> : std::true_type {}; template struct has_rtti : _has_rtti> {}; template struct cast_is_valid : std::conjunction< types_are_compat, is_base_of_no_cvpr, has_rtti, has_rtti> {}; template inline constexpr bool cast_is_valid_v = cast_is_valid::value; } } // downcast template < class To, class From, std::enable_if_t< RE::Ni_Impl::cast_is_valid_v< To, const From*>, int> = 0> To netimmerse_cast(const From* a_from) { if (!a_from) { return nullptr; } static REL::Relocation to{ RE::Ni_Impl::remove_cvpr_t::Ni_RTTI }; const RE::NiRTTI* toRTTI = to.get(); const RE::NiRTTI* fromRTTI = a_from->GetRTTI(); while (fromRTTI) { if (fromRTTI == toRTTI) { return static_cast(const_cast(a_from)); } fromRTTI = fromRTTI->GetBaseRTTI(); } return nullptr; } // upcast template < class To, class From, std::enable_if_t< RE::Ni_Impl::cast_is_valid_v< const From*, To>, int> = 0> To netimmerse_cast(const From* a_from) { return static_cast(const_cast(a_from)); }