From b2071d221b4d281e947c11464e166decf8ba47b6 Mon Sep 17 00:00:00 2001 From: GoogleBen Date: Thu, 9 Nov 2023 03:28:00 -0600 Subject: [PATCH] Convert Enum to use a union of signed values instead of a uint64_t See https://github.com/maximegmd/CyberEngineTweaks/issues/888 --- src/reverse/Converter.h | 2 +- src/reverse/Enum.cpp | 103 ++++++++++++++++++++++++++++-------- src/reverse/Enum.h | 34 +++++++++--- src/scripting/Scripting.cpp | 16 ++++-- 4 files changed, 123 insertions(+), 32 deletions(-) diff --git a/src/reverse/Converter.h b/src/reverse/Converter.h index 33a2d240..f670f7eb 100644 --- a/src/reverse/Converter.h +++ b/src/reverse/Converter.h @@ -156,7 +156,7 @@ struct EnumConverter : LuaRED else if (aObject.get_type() == sol::type::number) // Enum from number cast { auto* enumType = static_cast(apType->type); - const Enum en(enumType, aObject.as()); + const Enum en(enumType, aObject.as()); en.Set(*apType); } else if (aObject.get_type() == sol::type::string) // Enum from string cast diff --git a/src/reverse/Enum.cpp b/src/reverse/Enum.cpp index 216ad8d8..ea5ac7aa 100644 --- a/src/reverse/Enum.cpp +++ b/src/reverse/Enum.cpp @@ -17,7 +17,7 @@ Enum::Enum(const std::string& acTypeName, const std::string& acValue) } } -Enum::Enum(const std::string& acTypeName, uint32_t aValue) +Enum::Enum(const std::string& acTypeName, int32_t aValue) { const auto* cpType = RED4ext::CRTTISystem::Get()->GetEnum(RED4ext::FNV1a64(acTypeName.c_str())); if (cpType) @@ -33,19 +33,42 @@ Enum::Enum(const RED4ext::CEnum* acpType, const std::string& acValue) SetValueByName(acValue); } -Enum::Enum(const RED4ext::CEnum* acpType, uint32_t aValue) +Enum::Enum(const RED4ext::CEnum* acpType, int32_t aValue) : m_cpType(acpType) { SetValueSafe(aValue); } -void Enum::SetValueSafe(uint64_t aValue) +void Enum::SetValueSafe(int64_t aValue) { for (uint32_t i = 0; i < m_cpType->valueList.size; ++i) { - if (m_cpType->valueList[i] == static_cast(aValue)) + int64_t val = m_cpType->valueList[i]; + switch (m_cpType->GetSize()) { - m_value = aValue; + case sizeof(int8_t): + if (static_cast(val) == static_cast(aValue)) + { + m_value.int8 = static_cast(aValue); + } + break; + case sizeof(int16_t): + if (static_cast(val) == static_cast(aValue)) + { + m_value.int16 = static_cast(aValue); + } + break; + case sizeof(int32_t): + if (static_cast(val) == static_cast(aValue)) + { + m_value.int32 = static_cast(aValue); + } + break; + case sizeof(int64_t): + if (val == aValue) + { + m_value.int64 = aValue; + } break; } } @@ -56,10 +79,10 @@ void Enum::Get(const RED4ext::CStackType& acStackType) noexcept m_cpType = static_cast(acStackType.type); switch (acStackType.type->GetSize()) { - case sizeof(uint8_t): m_value = *static_cast(acStackType.value); break; - case sizeof(uint16_t): m_value = *static_cast(acStackType.value); break; - case sizeof(uint32_t): m_value = *static_cast(acStackType.value); break; - case sizeof(uint64_t): m_value = *static_cast(acStackType.value); break; + case sizeof(int8_t): m_value.int8 = *static_cast(acStackType.value); break; + case sizeof(int16_t): m_value.int16 = *static_cast(acStackType.value); break; + case sizeof(int32_t): m_value.int32 = *static_cast(acStackType.value); break; + case sizeof(int64_t): m_value.int64 = *static_cast(acStackType.value); break; } } @@ -68,10 +91,10 @@ void Enum::Set(RED4ext::CStackType& aStackType, TiltedPhoques::Allocator* apAllo aStackType.type = const_cast(m_cpType); // Sad cast switch (m_cpType->GetSize()) { - case sizeof(uint8_t): aStackType.value = apAllocator->New(static_cast(m_value)); break; - case sizeof(uint16_t): aStackType.value = apAllocator->New(static_cast(m_value)); break; - case sizeof(uint32_t): aStackType.value = apAllocator->New(static_cast(m_value)); break; - case sizeof(uint64_t): aStackType.value = apAllocator->New(static_cast(m_value)); break; + case sizeof(int8_t): aStackType.value = apAllocator->New(m_value.int8); break; + case sizeof(int16_t): aStackType.value = apAllocator->New(m_value.int16); break; + case sizeof(int32_t): aStackType.value = apAllocator->New(m_value.int32); break; + case sizeof(int64_t): aStackType.value = apAllocator->New(m_value.int64); break; } } @@ -79,10 +102,10 @@ void Enum::Set(RED4ext::CStackType& acStackType) const noexcept { switch (m_cpType->GetSize()) { - case sizeof(uint8_t): *static_cast(acStackType.value) = static_cast(m_value); break; - case sizeof(uint16_t): *static_cast(acStackType.value) = static_cast(m_value); break; - case sizeof(uint32_t): *static_cast(acStackType.value) = static_cast(m_value); break; - case sizeof(uint64_t): *static_cast(acStackType.value) = static_cast(m_value); break; + case sizeof(int8_t): *static_cast(acStackType.value) = m_value.int8; break; + case sizeof(int16_t): *static_cast(acStackType.value) = m_value.int16; break; + case sizeof(int32_t): *static_cast(acStackType.value) = m_value.int32; break; + case sizeof(int64_t): *static_cast(acStackType.value) = m_value.int64; break; } } @@ -90,10 +113,11 @@ std::string Enum::GetValueName() const { if (!m_cpType) return ""; + int64_t val = GetValue(); for (uint32_t i = 0; i < m_cpType->valueList.size; ++i) { - if (m_cpType->valueList[i] == static_cast(m_value)) + if (m_cpType->valueList[i] == val) { return m_cpType->hashList[i].ToString(); } @@ -113,7 +137,14 @@ void Enum::SetValueByName(const std::string& acValue) { if (m_cpType->hashList[i] == cValueName) { - m_value = m_cpType->valueList[i]; + int64_t val = m_cpType->valueList[i]; + switch (m_cpType->GetSize()) + { + case sizeof(int8_t): m_value.int8 = static_cast(val); break; + case sizeof(int16_t): m_value.int16 = static_cast(val); break; + case sizeof(int32_t): m_value.int32 = static_cast(val); break; + case sizeof(int64_t): m_value.int64 = static_cast(val); break; + } break; } } @@ -124,12 +155,33 @@ std::string Enum::ToString() const if (m_cpType) { const RED4ext::CName name = m_cpType->GetName(); - return name.ToString() + std::string(" : ") + GetValueName() + std::string(" (") + std::to_string(m_value) + std::string(")"); + std::string numStr; + switch (m_cpType->GetSize()) + { + case sizeof(int8_t): numStr = std::to_string(m_value.int8); break; + case sizeof(int16_t): numStr = std::to_string(m_value.int16); break; + case sizeof(int32_t): numStr = std::to_string(m_value.int32); break; + case sizeof(int64_t): numStr = std::to_string(m_value.int64); break; + default: return "Invalid enum"; + } + return name.ToString() + std::string(" : ") + GetValueName() + std::string(" (") + numStr + std::string(")"); } return "Invalid enum"; } +int64_t Enum::GetValue() const +{ + switch (m_cpType->GetSize()) + { + case sizeof(int8_t): return static_cast(m_value.int8); + case sizeof(int16_t): return static_cast(m_value.int16); + case sizeof(int32_t): return static_cast(m_value.int32); + case sizeof(int64_t): + default: return m_value.int64; + } +} + bool Enum::operator==(const Enum& acRhs) const noexcept { if (!m_cpType || !acRhs.m_cpType) @@ -138,7 +190,16 @@ bool Enum::operator==(const Enum& acRhs) const noexcept const RED4ext::CName name = m_cpType->GetName(); const RED4ext::CName nameRhs = acRhs.m_cpType->GetName(); - return name == nameRhs && m_value == acRhs.m_value; + bool valuesEqual; + switch (m_cpType->GetSize()) + { + case sizeof(int8_t): valuesEqual = m_value.int8 == acRhs.m_value.int8; break; + case sizeof(int16_t): valuesEqual = m_value.int16 == acRhs.m_value.int16; break; + case sizeof(int32_t): valuesEqual = m_value.int32 == acRhs.m_value.int32; break; + case sizeof(int64_t): valuesEqual = m_value.int64 == acRhs.m_value.int64; + } + + return name == nameRhs && valuesEqual; } const RED4ext::CEnum* Enum::GetType() const diff --git a/src/reverse/Enum.h b/src/reverse/Enum.h index 4c5a8d37..2c91e2ac 100644 --- a/src/reverse/Enum.h +++ b/src/reverse/Enum.h @@ -1,25 +1,43 @@ #pragma once +union EnumValue +{ + int8_t int8; + int16_t int16; + int32_t int32; + int64_t int64; +}; + +// Represents a specific value of an enum +// Always assumes enums may only be 1, 2, 4, or 8 bytes wide struct Enum { + // Creates an Enum from its type description and a value's name Enum(const RED4ext::CEnum*, const std::string& acValue); - Enum(const RED4ext::CEnum*, uint32_t aValue); + // Creates an Enum from its type description and a value. If the provided value does not appear to be valid, 0 is used instead + Enum(const RED4ext::CEnum*, int32_t aValue); + // Creates an Enum from a stack value with type RED4ext::CEnum*. value is assumed to be an int64_t Enum(const RED4ext::CStackType& acStackType); + // Creates an Enum from its type name and a value's name Enum(const std::string& acTypeName, const std::string& acValue); - Enum(const std::string& acTypeName, uint32_t aValue); + // Creates an Enum from its type name and a value. If the provided value does not appear to be valid, 0 is used instead + Enum(const std::string& acTypeName, int32_t aValue); + // Sets m_value based on acStackType's value void Get(const RED4ext::CStackType& acStackType) noexcept; + // Sets acStackType's type and value to match this instance. Always allocates new memory for the value. Does not free memory if a value is already present void Set(RED4ext::CStackType& acStackType, TiltedPhoques::Allocator* apAllocator) const noexcept; + // Sets acStackType's value based on m_value void Set(RED4ext::CStackType& acStackType) const noexcept; - // Returns the enum value by name + // Returns the name of the current value std::string GetValueName() const; - // Sets value by name in the enum list + // Sets value by name. If the provided name does not appear in the list of names, the value is not set void SetValueByName(const std::string& acValue); - // Sets by value verified against enum list - void SetValueSafe(uint64_t aValue); + // Sets by value verified against enum list. If the provided value does not appear to be valid, the value is not set + void SetValueSafe(int64_t aValue); std::string ToString() const; @@ -27,10 +45,12 @@ struct Enum const RED4ext::CEnum* GetType() const; const void* GetValuePtr() const; + // Returns m_value as an int64_t, casting with sign extension if needed + int64_t GetValue() const; protected: friend struct Scripting; const RED4ext::CEnum* m_cpType{nullptr}; - uint64_t m_value{0}; + EnumValue m_value{0}; }; diff --git a/src/scripting/Scripting.cpp b/src/scripting/Scripting.cpp index 89a92b40..4aaa47dd 100644 --- a/src/scripting/Scripting.cpp +++ b/src/scripting/Scripting.cpp @@ -214,17 +214,27 @@ void Scripting::PostInitializeScripting() [](const sol::object&) -> bool { return false; }); globals.new_usertype( - "Enum", sol::constructors(), sol::meta_function::to_string, + "Enum", sol::constructors(), sol::meta_function::to_string, &Enum::ToString, sol::meta_function::equal_to, &Enum::operator==, "value", sol::property(&Enum::GetValueName, &Enum::SetValueByName)); globals["EnumInt"] = [this](Enum& aEnum) -> sol::object { - static RTTILocator s_uint64Type{RED4ext::FNV1a64("Uint64")}; + static RTTILocator s_int8Type{RED4ext::FNV1a64("Int8")}; + static RTTILocator s_int16Type{RED4ext::FNV1a64("Int16")}; + static RTTILocator s_int32Type{RED4ext::FNV1a64("Int32")}; + static RTTILocator s_int64Type{RED4ext::FNV1a64("Int64")}; auto lockedState = m_lua.Lock(); RED4ext::CStackType stackType; - stackType.type = s_uint64Type; + switch (aEnum.m_cpType->GetSize()) + { + case sizeof(int8_t): stackType.type = s_int8Type; break; + case sizeof(int16_t): stackType.type = s_int16Type; break; + case sizeof(int32_t): stackType.type = s_int32Type; break; + case sizeof(int64_t): stackType.type = s_int64Type; break; + } + stackType.value = &aEnum.m_value; return Converter::ToLua(stackType, lockedState);