From b0aa1be4ecfab5dd6905ae55a973c3d6ed2cacad Mon Sep 17 00:00:00 2001 From: yamashi Date: Sun, 20 Dec 2020 14:54:55 +0100 Subject: [PATCH] Now using Lua for console --- src/overlay/Overlay.cpp | 24 ++-- src/overlay/Overlay.h | 6 +- src/overlay/Overlay_Hooks.cpp | 2 +- src/reverse/Scripting.cpp | 201 ++++++++++++++++++++++++++++------ src/reverse/Scripting.h | 27 ++++- xmake.lua | 4 +- 6 files changed, 214 insertions(+), 50 deletions(-) diff --git a/src/overlay/Overlay.cpp b/src/overlay/Overlay.cpp index 6c5dd7ca..c2c33728 100644 --- a/src/overlay/Overlay.cpp +++ b/src/overlay/Overlay.cpp @@ -95,12 +95,7 @@ void Overlay::DrawImgui(IDXGISwapChain3* apSwapChain) m_outputScroll = true; } - if (!Scripting::Execute(command, returnMessage)) - { - std::lock_guard _{ m_outputLock }; - m_outputLines.push_back(std::string("Error: ") + returnMessage); - m_outputScroll = true; - } + Scripting::Get().ExecuteLua(command); if (m_inputClear) std::memset(command, 0, sizeof(command)); @@ -167,7 +162,7 @@ TScriptCall** GetScriptCallArray() } -void* Overlay::Log(uintptr_t apThis, uint8_t** apStack) +void* Overlay::HookLog(uintptr_t apThis, uint8_t** apStack) { REDString result(""); apStack[6] = nullptr; @@ -176,13 +171,11 @@ void* Overlay::Log(uintptr_t apThis, uint8_t** apStack) GetScriptCallArray()[stack](apStack[8], apStack, &result, nullptr); ++(*apStack); - std::lock_guard _{ Get().m_outputLock }; - Get().m_outputLines.emplace_back(result.ToString()); - Get().m_outputScroll = true; + Get().Log(result.ToString()); result.Destroy(); - return 0; + return nullptr; } void Overlay::Toggle() @@ -206,11 +199,18 @@ void Overlay::Toggle() ClipToCenter(CGameEngine::Get()->pSomeStruct); } -bool Overlay::IsEnabled() +bool Overlay::IsEnabled() const { return m_enabled; } +void Overlay::Log(const std::string& acpText) +{ + std::lock_guard _{ m_outputLock }; + m_outputLines.emplace_back(acpText); + m_outputScroll = true; +} + Overlay::Overlay() = default; Overlay::~Overlay() = default; diff --git a/src/overlay/Overlay.h b/src/overlay/Overlay.h index a76b7a0d..d6d6d207 100644 --- a/src/overlay/Overlay.h +++ b/src/overlay/Overlay.h @@ -27,7 +27,9 @@ struct Overlay void Hook(); void Toggle(); - bool IsEnabled(); + bool IsEnabled() const; + + void Log(const std::string& acpText); protected: @@ -47,7 +49,7 @@ struct Overlay static BOOL SetMousePosition(void* apThis, HWND Wnd, long X, long Y); static BOOL ClipToCenter(CGameEngine::UnkC0* apThis); static LRESULT APIENTRY WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); - static void* Log(uintptr_t apThis, uint8_t** apStack); + static void* HookLog(uintptr_t apThis, uint8_t** apStack); private: diff --git a/src/overlay/Overlay_Hooks.cpp b/src/overlay/Overlay_Hooks.cpp index ad846e4a..ac605de9 100644 --- a/src/overlay/Overlay_Hooks.cpp +++ b/src/overlay/Overlay_Hooks.cpp @@ -33,7 +33,7 @@ void Overlay::EarlyHooks(Image* apImage) if(pLocation) { - if (MH_CreateHook(pLocation, &Log, reinterpret_cast(&m_realLog)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) + if (MH_CreateHook(pLocation, &HookLog, reinterpret_cast(&m_realLog)) != MH_OK || MH_EnableHook(pLocation) != MH_OK) { spdlog::error("\tCould not hook Log function!"); } diff --git a/src/reverse/Scripting.cpp b/src/reverse/Scripting.cpp index 01c9d030..35ef94d7 100644 --- a/src/reverse/Scripting.cpp +++ b/src/reverse/Scripting.cpp @@ -9,6 +9,7 @@ #include "Pattern.h" #include "RTTI.h" #include "Utils.h" +#include "overlay/Overlay.h" struct Unk523 { @@ -29,6 +30,90 @@ struct CScriptableStackFrame int64_t unk40; }; +using TCtor = CScriptableStackFrame * (*)(CScriptableStackFrame* aThis, __int64 aScriptable, Unk523* aArgs, int aArgsCount, __int64 a5, __int64* a6); +using TExec = bool (*)(CBaseFunction* aThis, CScriptableStackFrame* stack); + +Scripting::Scripting() +{ + m_lua.open_libraries(sol::lib::base, sol::lib::string, sol::lib::io, sol::lib::math); + + m_lua.new_usertype("__Game", + sol::meta_function::index, &Scripting::Index); + + m_lua.globals()["Game"] = this; + m_lua["print"] = [](sol::variadic_args args, sol::this_environment env, sol::this_state L) + { + std::ostringstream oss; + sol::state_view s(L); + for(auto& v : args) + { + std::string str = s["tostring"](v.get()); + oss << str << " "; + } + Overlay::Get().Log(oss.str()); + }; + + if (Options::Get().GameImage.version == Image::MakeVersion(1, 4)) + { + arg0Rtti = 0x1442FD030 - 0x140000000; + argiRtti = 0x143C62438 - 0x140000000; + ctorOffset = 0x140270370 - 0x140000000; + execOffset = 0x1402254A0 - 0x140000000; + } + else if (Options::Get().GameImage.version == Image::MakeVersion(1, 5)) + { + arg0Rtti = 0x1442BC710 - 0x140000000; + argiRtti = 0x143C22238 - 0x140000000; + ctorOffset = 0x14026F8A0 - 0x140000000; + execOffset = 0x1402249F0 - 0x140000000; + } + + m_pCtor = reinterpret_cast(ctorOffset + reinterpret_cast(GetModuleHandle(nullptr))); + m_pExec = reinterpret_cast(execOffset + reinterpret_cast(GetModuleHandle(nullptr))); + +} + +Scripting& Scripting::Get() +{ + static Scripting s_instance; + return s_instance; +} + +bool Scripting::ExecuteLua(const std::string& aCommand) +{ + try + { + m_lua.script(aCommand); + } + catch(std::exception& e) + { + Overlay::Get().Log( e.what()); + return false; + } + + return true; +} + +sol::protected_function Scripting::Index(Scripting& aThis, const std::string& acName) +{ + return aThis.InternalIndex(acName); +} + +sol::protected_function Scripting::InternalIndex(const std::string& acName) +{ + sol::state_view s(m_lua); + return make_object(s, [this, name = acName](sol::variadic_args args, sol::this_environment env, sol::this_state L) + { + std::string result; + bool code = this->Execute(name, args, env, L, result); + if(!code) + { + Overlay::Get().Log("Error: " + result); + } + return std::make_tuple(code, result); + }); +} + bool Scripting::Execute(const std::string& aCommand, std::string& aReturnMessage) { if (aCommand.empty()) @@ -73,39 +158,20 @@ bool Scripting::Execute(const std::string& aCommand, std::string& aReturnMessage redArgs.emplace_back(token.c_str()); } - uintptr_t arg0Rtti = 0; - uintptr_t argiRtti = 0; - uintptr_t ctorOffset = 0; - uintptr_t execOffset = 0; - - if (Options::Get().GameImage.version == Image::MakeVersion(1, 4)) - { - arg0Rtti = 0x1442FD030 - 0x140000000; - argiRtti = 0x143C62438 - 0x140000000; - ctorOffset = 0x140270370 - 0x140000000; - execOffset = 0x1402254A0 - 0x140000000; - } - else if (Options::Get().GameImage.version == Image::MakeVersion(1, 5)) - { - arg0Rtti = 0x1442BC710 - 0x140000000; - argiRtti = 0x143C22238 - 0x140000000; - ctorOffset = 0x14026F8A0 - 0x140000000; - execOffset = 0x1402249F0 - 0x140000000; - } - auto* const type = CRTTISystem::Get()->GetType(REDString::Hash("cpPlayerSystem")); auto* const engine = CGameEngine::Get(); auto* unk10 = engine->framework->unk10; - auto arg0RttiPtr = *(uintptr_t*)(arg0Rtti + (uintptr_t)GetModuleHandle(nullptr)); - auto argiRttiPtr = *(uintptr_t*)(argiRtti + (uintptr_t)GetModuleHandle(nullptr)); + auto arg0RttiPtr = *(uintptr_t*)(Get().arg0Rtti + (uintptr_t)GetModuleHandle(nullptr)); + auto argiRttiPtr = *(uintptr_t*)(Get().argiRtti + (uintptr_t)GetModuleHandle(nullptr)); + if (!arg0RttiPtr || !argiRttiPtr) { aReturnMessage = "Game has not completed initialization. Wait until the title screen."; return false; } - auto func = CRTTISystem::Get()->GetGlobalFunction(REDString::Hash(funcName.c_str())); + const auto func = CRTTISystem::Get()->GetGlobalFunction(REDString::Hash(funcName.c_str())); if (!func) { aReturnMessage = "Function '" + funcName + "' not found or is not a global."; @@ -150,19 +216,90 @@ bool Scripting::Execute(const std::string& aCommand, std::string& aReturnMessage } CScriptableStackFrame stack; - auto script40 = *(uintptr_t*)(scriptable + 0x40); - auto script40100 = *(uintptr_t*)(script40 + 0x100); - using ctor_t = CScriptableStackFrame * (*)(CScriptableStackFrame* aThis, __int64 aScriptable, Unk523* aArgs, - int aArgsCount, __int64 a5, __int64* a6); - ctor_t ctor = (ctor_t)(ctorOffset + (uintptr_t)GetModuleHandle(nullptr)); + auto* ctor = static_cast(Get().m_pCtor); + + ctor(&stack, scriptable, args, redArgs.size(), 0, nullptr); + + auto* exec = static_cast(Get().m_pExec); + + return exec(func, &stack); +} + +bool Scripting::Execute(const std::string& aFuncName, sol::variadic_args aArgs, sol::this_environment env, sol::this_state L, std::string& aReturnMessage) +{ + std::vector redArgs; + + for (auto v : aArgs) + { + std::string s = m_lua["tostring"](v.get()); + redArgs.emplace_back(s.c_str()); + } + + auto* const type = CRTTISystem::Get()->GetType(REDString::Hash("cpPlayerSystem")); + auto* const engine = CGameEngine::Get(); + auto* unk10 = engine->framework->unk10; + + auto arg0RttiPtr = *(uintptr_t*)(Get().arg0Rtti + (uintptr_t)GetModuleHandle(nullptr)); + auto argiRttiPtr = *(uintptr_t*)(Get().argiRtti + (uintptr_t)GetModuleHandle(nullptr)); + + if (!arg0RttiPtr || !argiRttiPtr) + { + aReturnMessage = "Game has not completed initialization. Wait until the title screen."; + return false; + } + + const auto func = CRTTISystem::Get()->GetGlobalFunction(REDString::Hash(aFuncName.c_str())); + if (!func) + { + aReturnMessage = "Function '" + aFuncName + "' not found or is not a global."; + return false; + } + + if (func->parameter_count < 1 || *func->parameters[0] != arg0RttiPtr) + { + aReturnMessage = "Function '" + aFuncName + "' parameter 0 must be ScriptGameInstance."; + return false; + } + + if (func->parameter_count - 1 != redArgs.size()) + { + aReturnMessage = "Function '" + aFuncName + "' expects " + + std::to_string(func->parameter_count - 1) + " parameters, not " + + std::to_string(redArgs.size()) + "."; + return false; + } + + for (uint32_t i = 1; i < func->parameter_count; i++) + { + if (*func->parameters[i] != argiRttiPtr) + { + aReturnMessage = "Function '" + aFuncName + "' parameter " + std::to_string(i) + " must be String."; + return false; + } + } + + const auto scriptable = unk10->GetTypeInstance(type); + + uint64_t a1 = *(uintptr_t*)(scriptable + 0x40); + + Unk523 args[100]; + args[0].unk0 = arg0RttiPtr; + args[0].unk8 = (uint64_t)&a1; + + for (auto i = 0u; i < redArgs.size(); ++i) + { + args[i+1].unk0 = argiRttiPtr; + args[i+1].unk8 = (uint64_t)&redArgs[i]; + } + + CScriptableStackFrame stack; - //Result result; + auto* ctor = static_cast(m_pCtor); - ctor(&stack, scriptable, args, 3, 0, 0); + ctor(&stack, scriptable, args, 3, 0, nullptr); - using exec_t = bool (*)(CBaseFunction* aThis, CScriptableStackFrame* stack); - exec_t exec = (exec_t)(execOffset + (uintptr_t)GetModuleHandle(nullptr)); + auto* exec = static_cast(m_pExec); return exec(func, &stack); } diff --git a/src/reverse/Scripting.h b/src/reverse/Scripting.h index 86841e9b..3bc525ab 100644 --- a/src/reverse/Scripting.h +++ b/src/reverse/Scripting.h @@ -3,12 +3,37 @@ #include #include #include +#include #include "REDString.h" struct Scripting { - static bool Execute(const std::string& aCommand, std::string& aReturnMessage); + Scripting(); + + static Scripting& Get(); + + bool ExecuteLua(const std::string& aCommand); + static bool Execute(const std::string& aCommand, std::string& aReturnMessage); + +protected: + + static sol::protected_function Index(Scripting& aThis, const std::string& acName); + sol::protected_function InternalIndex(const std::string& acName); + + bool Execute(const std::string& aFuncName, sol::variadic_args args, sol::this_environment env, sol::this_state L, std::string& aReturnMessage); + +private: + sol::state m_lua; + + uintptr_t arg0Rtti{ 0 }; + uintptr_t argiRtti{ 0 }; + uintptr_t ctorOffset{ 0 }; + uintptr_t execOffset{ 0 }; + uintptr_t arg0RttiPtr{ 0 }; + uintptr_t argiRttiPtr{ 0 }; + void* m_pCtor{ nullptr }; + void* m_pExec{ nullptr }; }; struct Result diff --git a/xmake.lua b/xmake.lua index d2604caf..c19489be 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,6 +1,6 @@ set_languages("cxx17") -add_requires("spdlog", "nlohmann_json", "minhook", "imgui", "lua") +add_requires("spdlog", "nlohmann_json", "minhook", "imgui", "lua", "sol2") add_rules("mode.debug", "mode.release") @@ -17,4 +17,4 @@ target("cyber_engine_tweaks") add_headerfiles("src/**.h") add_includedirs("src/") add_syslinks("User32", "d3d11", "D3D12") - add_packages("spdlog", "nlohmann_json", "minhook", "imgui", "lua") + add_packages("spdlog", "nlohmann_json", "minhook", "imgui", "lua", "sol2")