#pragma once /* * SDK generated by Dumper-7 * * https://github.com/Encryqed/Dumper-7 */ // Container implementations with iterators. See https://github.com/Fischsalat/UnrealContainers #include #include #include #include "UtfN.hpp" namespace UC { typedef int8_t int8; typedef int16_t int16; typedef int32_t int32; typedef int64_t int64; typedef uint8_t uint8; typedef uint16_t uint16; typedef uint32_t uint32; typedef uint64_t uint64; template class TArray; template class TSparseArray; template class TSet; template class TMap; template class TPair; namespace Iterators { class FSetBitIterator; template class TArrayIterator; template class TContainerIterator; template using TSparseArrayIterator = TContainerIterator>; template using TSetIterator = TContainerIterator>; template using TMapIterator = TContainerIterator>; } namespace ContainerImpl { namespace HelperFunctions { inline uint32 FloorLog2(uint32 Value) { uint32 pos = 0; if (Value >= 1 << 16) { Value >>= 16; pos += 16; } if (Value >= 1 << 8) { Value >>= 8; pos += 8; } if (Value >= 1 << 4) { Value >>= 4; pos += 4; } if (Value >= 1 << 2) { Value >>= 2; pos += 2; } if (Value >= 1 << 1) { pos += 1; } return pos; } inline uint32 CountLeadingZeros(uint32 Value) { if (Value == 0) return 32; return 31 - FloorLog2(Value); } } template struct TAlignedBytes { alignas(Alignment) uint8 Pad[Size]; }; template class TInlineAllocator { public: template class ForElementType { private: static constexpr int32 ElementSize = sizeof(ElementType); static constexpr int32 ElementAlign = alignof(ElementType); static constexpr int32 InlineDataSizeBytes = NumInlineElements * ElementSize; private: TAlignedBytes InlineData[NumInlineElements]; ElementType* SecondaryData; public: ForElementType() : InlineData{ 0x0 }, SecondaryData(nullptr) { } ForElementType(ForElementType&&) = default; ForElementType(const ForElementType&) = default; public: ForElementType& operator=(ForElementType&&) = default; ForElementType& operator=(const ForElementType&) = default; public: inline const ElementType* GetAllocation() const { return SecondaryData ? SecondaryData : reinterpret_cast(&InlineData); } inline uint32 GetNumInlineBytes() const { return NumInlineElements; } }; }; class FBitArray { protected: static constexpr int32 NumBitsPerDWORD = 32; static constexpr int32 NumBitsPerDWORDLogTwo = 5; private: TInlineAllocator<4>::ForElementType Data; int32 NumBits; int32 MaxBits; public: FBitArray() : NumBits(0), MaxBits(Data.GetNumInlineBytes() * NumBitsPerDWORD) { } FBitArray(const FBitArray&) = default; FBitArray(FBitArray&&) = default; public: FBitArray& operator=(FBitArray&&) = default; FBitArray& operator=(const FBitArray& Other) = default; private: inline void VerifyIndex(int32 Index) const { if (!IsValidIndex(Index)) throw std::out_of_range("Index was out of range!"); } public: inline int32 Num() const { return NumBits; } inline int32 Max() const { return MaxBits; } inline const uint32* GetData() const { return reinterpret_cast(Data.GetAllocation()); } inline bool IsValidIndex(int32 Index) const { return Index >= 0 && Index < NumBits; } inline bool IsValid() const { return GetData() && NumBits > 0; } public: inline bool operator[](int32 Index) const { VerifyIndex(Index); return GetData()[Index / NumBitsPerDWORD] & (1 << (Index & (NumBitsPerDWORD - 1))); } inline bool operator==(const FBitArray& Other) const { return NumBits == Other.NumBits && GetData() == Other.GetData(); } inline bool operator!=(const FBitArray& Other) const { return NumBits != Other.NumBits || GetData() != Other.GetData(); } public: friend Iterators::FSetBitIterator begin(const FBitArray& Array); friend Iterators::FSetBitIterator end (const FBitArray& Array); }; template union TSparseArrayElementOrFreeListLink { SparseArrayType ElementData; struct { int32 PrevFreeIndex; int32 NextFreeIndex; }; }; template class SetElement { private: template friend class TSet; private: SetType Value; int32 HashNextId; int32 HashIndex; }; } template class TPair { public: KeyType First; ValueType Second; public: TPair(KeyType Key, ValueType Value) : First(Key), Second(Value) { } public: inline KeyType& Key() { return First; } inline const KeyType& Key() const { return First; } inline ValueType& Value() { return Second; } inline const ValueType& Value() const { return Second; } }; template class TArray { private: template friend class TAllocatedArray; template friend class TSparseArray; protected: static constexpr uint64 ElementAlign = alignof(ArrayElementType); static constexpr uint64 ElementSize = sizeof(ArrayElementType); protected: ArrayElementType* Data; int32 NumElements; int32 MaxElements; public: TArray() : TArray(nullptr, 0, 0) { } TArray(ArrayElementType* Data, int32 NumElements, int32 MaxElements) : Data(Data), NumElements(NumElements), MaxElements(MaxElements) { } TArray(const TArray&) = default; TArray(TArray&&) = default; public: TArray& operator=(TArray&&) = default; TArray& operator=(const TArray&) = default; private: inline int32 GetSlack() const { return MaxElements - NumElements; } inline void VerifyIndex(int32 Index) const { if (!IsValidIndex(Index)) throw std::out_of_range("Index was out of range!"); } inline ArrayElementType& GetUnsafe(int32 Index) { return Data[Index]; } inline const ArrayElementType& GetUnsafe(int32 Index) const { return Data[Index]; } public: /* Adds to the array if there is still space for one more element */ inline bool Add(const ArrayElementType& Element) { if (GetSlack() <= 0) return false; Data[NumElements] = Element; NumElements++; return true; } inline bool Remove(int32 Index) { if (!IsValidIndex(Index)) return false; NumElements--; for (int i = Index; i < NumElements; i++) { /* NumElements was decremented, acessing i + 1 is safe */ Data[i] = Data[i + 1]; } return true; } inline void Clear() { NumElements = 0; if (Data) memset(Data, 0, NumElements * ElementSize); } public: inline int32 Num() const { return NumElements; } inline int32 Max() const { return MaxElements; } inline const ArrayElementType* GetDataPtr() const { return Data; } inline bool IsValidIndex(int32 Index) const { return Data && Index >= 0 && Index < NumElements; } inline bool IsValid() const { return Data && NumElements > 0 && MaxElements >= NumElements; } public: inline ArrayElementType& operator[](int32 Index) { VerifyIndex(Index); return Data[Index]; } inline const ArrayElementType& operator[](int32 Index) const { VerifyIndex(Index); return Data[Index]; } inline bool operator==(const TArray& Other) const { return Data == Other.Data; } inline bool operator!=(const TArray& Other) const { return Data != Other.Data; } inline explicit operator bool() const { return IsValid(); }; public: template friend Iterators::TArrayIterator begin(const TArray& Array); template friend Iterators::TArrayIterator end (const TArray& Array); }; class FString : public TArray { public: friend std::ostream& operator<<(std::ostream& Stream, const UC::FString& Str) { return Stream << Str.ToString(); } public: using TArray::TArray; FString(const wchar_t* Str) { const uint32 NullTerminatedLength = static_cast(wcslen(Str) + 0x1); Data = const_cast(Str); NumElements = NullTerminatedLength; MaxElements = NullTerminatedLength; } FString(wchar_t* Str, int32 Num, int32 Max) { Data = Str; NumElements = Num; MaxElements = Max; } public: inline std::string ToString() const { if (*this) { return UtfN::Utf16StringToUtf8String(Data, NumElements - 1); // Exclude null-terminator } return ""; } inline std::wstring ToWString() const { if (*this) return std::wstring(Data); return L""; } public: inline wchar_t* CStr() { return Data; } inline const wchar_t* CStr() const { return Data; } public: inline bool operator==(const FString& Other) const { return Other ? NumElements == Other.NumElements && wcscmp(Data, Other.Data) == 0 : false; } inline bool operator!=(const FString& Other) const { return Other ? NumElements != Other.NumElements || wcscmp(Data, Other.Data) != 0 : true; } }; // Utf8String that assumes C-APIs (strlen, strcmp) behaviour works for char8_t like Ansi strings, execept it's counting/comparing bytes not characters. class FUtf8String : public TArray { public: friend std::ostream& operator<<(std::ostream& Stream, const UC::FUtf8String& Str) { return Stream << Str.ToString(); } private: inline const char* GetDataAsConstCharPtr() const { return reinterpret_cast(Data); } public: using TArray::TArray; FUtf8String(const char8_t* Str) { Data = const_cast(Str); const uint32 NullTerminatedLength = static_cast(strlen(GetDataAsConstCharPtr()) + 0x1); NumElements = NullTerminatedLength; MaxElements = NullTerminatedLength; } FUtf8String(char8_t* Str, int32 Num, int32 Max) { Data = Str; NumElements = Num; MaxElements = Max; } public: inline std::string ToString() const { if (*this) { return std::string(GetDataAsConstCharPtr(), NumElements - 1); // Exclude null-terminator } return ""; } inline std::wstring ToWString() const { if (*this) return UtfN::StringToWString(ToString()); // Exclude null-terminator return L""; } public: inline char8_t* CStr() { return Data; } inline const char8_t* CStr() const { return Data; } public: inline bool operator==(const FUtf8String& Other) const { return Other ? NumElements == Other.NumElements && strcmp(GetDataAsConstCharPtr(), Other.GetDataAsConstCharPtr()) == 0 : false; } inline bool operator!=(const FUtf8String& Other) const { return Other ? NumElements != Other.NumElements || strcmp(GetDataAsConstCharPtr(), Other.GetDataAsConstCharPtr()) != 0 : true; } }; class FAnsiString : public TArray { public: friend std::ostream& operator<<(std::ostream& Stream, const UC::FAnsiString& Str) { return Stream << Str.ToString(); } public: using TArray::TArray; FAnsiString(const char* Str) { const uint32 NullTerminatedLength = static_cast(strlen(Str) + 0x1); Data = const_cast(Str); NumElements = NullTerminatedLength; MaxElements = NullTerminatedLength; } FAnsiString(char* Str, int32 Num, int32 Max) { Data = Str; NumElements = Num; MaxElements = Max; } public: inline std::string ToString() const { if (*this) { return std::string(Data, NumElements - 1); // Exclude null-terminator } return ""; } inline std::wstring ToWString() const { if (*this) return UtfN::StringToWString(ToString()); // Exclude null-terminator return L""; } public: inline char* CStr() { return Data; } inline const char* CStr() const { return Data; } public: inline bool operator==(const FAnsiString& Other) const { return Other ? NumElements == Other.NumElements && strcmp(Data, Other.Data) == 0 : false; } inline bool operator!=(const FAnsiString& Other) const { return Other ? NumElements != Other.NumElements || strcmp(Data, Other.Data) != 0 : true; } }; /* * Class to allow construction of a TArray, that uses c-style standard-library memory allocation. * * Useful for calling functions that expect a buffer of a certain size and do not reallocate that buffer. * This avoids leaking memory, if the array would otherwise be allocated by the engine, and couldn't be freed without FMemory-functions. */ template class TAllocatedArray : public TArray { public: TAllocatedArray() = delete; public: TAllocatedArray(int32 Size) { this->Data = static_cast(malloc(Size * sizeof(ArrayElementType))); this->NumElements = 0x0; this->MaxElements = Size; } ~TAllocatedArray() { if (this->Data) free(this->Data); this->NumElements = 0x0; this->MaxElements = 0x0; } public: inline operator TArray() { return *reinterpret_cast< TArray*>(this); } inline operator const TArray() const { return *reinterpret_cast*>(this); } }; /* * Class to allow construction of an FString, that uses c-style standard-library memory allocation. * * Useful for calling functions that expect a buffer of a certain size and do not reallocate that buffer. * This avoids leaking memory, if the array would otherwise be allocated by the engine, and couldn't be freed without FMemory-functions. */ class FAllocatedString : public FString { public: FAllocatedString() = delete; public: FAllocatedString(int32 Size) { Data = static_cast(malloc(Size * sizeof(wchar_t))); NumElements = 0x0; MaxElements = Size; } ~FAllocatedString() { if (Data) free(Data); NumElements = 0x0; MaxElements = 0x0; } public: inline operator FString() { return *reinterpret_cast< FString*>(this); } inline operator const FString() const { return *reinterpret_cast(this); } }; template class TSparseArray { private: static constexpr uint32 ElementAlign = alignof(SparseArrayElementType); static constexpr uint32 ElementSize = sizeof(SparseArrayElementType); private: using FElementOrFreeListLink = ContainerImpl::TSparseArrayElementOrFreeListLink>; private: TArray Data; ContainerImpl::FBitArray AllocationFlags; int32 FirstFreeIndex; int32 NumFreeIndices; public: TSparseArray() : FirstFreeIndex(-1), NumFreeIndices(0) { } TSparseArray(TSparseArray&&) = default; TSparseArray(const TSparseArray&) = default; public: TSparseArray& operator=(TSparseArray&&) = default; TSparseArray& operator=(const TSparseArray&) = default; private: inline void VerifyIndex(int32 Index) const { if (!IsValidIndex(Index)) throw std::out_of_range("Index was out of range!"); } public: inline int32 NumAllocated() const { return Data.Num(); } inline int32 Num() const { return NumAllocated() - NumFreeIndices; } inline int32 Max() const { return Data.Max(); } inline bool IsValidIndex(int32 Index) const { return Data.IsValidIndex(Index) && AllocationFlags[Index]; } inline bool IsValid() const { return Data.IsValid() && AllocationFlags.IsValid(); } public: const ContainerImpl::FBitArray& GetAllocationFlags() const { return AllocationFlags; } public: inline SparseArrayElementType& operator[](int32 Index) { VerifyIndex(Index); return *reinterpret_cast(&Data.GetUnsafe(Index).ElementData); } inline const SparseArrayElementType& operator[](int32 Index) const { VerifyIndex(Index); return *reinterpret_cast(&Data.GetUnsafe(Index).ElementData); } inline bool operator==(const TSparseArray& Other) const { return Data == Other.Data; } inline bool operator!=(const TSparseArray& Other) const { return Data != Other.Data; } public: template friend Iterators::TSparseArrayIterator begin(const TSparseArray& Array); template friend Iterators::TSparseArrayIterator end (const TSparseArray& Array); }; template class TSet { private: static constexpr uint32 ElementAlign = alignof(SetElementType); static constexpr uint32 ElementSize = sizeof(SetElementType); private: using SetDataType = ContainerImpl::SetElement; using HashType = ContainerImpl::TInlineAllocator<1>::ForElementType; private: TSparseArray Elements; HashType Hash; int32 HashSize; public: TSet() : HashSize(0) { } TSet(TSet&&) = default; TSet(const TSet&) = default; public: TSet& operator=(TSet&&) = default; TSet& operator=(const TSet&) = default; private: inline void VerifyIndex(int32 Index) const { if (!IsValidIndex(Index)) throw std::out_of_range("Index was out of range!"); } public: inline int32 NumAllocated() const { return Elements.NumAllocated(); } inline int32 Num() const { return Elements.Num(); } inline int32 Max() const { return Elements.Max(); } inline bool IsValidIndex(int32 Index) const { return Elements.IsValidIndex(Index); } inline bool IsValid() const { return Elements.IsValid(); } public: const ContainerImpl::FBitArray& GetAllocationFlags() const { return Elements.GetAllocationFlags(); } public: inline SetElementType& operator[] (int32 Index) { return Elements[Index].Value; } inline const SetElementType& operator[] (int32 Index) const { return Elements[Index].Value; } inline bool operator==(const TSet& Other) const { return Elements == Other.Elements; } inline bool operator!=(const TSet& Other) const { return Elements != Other.Elements; } public: template friend Iterators::TSetIterator begin(const TSet& Set); template friend Iterators::TSetIterator end (const TSet& Set); }; template class TMap { public: using ElementType = TPair; private: TSet Elements; private: inline void VerifyIndex(int32 Index) const { if (!IsValidIndex(Index)) throw std::out_of_range("Index was out of range!"); } public: inline int32 NumAllocated() const { return Elements.NumAllocated(); } inline int32 Num() const { return Elements.Num(); } inline int32 Max() const { return Elements.Max(); } inline bool IsValidIndex(int32 Index) const { return Elements.IsValidIndex(Index); } inline bool IsValid() const { return Elements.IsValid(); } public: const ContainerImpl::FBitArray& GetAllocationFlags() const { return Elements.GetAllocationFlags(); } public: inline decltype(auto) Find(const KeyElementType& Key, bool(*Equals)(const KeyElementType& LeftKey, const KeyElementType& RightKey)) { for (auto It = begin(*this); It != end(*this); ++It) { if (Equals(It->Key(), Key)) return It; } return end(*this); } public: inline ElementType& operator[] (int32 Index) { return Elements[Index]; } inline const ElementType& operator[] (int32 Index) const { return Elements[Index]; } inline bool operator==(const TMap& Other) const { return Elements == Other.Elements; } inline bool operator!=(const TMap& Other) const { return Elements != Other.Elements; } public: template friend Iterators::TMapIterator begin(const TMap& Map); template friend Iterators::TMapIterator end (const TMap& Map); }; namespace Iterators { class FRelativeBitReference { protected: static constexpr int32 NumBitsPerDWORD = 32; static constexpr int32 NumBitsPerDWORDLogTwo = 5; public: inline explicit FRelativeBitReference(int32 BitIndex) : WordIndex(BitIndex >> NumBitsPerDWORDLogTwo) , Mask(1 << (BitIndex & (NumBitsPerDWORD - 1))) { } int32 WordIndex; uint32 Mask; }; class FSetBitIterator : public FRelativeBitReference { private: const ContainerImpl::FBitArray& Array; uint32 UnvisitedBitMask; int32 CurrentBitIndex; int32 BaseBitIndex; public: explicit FSetBitIterator(const ContainerImpl::FBitArray& InArray, int32 StartIndex = 0) : FRelativeBitReference(StartIndex) , Array(InArray) , UnvisitedBitMask((~0U) << (StartIndex & (NumBitsPerDWORD - 1))) , CurrentBitIndex(StartIndex) , BaseBitIndex(StartIndex & ~(NumBitsPerDWORD - 1)) { if (StartIndex != Array.Num()) FindFirstSetBit(); } public: inline FSetBitIterator& operator++() { UnvisitedBitMask &= ~this->Mask; FindFirstSetBit(); return *this; } inline explicit operator bool() const { return CurrentBitIndex < Array.Num(); } inline bool operator==(const FSetBitIterator& Rhs) const { return CurrentBitIndex == Rhs.CurrentBitIndex && &Array == &Rhs.Array; } inline bool operator!=(const FSetBitIterator& Rhs) const { return CurrentBitIndex != Rhs.CurrentBitIndex || &Array != &Rhs.Array; } public: inline int32 GetIndex() { return CurrentBitIndex; } void FindFirstSetBit() { const uint32* ArrayData = Array.GetData(); const int32 ArrayNum = Array.Num(); const int32 LastWordIndex = (ArrayNum - 1) / NumBitsPerDWORD; uint32 RemainingBitMask = ArrayData[this->WordIndex] & UnvisitedBitMask; while (!RemainingBitMask) { ++this->WordIndex; BaseBitIndex += NumBitsPerDWORD; if (this->WordIndex > LastWordIndex) { CurrentBitIndex = ArrayNum; return; } RemainingBitMask = ArrayData[this->WordIndex]; UnvisitedBitMask = ~0; } const uint32 NewRemainingBitMask = RemainingBitMask & (RemainingBitMask - 1); this->Mask = NewRemainingBitMask ^ RemainingBitMask; CurrentBitIndex = BaseBitIndex + NumBitsPerDWORD - 1 - ContainerImpl::HelperFunctions::CountLeadingZeros(this->Mask); if (CurrentBitIndex > ArrayNum) CurrentBitIndex = ArrayNum; } }; template class TArrayIterator { private: TArray& IteratedArray; int32 Index; public: TArrayIterator(const TArray& Array, int32 StartIndex = 0x0) : IteratedArray(const_cast&>(Array)), Index(StartIndex) { } public: inline int32 GetIndex() { return Index; } inline int32 IsValid() { return IteratedArray.IsValidIndex(GetIndex()); } public: inline TArrayIterator& operator++() { ++Index; return *this; } inline TArrayIterator& operator--() { --Index; return *this; } inline ArrayType& operator*() { return IteratedArray[GetIndex()]; } inline const ArrayType& operator*() const { return IteratedArray[GetIndex()]; } inline ArrayType* operator->() { return &IteratedArray[GetIndex()]; } inline const ArrayType* operator->() const { return &IteratedArray[GetIndex()]; } inline bool operator==(const TArrayIterator& Other) const { return &IteratedArray == &Other.IteratedArray && Index == Other.Index; } inline bool operator!=(const TArrayIterator& Other) const { return &IteratedArray != &Other.IteratedArray || Index != Other.Index; } }; template class TContainerIterator { private: ContainerType& IteratedContainer; FSetBitIterator BitIterator; public: TContainerIterator(const ContainerType& Container, const ContainerImpl::FBitArray& BitArray, int32 StartIndex = 0x0) : IteratedContainer(const_cast(Container)), BitIterator(BitArray, StartIndex) { } public: inline int32 GetIndex() { return BitIterator.GetIndex(); } inline int32 IsValid() { return IteratedContainer.IsValidIndex(GetIndex()); } public: inline TContainerIterator& operator++() { ++BitIterator; return *this; } inline TContainerIterator& operator--() { --BitIterator; return *this; } inline auto& operator*() { return IteratedContainer[GetIndex()]; } inline const auto& operator*() const { return IteratedContainer[GetIndex()]; } inline auto* operator->() { return &IteratedContainer[GetIndex()]; } inline const auto* operator->() const { return &IteratedContainer[GetIndex()]; } inline bool operator==(const TContainerIterator& Other) const { return &IteratedContainer == &Other.IteratedContainer && BitIterator == Other.BitIterator; } inline bool operator!=(const TContainerIterator& Other) const { return &IteratedContainer != &Other.IteratedContainer || BitIterator != Other.BitIterator; } }; } inline Iterators::FSetBitIterator begin(const ContainerImpl::FBitArray& Array) { return Iterators::FSetBitIterator(Array, 0); } inline Iterators::FSetBitIterator end (const ContainerImpl::FBitArray& Array) { return Iterators::FSetBitIterator(Array, Array.Num()); } template inline Iterators::TArrayIterator begin(const TArray& Array) { return Iterators::TArrayIterator(Array, 0); } template inline Iterators::TArrayIterator end (const TArray& Array) { return Iterators::TArrayIterator(Array, Array.Num()); } template inline Iterators::TSparseArrayIterator begin(const TSparseArray& Array) { return Iterators::TSparseArrayIterator(Array, Array.GetAllocationFlags(), 0); } template inline Iterators::TSparseArrayIterator end (const TSparseArray& Array) { return Iterators::TSparseArrayIterator(Array, Array.GetAllocationFlags(), Array.NumAllocated()); } template inline Iterators::TSetIterator begin(const TSet& Set) { return Iterators::TSetIterator(Set, Set.GetAllocationFlags(), 0); } template inline Iterators::TSetIterator end (const TSet& Set) { return Iterators::TSetIterator(Set, Set.GetAllocationFlags(), Set.NumAllocated()); } template inline Iterators::TMapIterator begin(const TMap& Map) { return Iterators::TMapIterator(Map, Map.GetAllocationFlags(), 0); } template inline Iterators::TMapIterator end (const TMap& Map) { return Iterators::TMapIterator(Map, Map.GetAllocationFlags(), Map.NumAllocated()); } #if defined(_WIN64) static_assert(sizeof(TArray) == 0x10, "TArray has a wrong size!"); static_assert(sizeof(TSet) == 0x50, "TSet has a wrong size!"); static_assert(sizeof(TMap) == 0x50, "TMap has a wrong size!"); #elif defined(_WIN32) static_assert(sizeof(TArray) == 0x0C, "TArray has a wrong size!"); static_assert(sizeof(TSet) == 0x3C, "TSet has a wrong size!"); static_assert(sizeof(TMap) == 0x3C, "TMap has a wrong size!"); #endif }