diff --git a/src/overlay/Overlay_D3D12.cpp b/src/overlay/Overlay_D3D12.cpp index 7fa263ca..f5c78e09 100644 --- a/src/overlay/Overlay_D3D12.cpp +++ b/src/overlay/Overlay_D3D12.cpp @@ -227,6 +227,8 @@ bool Overlay::InitializeD3D12Downlevel(ID3D12CommandQueue* pCommandQueue, ID3D12 } m_pCommandQueue = pCommandQueue; + m_outWidth = static_cast(pSourceTex2D->GetDesc().Width); + m_outHeight = pSourceTex2D->GetDesc().Height; if (hWindow != m_hWnd) spdlog::warn("Overlay::InitializeD3D12Downlevel() - current output window does not match hooked window! Currently hooked to {0} while current output window is {1}.", reinterpret_cast(m_hWnd), reinterpret_cast(hWindow)); diff --git a/src/overlay/Overlay_Hooks.cpp b/src/overlay/Overlay_Hooks.cpp index 3706df73..72fae7e6 100644 --- a/src/overlay/Overlay_Hooks.cpp +++ b/src/overlay/Overlay_Hooks.cpp @@ -173,27 +173,15 @@ HRESULT Overlay::PresentD3D12Downlevel(ID3D12CommandQueueDownlevel* pCommandQueu { auto& overlay = Get(); - // On Windows 7 there is no swap chain to query the current backbuffer index. Instead do a reverse lookup in the known backbuffer list - const auto it = std::find(overlay.m_downlevelBackbuffers.cbegin(), overlay.m_downlevelBackbuffers.cend(), pSourceTex2D); - bool resizing = false; - if (it == overlay.m_downlevelBackbuffers.cend()) - { - if (overlay.m_initialized) - { - spdlog::warn("Overlay::PresentD3D12Downlevel() - buffer at {0} not found in backbuffer list! Assuming window was resized - note that support for resizing is experimental.", (void*)pSourceTex2D); - overlay.ResetD3D12State(); - } + // On Windows 7 there is no swap chain to query the current backbuffer index, so instead we simply count to 3 and wrap around. + // Increment the buffer index here even if the overlay is not enabled, so we stay in sync with the game's present calls. + // TODO: investigate if there isn't a better way of doing this (finding the current index in the game exe?) + overlay.m_downlevelBufferIndex = (!overlay.m_initialized || overlay.m_downlevelBufferIndex == 2) ? 0 : overlay.m_downlevelBufferIndex + 1; - overlay.m_downlevelBackbuffers.emplace_back(pSourceTex2D); - - // If we don't have a full backbuffer list, do not attempt to reinitialize yet - resizing = overlay.m_downlevelBackbuffers.size() < 3; - } - else - overlay.m_downlevelBufferIndex = static_cast(std::distance(overlay.m_downlevelBackbuffers.cbegin(), it)); - - if (!resizing && overlay.InitializeD3D12Downlevel(overlay.m_pCommandQueue, pSourceTex2D, hWindow)) + if (overlay.InitializeD3D12Downlevel(overlay.m_pCommandQueue, pSourceTex2D, hWindow)) + { overlay.Render(); + } return overlay.m_realPresentD3D12Downlevel(pCommandQueueDownlevel, pOpenCommandList, pSourceTex2D, hWindow, Flags); } diff --git a/src/reverse/Converter.cpp b/src/reverse/Converter.cpp index 2ece2e60..f88f708b 100644 --- a/src/reverse/Converter.cpp +++ b/src/reverse/Converter.cpp @@ -3,6 +3,7 @@ #include "Converter.h" #include "BasicTypes.h" +#include "Enum.h" #include "LuaRED.h" auto s_metaVisitor = [](auto... args) { @@ -24,7 +25,8 @@ auto s_metaVisitor = [](auto... args) { LuaRED(), LuaRED(), LuaRED(), - LuaRED() + LuaRED(), + LuaRED() ); size_t Converter::Size(RED4ext::IRTTIType* apRtti) diff --git a/src/reverse/Enum.cpp b/src/reverse/Enum.cpp new file mode 100644 index 00000000..0955c322 --- /dev/null +++ b/src/reverse/Enum.cpp @@ -0,0 +1,139 @@ +#include + +#include "Enum.h" + +Enum::Enum(const RED4ext::CStackType& stackType) +{ + Get(stackType); +} + +Enum::Enum(const std::string& typeName, const std::string& value) +{ + auto* pType = static_cast(RED4ext::CRTTISystem::Get()->GetEnum(RED4ext::FNV1a(typeName.c_str()))); + if (pType) + { + m_type = pType; + SetValueByName(value); + } +} + +Enum::Enum(const std::string& typeName, uint32_t value) +{ + auto* pType = static_cast(RED4ext::CRTTISystem::Get()->GetEnum(RED4ext::FNV1a(typeName.c_str()))); + if (pType) + { + m_type = pType; + SetValueSafe(static_cast(value)); + } +} + + +Enum::Enum(const RED4ext::CEnum* pType, const std::string& value) + : m_type(pType) +{ + SetValueByName(value); +} + + +Enum::Enum(const RED4ext::CEnum* pType, uint32_t value) + : m_type(pType) +{ + SetValueSafe(static_cast(value)); +} + +void Enum::SetValueSafe(uint64_t value) +{ + for (auto i = 0; i < m_type->valueList.size; ++i) + { + if (m_type->valueList[i] == value) + { + m_value = value; + break; + } + } +} + +void Enum::Get(const RED4ext::CStackType& stackType) +{ + m_type = static_cast(stackType.type); + switch (stackType.type->GetSize()) + { + case sizeof(uint8_t) : + m_value = *static_cast(stackType.value); + break; + case sizeof(uint16_t): + m_value = *static_cast(stackType.value); + break; + case sizeof(uint32_t): + m_value = *static_cast(stackType.value); + break; + case sizeof(uint64_t): + m_value = *static_cast(stackType.value); + break; + } +} + +void Enum::Set(RED4ext::CStackType& stackType, TiltedPhoques::Allocator* apAllocator) +{ + stackType.type = const_cast(m_type); // Sad cast + switch (m_type->GetSize()) + { + case sizeof(uint8_t): + stackType.value = apAllocator->New(static_cast(m_value)); + break; + case sizeof(uint16_t): + stackType.value = apAllocator->New(static_cast(m_value)); + break; + case sizeof(uint32_t): + stackType.value = apAllocator->New(static_cast(m_value)); + break; + case sizeof(uint64_t): + stackType.value = apAllocator->New(static_cast(m_value)); + break; + } +} + + +std::string Enum::GetValueName() const +{ + for (auto i = 0; i < m_type->valueList.size; ++i) + { + if (m_type->valueList[i] == m_value) + { + return RED4ext::CName(m_type->hashList[i]).ToString(); + } + } + + return ""; +} + + +void Enum::SetValueByName(const std::string& value) +{ + for (auto i = 0; i < m_type->hashList.size; ++i) + { + if (m_type->hashList[i] == RED4ext::FNV1a(value.c_str())) + { + m_value = m_type->valueList[i]; + break; + } + } +} + +std::string Enum::ToString() const +{ + if (m_type) + { + RED4ext::CName name; + m_type->GetName(name); + return name.ToString() + std::string(" : ") + GetValueName() + std::string(" (") + std::to_string(m_value) + std::string(")"); + } + + return "Invalid enum"; +} + +const RED4ext::CEnum* Enum::GetType() const +{ + return m_type; +} + diff --git a/src/reverse/Enum.h b/src/reverse/Enum.h new file mode 100644 index 00000000..3d9c51dc --- /dev/null +++ b/src/reverse/Enum.h @@ -0,0 +1,106 @@ +#pragma once + +#include "LuaRED.h" + +struct Enum +{ + Enum(const RED4ext::CEnum*, const std::string& value); + Enum(const RED4ext::CEnum*, uint32_t value); + Enum(const RED4ext::CStackType& stackType); + Enum(const std::string& typeName, const std::string& value); + Enum(const std::string& typeName, uint32_t value); + + void Get(const RED4ext::CStackType& stackType); + void Set(RED4ext::CStackType& stackType, TiltedPhoques::Allocator* apAllocator); + + // Returns the enum value by name + std::string GetValueName() const; + + // Sets value by name in the enum list + void SetValueByName(const std::string& value); + + // Sets by value verified against enum list + void SetValueSafe(uint64_t value); + + std::string ToString() const; + + const RED4ext::CEnum* GetType() const; + +protected: + const RED4ext::CEnum* m_type{ nullptr }; + uint64_t m_value{ 0 }; +}; + +template<> +struct LuaRED +{ + sol::object ToLua(RED4ext::CStackType& aResult, sol::state_view aLua) + { + return make_object(aLua, Enum(aResult)); + } + + RED4ext::CStackType ToRED(sol::object aObject, RED4ext::IRTTIType* apRtti, TiltedPhoques::Allocator* apAllocator) + { + RED4ext::CStackType result; + if (aObject.is()) + { + auto* pEnum = aObject.as(); + if (pEnum->GetType() == apRtti) + { + pEnum->Set(result, apAllocator); + } + else // The enum type we were passed isn't the same + { + result.type = apRtti; + result.value = nullptr; + } + } + else if (aObject.get_type() == sol::type::number) // Enum from number cast + { + auto* enumType = static_cast(apRtti); + if (aObject != sol::nil) + { + Enum en(enumType, aObject.as()); + en.Set(result, apAllocator); + } + } + else if (aObject.get_type() == sol::type::string) // Enum from string cast + { + auto* enumType = static_cast(apRtti); + if (aObject != sol::nil) + { + sol::state_view v(aObject.lua_state()); + std::string str = v["tostring"](aObject); + Enum en(enumType, str); + en.Set(result, apAllocator); + } + } + else + { + // Probably not going to like this but ok + result.type = apRtti; + result.value = nullptr; + } + + return result; + } + + size_t Size() const noexcept + { + return m_pRtti ? m_pRtti->GetSize() : 0; + } + + bool Is(RED4ext::IRTTIType* apRtti) const + { + if (apRtti->GetType() == RED4ext::ERTTIType::Enum) + { + m_pRtti = apRtti; + return true; + } + + return false; + } + +private: + mutable RED4ext::IRTTIType* m_pRtti{ nullptr }; +}; diff --git a/src/reverse/Type.cpp b/src/reverse/Type.cpp index 493bf6fa..afe8eb16 100644 --- a/src/reverse/Type.cpp +++ b/src/reverse/Type.cpp @@ -1,4 +1,5 @@ #include +#include #include "Type.h" @@ -14,6 +15,11 @@ std::string Type::Descriptor::ToString() const { result += "\t\t" + function + ",\n"; } + result += "},\n\tstaticFunctions: {\n"; + for (auto& function : staticFunctions) + { + result += "\t\t" + function + ",\n"; + } result += "},\nproperties: {\n"; for (auto& property : properties) { @@ -57,8 +63,21 @@ sol::protected_function Type::InternalIndex(const std::string& acName) auto* pFunc = m_pType->GetFunction(RED4ext::FNV1a(acName.c_str())); if(!pFunc) { - Overlay::Get().Log("Function '" + acName + "' not found in system '" + GetName() + "'."); - return sol::nil; + // Search the function table if it isn't found, the above function only searches by ShortName so overloads are not found + for (uint32_t i = 0; i < m_pType->funcs.size; ++i) + { + if (m_pType->funcs.entries[i]->name.hash == RED4ext::FNV1a(acName.c_str())) + { + pFunc = static_cast(m_pType->funcs.entries[i]); + break; + } + } + + if (!pFunc) + { + Overlay::Get().Log("Function '" + acName + "' not found in system '" + GetName() + "'."); + return sol::nil; + } } auto obj = make_object(m_lua, [pFunc, name = acName](Type* apType, sol::variadic_args args, sol::this_environment env, sol::this_state L) @@ -98,21 +117,35 @@ Type::Descriptor Type::Dump() const { descriptor.name = m_pType->name.ToString(); - for (auto i = 0u; i < m_pType->funcs.size; ++i) + RED4ext::CClass* type = m_pType; + while (type) { - auto* pFunc = m_pType->funcs[i]; - std::string funcName = pFunc->name.ToString(); - descriptor.functions.push_back(funcName); - } + std::string name = type->name.ToString(); + for (auto i = 0u; i < type->funcs.size; ++i) + { + auto* pFunc = type->funcs[i]; + std::string funcName = "Owner:(" + name + ") Hash:" + pFunc->name.ToString() + " Hash:( " + fmt::format("{:016x}", pFunc->name.hash) + ") / ShortName:(" + pFunc->name2.ToString() + ") Hash:( " + fmt::format("{:016x}", pFunc->name2.hash) + ")"; + descriptor.functions.push_back(funcName); + } - for (auto i = 0u; i < m_pType->props.size; ++i) - { - auto* pProperty = m_pType->props[i]; - RED4ext::CName name; - pProperty->type->GetName(name); - - std::string propName = std::string(pProperty->name.ToString()) + " : " + name.ToString(); - descriptor.properties.push_back(propName); + for (auto i = 0u; i < type->staticFuncs.size; ++i) + { + auto* pFunc = type->staticFuncs[i]; + std::string funcName = "Owner:(" + name + ") Hash:" + pFunc->name.ToString() + " Hash:( " + fmt::format("{:016x}", pFunc->name.hash) + ") / ShortName:(" + pFunc->name2.ToString() + ") Hash:( " + fmt::format("{:016x}", pFunc->name2.hash) + ")"; + descriptor.staticFunctions.push_back(funcName); + } + + for (auto i = 0u; i < type->props.size; ++i) + { + auto* pProperty = type->props[i]; + RED4ext::CName name; + pProperty->type->GetName(name); + + std::string propName = std::string(pProperty->name.ToString()) + " : " + name.ToString(); + descriptor.properties.push_back(propName); + } + + type = type->parent && type->parent->GetType() == RED4ext::ERTTIType::Class ? type->parent : nullptr; } } diff --git a/src/reverse/Type.h b/src/reverse/Type.h index dfd7fe1f..820a40ef 100644 --- a/src/reverse/Type.h +++ b/src/reverse/Type.h @@ -6,6 +6,7 @@ struct Type { std::string name{"Unknown"}; std::vector functions; + std::vector staticFunctions; std::vector properties; std::string ToString() const; @@ -22,7 +23,6 @@ struct Type sol::object Execute(RED4ext::CClassFunction* apFunc, const std::string& acName, sol::variadic_args args, sol::this_environment env, sol::this_state L, std::string& aReturnMessage); protected: - virtual RED4ext::IScriptable* GetHandle() { return nullptr; } RED4ext::CClass* m_pType{ nullptr }; @@ -33,3 +33,4 @@ struct Type sol::state_view m_lua; std::unordered_map m_properties; }; + diff --git a/src/scripting/Scripting.cpp b/src/scripting/Scripting.cpp index b6ab22ef..52506fd4 100644 --- a/src/scripting/Scripting.cpp +++ b/src/scripting/Scripting.cpp @@ -14,6 +14,7 @@ #include "reverse/StrongReference.h" #include "reverse/Converter.h" #include "reverse/WeakReference.h" +#include "reverse/Enum.h" Scripting::Scripting() { @@ -165,6 +166,23 @@ RED4ext::CStackType Scripting::ToRED(sol::object aObject, RED4ext::IRTTIType* ap result.value = apAllocator->New(); } } + else if (aObject.is()) // Handle Implicit Cast - Probably an awful conversion without proper ref handling but try anyway + { + auto* pSubType = static_cast(apRtti)->parent; + RED4ext::IRTTIType* pType = aObject.as()->m_pType; + while (pType != nullptr && pType != pSubType) + { + pType = static_cast(pType)->parent; + } + + if (pType != nullptr) + { + if (hasData) + result.value = apAllocator->New(*reinterpret_cast(&aObject.as().m_weakHandle)); + else + result.value = apAllocator->New(); + } + } } else if (apRtti->GetType() == RED4ext::ERTTIType::WeakHandle) { @@ -185,6 +203,23 @@ RED4ext::CStackType Scripting::ToRED(sol::object aObject, RED4ext::IRTTIType* ap result.value = apAllocator->New(); } } + else if (aObject.is()) // Handle Implicit Cast + { + auto* pSubType = static_cast(apRtti)->parent; + RED4ext::IRTTIType* pType = aObject.as()->m_pType; + while (pType != nullptr && pType != pSubType) + { + pType = static_cast(pType)->parent; + } + + if (pType != nullptr) + { + if (hasData) + result.value = apAllocator->New(*reinterpret_cast(&aObject.as().m_strongHandle)); + else + result.value = apAllocator->New(); + } + } } else if (apRtti->GetType() == RED4ext::ERTTIType::Array) { @@ -272,6 +307,11 @@ void Scripting::Initialize() "z", &Vector4::z, "w", &Vector4::w); + m_lua.new_usertype("Enum", + sol::constructors(), + sol::meta_function::to_string, &Enum::ToString, + "value", sol::property(&Enum::GetValueName, &Enum::SetValueByName)); + m_lua["ToVector4"] = [this](sol::table table) -> Vector4 { return Vector4 @@ -341,7 +381,8 @@ void Scripting::Initialize() m_lua.new_usertype("TweakDBID", sol::constructors(), - sol::meta_function::to_string, &TweakDBID::ToString); + sol::meta_function::to_string, &TweakDBID::ToString, + "hash", &TweakDBID::name_hash); m_lua["ToTweakDBID"] = [this](sol::table table) -> TweakDBID { @@ -354,7 +395,8 @@ void Scripting::Initialize() m_lua.new_usertype("ItemID", sol::constructors(), - sol::meta_function::to_string, &ItemID::ToString); + sol::meta_function::to_string, &ItemID::ToString, + "tdbid", &ItemID::id); m_lua["ToItemID"] = [this](sol::table table) -> ItemID { @@ -405,6 +447,7 @@ void Scripting::Initialize() std::string str = s["tostring"]((*it).get()); oss << str; } + spdlog::info(oss.str()); Overlay::Get().Log(oss.str()); }; @@ -481,6 +524,7 @@ sol::object Scripting::Execute(const std::string& aFuncName, sol::variadic_args static const auto hashcpPlayerSystem = RED4ext::FNV1a("cpPlayerSystem"); static const auto hashGameInstance = RED4ext::FNV1a("ScriptGameInstance"); + auto* pGIType = pRtti->GetType(RED4ext::FNV1a("ScriptGameInstance")); auto* pPlayerSystem = pRtti->GetClass(hashcpPlayerSystem); auto* gameInstanceType = pRtti->GetClass(hashGameInstance); @@ -496,24 +540,6 @@ sol::object Scripting::Execute(const std::string& aFuncName, sol::variadic_args } } - if (pFunc->params.size - 1 != aArgs.size()) - { - aReturnMessage = "Function '" + aFuncName + "' expects " + - std::to_string(pFunc->params.size - 1) + " parameters, not " + - std::to_string(aArgs.size()) + "."; - - return sol::nil; - } - - const auto* engine = RED4ext::CGameEngine::Get(); - auto* unk10 = engine->framework->gameInstance; - - using CStackType = RED4ext::CStackType; - std::vector args(aArgs.size() + 1); - - args[0].type = pFunc->params[0]->type; - args[0].value = &unk10; - // 8KB should cut it static thread_local TiltedPhoques::ScratchAllocator s_scratchMemory(1 << 13); struct ResetAllocator @@ -525,15 +551,49 @@ sol::object Scripting::Execute(const std::string& aFuncName, sol::variadic_args }; ResetAllocator ___allocatorReset; + using CStackType = RED4ext::CStackType; + const auto* engine = RED4ext::CGameEngine::Get(); + auto* unk10 = engine->framework->gameInstance; + + RED4ext::CName name; + uint8_t argOffset = 0; + + if (pFunc->params.size > 0) + { + auto* pType = pFunc->params[0]->type; + // check if the first argument is expected to be ScriptGameInstance + if (pType == pGIType) + { + argOffset = 1; + } + } + + std::vector args(aArgs.size() + argOffset); + + if (pFunc->params.size - argOffset != aArgs.size()) + { + aReturnMessage = "Function '" + aFuncName + "' expects " + + std::to_string(pFunc->params.size - argOffset) + " parameters, not " + + std::to_string(aArgs.size()) + "."; + + return sol::nil; + } + + if (argOffset > 0) + { + // Inject the ScriptGameInstance into first argument + args[0].type = pFunc->params[0]->type; + args[0].value = &unk10; + } + for (auto i = 0ull; i < aArgs.size(); ++i) { - args[i + 1ull] = ToRED(aArgs[i].get(), pFunc->params[i + 1ull]->type, &s_scratchMemory); + args[i + argOffset] = ToRED(aArgs[i].get(), pFunc->params[i + argOffset]->type, &s_scratchMemory); - if(!args[i + 1ull].value) + if(!args[i + argOffset].value) { - auto* pType = pFunc->params[i + 1]->type; + auto* pType = pFunc->params[i + argOffset]->type; - RED4ext::CName name; pType->GetName(name); if (name) {