Skip to content

Commit

Permalink
Convert Enum to use a union of signed values instead of a uint64_t
Browse files Browse the repository at this point in the history
  • Loading branch information
googleben committed Nov 9, 2023
1 parent 0544f6f commit b2071d2
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 32 deletions.
2 changes: 1 addition & 1 deletion src/reverse/Converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ struct EnumConverter : LuaRED<Enum, "Enum">
else if (aObject.get_type() == sol::type::number) // Enum from number cast
{
auto* enumType = static_cast<RED4ext::CEnum*>(apType->type);
const Enum en(enumType, aObject.as<uint32_t>());
const Enum en(enumType, aObject.as<int32_t>());
en.Set(*apType);
}
else if (aObject.get_type() == sol::type::string) // Enum from string cast
Expand Down
103 changes: 82 additions & 21 deletions src/reverse/Enum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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<int64_t>(aValue))
int64_t val = m_cpType->valueList[i];
switch (m_cpType->GetSize())
{
m_value = aValue;
case sizeof(int8_t):
if (static_cast<int8_t>(val) == static_cast<int8_t>(aValue))
{
m_value.int8 = static_cast<int8_t>(aValue);
}
break;
case sizeof(int16_t):
if (static_cast<int16_t>(val) == static_cast<int16_t>(aValue))
{
m_value.int16 = static_cast<int16_t>(aValue);
}
break;
case sizeof(int32_t):
if (static_cast<int32_t>(val) == static_cast<int32_t>(aValue))
{
m_value.int32 = static_cast<int32_t>(aValue);
}
break;
case sizeof(int64_t):
if (val == aValue)
{
m_value.int64 = aValue;
}
break;
}
}
Expand All @@ -56,10 +79,10 @@ void Enum::Get(const RED4ext::CStackType& acStackType) noexcept
m_cpType = static_cast<const RED4ext::CEnum*>(acStackType.type);
switch (acStackType.type->GetSize())
{
case sizeof(uint8_t): m_value = *static_cast<uint8_t*>(acStackType.value); break;
case sizeof(uint16_t): m_value = *static_cast<uint16_t*>(acStackType.value); break;
case sizeof(uint32_t): m_value = *static_cast<uint32_t*>(acStackType.value); break;
case sizeof(uint64_t): m_value = *static_cast<uint64_t*>(acStackType.value); break;
case sizeof(int8_t): m_value.int8 = *static_cast<int8_t*>(acStackType.value); break;
case sizeof(int16_t): m_value.int16 = *static_cast<int16_t*>(acStackType.value); break;
case sizeof(int32_t): m_value.int32 = *static_cast<int32_t*>(acStackType.value); break;
case sizeof(int64_t): m_value.int64 = *static_cast<int64_t*>(acStackType.value); break;
}
}

Expand All @@ -68,32 +91,33 @@ void Enum::Set(RED4ext::CStackType& aStackType, TiltedPhoques::Allocator* apAllo
aStackType.type = const_cast<RED4ext::CEnum*>(m_cpType); // Sad cast
switch (m_cpType->GetSize())
{
case sizeof(uint8_t): aStackType.value = apAllocator->New<uint8_t>(static_cast<uint8_t>(m_value)); break;
case sizeof(uint16_t): aStackType.value = apAllocator->New<uint16_t>(static_cast<uint16_t>(m_value)); break;
case sizeof(uint32_t): aStackType.value = apAllocator->New<uint32_t>(static_cast<uint32_t>(m_value)); break;
case sizeof(uint64_t): aStackType.value = apAllocator->New<uint64_t>(static_cast<uint64_t>(m_value)); break;
case sizeof(int8_t): aStackType.value = apAllocator->New<int8_t>(m_value.int8); break;
case sizeof(int16_t): aStackType.value = apAllocator->New<int16_t>(m_value.int16); break;
case sizeof(int32_t): aStackType.value = apAllocator->New<int32_t>(m_value.int32); break;
case sizeof(int64_t): aStackType.value = apAllocator->New<int64_t>(m_value.int64); break;
}
}

void Enum::Set(RED4ext::CStackType& acStackType) const noexcept
{
switch (m_cpType->GetSize())
{
case sizeof(uint8_t): *static_cast<uint8_t*>(acStackType.value) = static_cast<uint8_t>(m_value); break;
case sizeof(uint16_t): *static_cast<uint16_t*>(acStackType.value) = static_cast<uint16_t>(m_value); break;
case sizeof(uint32_t): *static_cast<uint32_t*>(acStackType.value) = static_cast<uint32_t>(m_value); break;
case sizeof(uint64_t): *static_cast<uint64_t*>(acStackType.value) = static_cast<uint64_t>(m_value); break;
case sizeof(int8_t): *static_cast<int8_t*>(acStackType.value) = m_value.int8; break;
case sizeof(int16_t): *static_cast<int16_t*>(acStackType.value) = m_value.int16; break;
case sizeof(int32_t): *static_cast<int32_t*>(acStackType.value) = m_value.int32; break;
case sizeof(int64_t): *static_cast<int64_t*>(acStackType.value) = m_value.int64; break;
}
}

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<int64_t>(m_value))
if (m_cpType->valueList[i] == val)
{
return m_cpType->hashList[i].ToString();
}
Expand All @@ -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<int8_t>(val); break;
case sizeof(int16_t): m_value.int16 = static_cast<int16_t>(val); break;
case sizeof(int32_t): m_value.int32 = static_cast<int32_t>(val); break;
case sizeof(int64_t): m_value.int64 = static_cast<int64_t>(val); break;
}
break;
}
}
Expand All @@ -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<int64_t>(m_value.int8);
case sizeof(int16_t): return static_cast<int64_t>(m_value.int16);
case sizeof(int32_t): return static_cast<int64_t>(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)
Expand All @@ -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
Expand Down
34 changes: 27 additions & 7 deletions src/reverse/Enum.h
Original file line number Diff line number Diff line change
@@ -1,36 +1,56 @@
#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;

bool operator==(const Enum& acRhs) const noexcept;

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};
};
16 changes: 13 additions & 3 deletions src/scripting/Scripting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -214,17 +214,27 @@ void Scripting::PostInitializeScripting()
[](const sol::object&) -> bool { return false; });

globals.new_usertype<Enum>(
"Enum", sol::constructors<Enum(const std::string&, const std::string&), Enum(const std::string&, uint32_t), Enum(const Enum&)>(), sol::meta_function::to_string,
"Enum", sol::constructors<Enum(const std::string&, const std::string&), Enum(const std::string&, int32_t), Enum(const Enum&)>(), 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);
Expand Down

0 comments on commit b2071d2

Please sign in to comment.