Skip to content

Commit

Permalink
Now using Lua for console
Browse files Browse the repository at this point in the history
  • Loading branch information
maximegmd committed Dec 20, 2020
1 parent 50c63a2 commit b0aa1be
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 50 deletions.
24 changes: 12 additions & 12 deletions src/overlay/Overlay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,7 @@ void Overlay::DrawImgui(IDXGISwapChain3* apSwapChain)
m_outputScroll = true;
}

if (!Scripting::Execute(command, returnMessage))
{
std::lock_guard<std::recursive_mutex> _{ 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));
Expand Down Expand Up @@ -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;
Expand All @@ -176,13 +171,11 @@ void* Overlay::Log(uintptr_t apThis, uint8_t** apStack)
GetScriptCallArray()[stack](apStack[8], apStack, &result, nullptr);
++(*apStack);

std::lock_guard<std::recursive_mutex> _{ 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()
Expand All @@ -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<std::recursive_mutex> _{ m_outputLock };
m_outputLines.emplace_back(acpText);
m_outputScroll = true;
}

Overlay::Overlay() = default;

Overlay::~Overlay() = default;
Expand Down
6 changes: 4 additions & 2 deletions src/overlay/Overlay.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ struct Overlay
void Hook();

void Toggle();
bool IsEnabled();
bool IsEnabled() const;

void Log(const std::string& acpText);

protected:

Expand All @@ -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:

Expand Down
2 changes: 1 addition & 1 deletion src/overlay/Overlay_Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ void Overlay::EarlyHooks(Image* apImage)

if(pLocation)
{
if (MH_CreateHook(pLocation, &Log, reinterpret_cast<void**>(&m_realLog)) != MH_OK || MH_EnableHook(pLocation) != MH_OK)
if (MH_CreateHook(pLocation, &HookLog, reinterpret_cast<void**>(&m_realLog)) != MH_OK || MH_EnableHook(pLocation) != MH_OK)
{
spdlog::error("\tCould not hook Log function!");
}
Expand Down
201 changes: 169 additions & 32 deletions src/reverse/Scripting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "Pattern.h"
#include "RTTI.h"
#include "Utils.h"
#include "overlay/Overlay.h"

struct Unk523
{
Expand All @@ -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<Scripting>("__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<sol::object>());
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<void*>(ctorOffset + reinterpret_cast<uintptr_t>(GetModuleHandle(nullptr)));
m_pExec = reinterpret_cast<void*>(execOffset + reinterpret_cast<uintptr_t>(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())
Expand Down Expand Up @@ -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<CClass>(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.";
Expand Down Expand Up @@ -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<TCtor>(Get().m_pCtor);

ctor(&stack, scriptable, args, redArgs.size(), 0, nullptr);

auto* exec = static_cast<TExec>(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<REDString> redArgs;

for (auto v : aArgs)
{
std::string s = m_lua["tostring"](v.get<sol::object>());
redArgs.emplace_back(s.c_str());
}

auto* const type = CRTTISystem::Get()->GetType<CClass>(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<TCtor>(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<TExec>(m_pExec);

return exec(func, &stack);
}
Expand Down
27 changes: 26 additions & 1 deletion src/reverse/Scripting.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,37 @@
#include <cstddef>
#include <cstdint>
#include <string>
#include <sol/sol.hpp>

#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
Expand Down
4 changes: 2 additions & 2 deletions xmake.lua
Original file line number Diff line number Diff line change
@@ -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")

Expand All @@ -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")

0 comments on commit b0aa1be

Please sign in to comment.