From 892d38ad4f7743565020316741387a5c604cf16a Mon Sep 17 00:00:00 2001 From: c0dycode Date: Sat, 31 Jul 2021 19:26:32 +0200 Subject: [PATCH] Initial push. Still data missing --- BL3Lua.cpp | 522 +++++++ BL3Lua.h | 61 + BL3Lua.vcxproj | 188 +++ BL3Lua.vcxproj.filters | 86 ++ BL3Lua.vcxproj.user | 18 + BL3LuaDLL.cpp | 2928 ++++++++++++++++++++++++++++++++++++++++ BitFields.hpp | 88 ++ Helper.hpp | 78 ++ LuaComponent.cpp | 61 + LuaUtility.cpp | 151 +++ LuaUtility.h | 18 + include/LuaComponent.h | 21 + include/Patterns.h | 73 + include/Utility.hpp | 14 + include/json.lua | 390 ++++++ split.lua | 129 ++ 16 files changed, 4826 insertions(+) create mode 100644 BL3Lua.cpp create mode 100644 BL3Lua.h create mode 100644 BL3Lua.vcxproj create mode 100644 BL3Lua.vcxproj.filters create mode 100644 BL3Lua.vcxproj.user create mode 100644 BL3LuaDLL.cpp create mode 100644 BitFields.hpp create mode 100644 Helper.hpp create mode 100644 LuaComponent.cpp create mode 100644 LuaUtility.cpp create mode 100644 LuaUtility.h create mode 100644 include/LuaComponent.h create mode 100644 include/Patterns.h create mode 100644 include/Utility.hpp create mode 100644 include/json.lua create mode 100644 split.lua diff --git a/BL3Lua.cpp b/BL3Lua.cpp new file mode 100644 index 0000000..012b737 --- /dev/null +++ b/BL3Lua.cpp @@ -0,0 +1,522 @@ +#include "BL3Lua.h" +#include "LuaUtility.h" + +#include "include/patternscan.hpp" + +int lua_exception_handler(lua_State* L, sol::optional maybe_exception, sol::string_view description) { + // L is the lua state, which you can wrap in a state_view if necessary + // maybe_exception will contain exception, if it exists + // description will either be the what() of the exception or a description saying that we hit the general-case catch(...) + std::cout << "An exception occurred in a function, here's what it says "; + if (maybe_exception) { + std::cout << "(straight from the exception): "; + const std::exception& ex = *maybe_exception; + std::cout << ex.what() << std::endl; + } + else { + std::cout << "(from the description parameter): "; + std::cout.write(description.data(), static_cast(description.size())); + std::cout << std::endl; + } + + // you must push 1 element onto the stack to be + // transported through as the error object in Lua + // note that Lua -- and 99.5% of all Lua users and libraries -- expects a string + // so we push a single string (in our case, the description of the error) + return sol::stack::push(L, description); +} + +inline void my_panic(sol::optional maybe_msg) { + std::cerr << "Lua is in a panic state and will now abort() the application" << std::endl; + if (maybe_msg) { + const std::string& msg = maybe_msg.value(); + std::cerr << "\terror message: " << msg << std::endl; + } + // When this function exits, Lua will exhibit default behavior and abort() +} + +BL3Lua::BL3Lua() +{ + InitializeCriticalSection(&luaCritSec); + /*InitializeCriticalSection(&procEventCritSec); + InitializeCriticalSection(&consoleCommandCritSec);*/ + + lua = new sol::state(); + lua->open_libraries(); + lua->set_exception_handler(lua_exception_handler); + lua->set_panic(sol::c_call); + + registerBase(); + while (SDK::GEngine == nullptr) + { + auto engineAddress = PatternScan::FindSignature(NULL, "48 8b 4c 24 50 48 89 05 ?? ?? ?? ?? 48 85 c9 74 05 e8"); + if (engineAddress != NULL) + { + auto tmp = (engineAddress + 5); + auto tmp2 = tmp + 3; + SDK::GEngine = reinterpret_cast((tmp + 7 + *(int*)(tmp2))); + } + } + consoleEnvironment = sol::environment(*lua, sol::create, lua->globals()); +} + +BL3Lua::~BL3Lua() +{ + lua_close(lua->lua_state()); + DeleteCriticalSection(&luaCritSec); +} + +bool BL3Lua::ExecuteScriptFile(const char* filePath, bool printInGame) +{ + bool result = false; + EnterCriticalSection(&luaCritSec); + int indexToDelete = -1; + for (int i = 0; i < loadedComponents.size(); i++) { + if (!strcmp(loadedComponents[i]->m_FileName.c_str(), filePath)) { + indexToDelete = i; + break; + } + } + // TODO: Properly delete references during iteration + // Implement iterator instead of loop + if (indexToDelete != -1) { + // Remove registered Hotkeys from this env + auto hkIT = registeredHotkeys.begin(); + while (hkIT != registeredHotkeys.end()) { + /*for (auto& r : hkIT) {*/ + sol::reference eRef = std::get<0>(*hkIT); + sol::environment env = sol::get_environment(eRef); + if (env == loadedComponents[indexToDelete]->m_Env) { + eRef.abandon(); + hkIT = registeredHotkeys.erase(hkIT); + //registeredHotkeys.erase(std::remove(registeredHotkeys.begin(), registeredHotkeys.end(), r), registeredHotkeys.end()); + } + if(hkIT != registeredHotkeys.end()) + ++hkIT; + } + auto procEventIT = procEventHooks.begin(); + // Remove procEventHooks from this env + //for (auto& r : procEventHooks) { + + while(procEventIT != procEventHooks.end()){ + bool didErase = false; + for (auto& p : procEventIT->second) { + sol::reference eRef = std::get<0>(p); + sol::environment env = sol::get_environment(eRef); + if (env == loadedComponents[indexToDelete]->m_Env) { + eRef.abandon(); + //r.second.erase(std::remove(r.second.begin(), r.second.end(), p), r.second.end()); + didErase = true; + } + } + if (didErase) { + procEventIT = procEventHooks.erase(procEventIT); + if (procEventIT != procEventHooks.end()) + ++procEventIT; + } + } + // Remove consoleCommands from this env + auto ccIT = consoleCommands.begin(); + //for (auto& r : consoleCommands) { + while(ccIT != consoleCommands.end()){ + sol::reference eRef = std::get<0>(ccIT->second); + sol::environment env = sol::get_environment(eRef); + if (env == loadedComponents[indexToDelete]->m_Env) { + eRef.abandon(); + ccIT = consoleCommands.erase(ccIT); + //consoleCommands.erase(r.first); + } + if (ccIT != consoleCommands.end()) + ++ccIT; + } + delete loadedComponents[indexToDelete]; + loadedComponents.erase(loadedComponents.begin() + indexToDelete); + } + lua->collect_garbage(); + LuaComponent* nComp = new LuaComponent(filePath, lua); + nComp->Init(); + loadedComponents.push_back(nComp); + lua->collect_garbage(); + LeaveCriticalSection(&luaCritSec); + return nComp->m_Env != sol::lua_nil; +} + +bool BL3Lua::ExecuteScriptString(const char* scriptCode, bool printInGame) +{ + bool result = false; + EnterCriticalSection(&luaCritSec); + try { + auto func_result = lua->safe_script(scriptCode, consoleEnvironment); + if (!func_result.valid()) { + sol::error err = func_result; + std::cout << "[LUA] failed with error: " << err.what() << std::endl; + if (printInGame) { + sol::protected_function printFunc = (*(lua))["PrintGameConsole"]; + if (printFunc.valid()) { + std::string errorMessage = std::string("[LUA] failed with error: ").append(err.what()); + printFunc(errorMessage); + } + } + } + else { + result = true; + } + } + catch (...) { + } + lua->collect_garbage(); + LeaveCriticalSection(&luaCritSec); + return result; +} + +void BL3Lua::RunCallbacks(const char* callbackName, sol::object data) +{ + EnterCriticalSection(&luaCritSec); + try{ + sol::protected_function runCallbackFunc = (*lua)["RunCallbacks"]; + if (runCallbackFunc.valid()){ + sol::protected_function_result result = runCallbackFunc(callbackName, data); + if (!result.valid()) { + sol::error err = result; + std::cout << "Error during callbacks: " << err.what() << std::endl; + } + } + } + catch(...){} + lua->collect_garbage(); + LeaveCriticalSection(&luaCritSec); +} + +void BL3Lua::TDump(sol::object tableToDump) +{ + EnterCriticalSection(&luaCritSec); + try { + sol::protected_function tdumpFunc = (*lua)["internalTDump"]; + if (tdumpFunc.valid()) { + sol::protected_function_result result = tdumpFunc(tableToDump); + if (!result.valid()) { + sol::error err = result; + std::cout << "Error during tdump: " << err.what() << std::endl; + } + } + } + catch (...) {} + LeaveCriticalSection(&luaCritSec); +} + +void BL3Lua::ClearCallbacks() +{ + EnterCriticalSection(&luaCritSec); + while (callbacks && !callbacks.empty()) { + callbacks.clear(); + } + callbacks = lua->create_named_table("Callbacks"); + lua->collect_garbage(); + LeaveCriticalSection(&luaCritSec); +} + +void BL3Lua::ValidateHotkeys() +{ + EnterCriticalSection(&luaCritSec); + std::vector toDelete; + std::vector> tmp; + bool needsFixup = false; + for (int i = 0; i < registeredHotkeys.size(); ++i) { + if (!std::get<0>(registeredHotkeys[i]).valid()) + { + needsFixup = true; + } + else { + tmp.push_back(registeredHotkeys[i]); + } + } + if (needsFixup) { + registeredHotkeys = tmp; + } + LeaveCriticalSection(&luaCritSec); +} + +// Registering works +uint32_t BL3Lua::RegisterHotkey(sol::this_state s, sol::this_environment e, sol::variadic_args va) { + bool anyError = false; + ValidateHotkeys(); + EnterCriticalSection(&luaCritSec); + std::vector hotkeys; + sol::function func; + for (auto v : va) { + if (v.is()) { + std::string hk = v.as(); + if (!LuaUtility::ContainsKey(hk)) + { + anyError = true; + sol::protected_function printFunc = (*(lua))["PrintGameConsole"]; + if (printFunc.valid()) { + char buffer[1024]; + sprintf_s(buffer, sizeof(buffer), "\"%s\" is not a valid keybind!", hk.c_str()); + std::string errorMessage = std::string(buffer); + printFunc(errorMessage); + } + } + else { + hotkeys.push_back(hk); + } + } + else if (v.is()) { + func = v.as(); + } + } + //std::vector hotkeys = hotkeyTable.as>(); + if (hotkeys.size() >= 1 && hotkeys.size() <= 3 && func.valid()) { + for (auto& hk : hotkeys) { + if (!LuaUtility::ContainsKey(hk)) + { + anyError = true; + sol::protected_function printFunc = (*(lua))["PrintGameConsole"]; + if (printFunc.valid()) { + char buffer[1024]; + sprintf_s(buffer, sizeof(buffer), "\"%s\" is not a valid keybind!", hk.c_str()); + std::string errorMessage = std::string(buffer); + printFunc(errorMessage); + } + } + } + if (anyError) { + LeaveCriticalSection(&luaCritSec); + return 0; + } + + int index = -1; + for (int i = 0; i < registeredHotkeys.size(); ++i) { + sol::function testFunc = std::get<0>(registeredHotkeys[i]); + if (std::get<0>(registeredHotkeys[i]) == func) { + index = i; + break; + } + } + + switch (hotkeys.size()) + { + case 1: + if (index != -1) { + registeredHotkeys[index] = std::make_tuple(func, s.L, hotkeys.at(0), "", ""); + } + else { + registeredHotkeys.push_back(std::make_tuple(func, s.L, hotkeys.at(0), "", "")); + } + break; + case 2: + if (index != -1) { + registeredHotkeys[index] = std::make_tuple(func, s.L, hotkeys.at(0), "", ""); + } + else { + registeredHotkeys.push_back(std::make_tuple(func, s.L, hotkeys.at(0), hotkeys.at(1), "")); + } + break; + case 3: + if (index != -1) { + registeredHotkeys[index] = std::make_tuple(func, s.L, hotkeys.at(0), "", ""); + } + else { + registeredHotkeys.push_back(std::make_tuple(func, s.L, hotkeys.at(0), hotkeys.at(1), hotkeys.at(2))); + } + break; + default: + break; + } + } + LeaveCriticalSection(&luaCritSec); + return 0; +} + +void BL3Lua::RemoveHotkey(sol::this_state s, sol::this_environment e, uint32_t, sol::function func) { + lua->collect_garbage(); +} + +void BL3Lua::RemoveAllHotkeys() { + EnterCriticalSection(&luaCritSec); + registeredHotkeys.clear(); + lua->collect_garbage(); + LeaveCriticalSection(&luaCritSec); +} + +void BL3Lua::AddHook(sol::this_state s, sol::this_environment e, const std::string& hookName, sol::function func, bool preHook) +{ + EnterCriticalSection(&luaCritSec); + procEventHooks[hookName].push_back(std::make_tuple(func, s.L, preHook)); + lua->collect_garbage(); + LeaveCriticalSection(&luaCritSec); +} + +void BL3Lua::RemoveAllHooks() +{ + EnterCriticalSection(&luaCritSec); + if (procEventHooks.size() > 0) { + procEventHooks.clear(); + } + lua->collect_garbage(); + LeaveCriticalSection(&luaCritSec); +} + +void BL3Lua::RemoveHook(sol::this_state s, sol::this_environment e, const std::string& hookName, sol::function func) +{ + int index = -1; + EnterCriticalSection(&luaCritSec); + for (int i = 0; i < procEventHooks[hookName].size(); ++i) { + if (std::get<0>(procEventHooks[hookName][i]).registry_index() == func.registry_index()) { + index = i; + break; + } + } + if (index != -1) + procEventHooks[hookName].erase(procEventHooks[hookName].begin(), procEventHooks[hookName].begin() + index); + lua->collect_garbage(); + LeaveCriticalSection(&luaCritSec); +} + +void BL3Lua::AddConsoleCommand(sol::this_state s, sol::this_environment e, const std::string& command, sol::function func) +{ + EnterCriticalSection(&luaCritSec); + consoleCommands[command] = std::make_tuple(func, s.L, command.size()); + lua->collect_garbage(); + LeaveCriticalSection(&luaCritSec); +} + +void BL3Lua::RemoveAllConsoleCommands() +{ + EnterCriticalSection(&luaCritSec); + if (consoleCommands.size() > 0) { + consoleCommands.clear(); + } + lua->collect_garbage(); + LeaveCriticalSection(&luaCritSec); +} + +void BL3Lua::RemoveConsoleCommand(sol::this_state s, sol::this_environment e, const std::string& command, sol::function func) +{ + int index = -1; + EnterCriticalSection(&luaCritSec); + for (int i = 0; i < consoleCommands.size(); ++i) { + if (std::get<0>(consoleCommands[command]) == func) { + index = i; + break; + } + } + if (index != -1){ + consoleCommands.erase(command); + } + lua->collect_garbage(); + LeaveCriticalSection(&luaCritSec); +} + +void BL3Lua::registerBase() +{ + lua->set_function("IsKeyPressed", sol::overload( + [](std::string keyName) { return LuaUtility::IsKeyPressed(keyName); }, + [](std::string keyName, std::string keyName2) {return LuaUtility::IsKeyPressed(keyName, keyName2); }, + [](std::string keyName, std::string keyName2, std::string keyName3) {return LuaUtility::IsKeyPressed(keyName, keyName2, keyName3); + })); + + lua->set_function("FindObject", [](std::string name) { + auto tmp = SDK::UObject::FindObject(name); + return tmp; + }); + + lua->set_function("StaticFindObject", [](SDK::UClass* pClass, const std::wstring_view name) + { + return StaticFindObject(pClass, reinterpret_cast(-1), &name.data()[0], false); + }); + + lua->set_function("RegisterHotkey", [&](sol::this_state s, sol::this_environment e, sol::variadic_args va) { + RegisterHotkey(s, e, va); + }); + + lua->set_function("ClearHotkeys", [&]() {RemoveAllHotkeys(); }); + + sol::protected_function_result result = lua->safe_script(R"( +function RegisterCallback(onEventName, lCallback) + while true do + if Callbacks then + break + end + end + if Callbacks then + Callbacks[lCallback] = onEventName + else + print("Callback-Table does not exist!") + end +end + +function RunCallbacks(eventName, data) + for key, value in pairs(Callbacks) do + key(data) + end +end + +function internalTDump(object) + if object == nil then + return + end + for key, value in pairs(getmetatable(object)) do + if not string.find(key, "_") and key ~= "new" then + -- if type(value) == "function" then + -- print("[Function] " .. tostring(key)) + -- else + -- print(tostring(key)) + -- end + print(tostring(key)) + end + end +end + +defPrint = print +print = function(...) + for i,v in ipairs{...} do + if PrintGameConsole then + PrintGameConsole(tostring(v)) + end + end + defPrint(...) +end + +)"); + if (!result.valid()) { + sol::error err = result; + std::cout << err.what() << std::endl; + } + + SDK::UObject::SetupLuaBind(lua); + loadlibs(); + adjustRequirePath(); + removeAbusableFuncs(); + callbacks = lua->create_named_table("Callbacks"); +} + +void BL3Lua::loadlibs() +{ + const char* jsonlua = +#include "include/json.lua" + ; + const char* splitlua = +#include "include/split.lua" + ; + lua->require_script("json", jsonlua); + lua->require_script("split", splitlua); +} + +void BL3Lua::adjustRequirePath() { + const std::string defaultPath = (*lua)["package"]["path"]; + wchar_t pBuffer[MAX_PATH]; + GetCurrentDirectory(sizeof(pBuffer), pBuffer); + std::wstring widePath(pBuffer); + std::string path(widePath.begin(), widePath.end()); + + path.append("\\lua\\Modules\\?.lua"); + (*lua)["package"]["path"] = defaultPath + ";" + path; +} + +void BL3Lua::removeAbusableFuncs() { + sol::table global = lua->globals(); + if (global.valid()) { + global["os"]["remove"] = sol::lua_nil; + } +} diff --git a/BL3Lua.h b/BL3Lua.h new file mode 100644 index 0000000..916a314 --- /dev/null +++ b/BL3Lua.h @@ -0,0 +1,61 @@ +#pragma once +#define SOL_EXCEPTIONS_SAFE_PROPAGATION 1 +#define SOL_ALL_SAFETIES_ON 1 +//#include "SOL/sol.hpp" +#include "F:/Programmieren/Lua/sol2/single/single/include/sol/sol.hpp" +#include +#include +#include +#include +//#include "C:/SDK_GEN/BL3/SDK/BL3_Basic.hpp" +//#include "C:/SDK_GEN/BL3/SDK/BL3_CoreUObject_classes.hpp" +#include "C:/SDK_GEN/BL3-2021-4-9/SDK.hpp" +#include + +#include "LuaComponent.h" + +class BL3Lua +{ +public: + BL3Lua(); + ~BL3Lua(); + bool ExecuteScriptFile(const char* filePath, bool printInGame = false); + bool ExecuteScriptString(const char* scriptCode, bool printInGame = false); + void RunCallbacks(const char* callbackName, sol::object data); + void TDump(sol::object tableToDump); + void ClearCallbacks(); + void AddHook(sol::this_state s, sol::this_environment e, const std::string& hookName, sol::function func, bool preHook); + void RemoveHook(sol::this_state s, sol::this_environment e, const std::string& hookName, sol::function func); + void RemoveAllHooks(); + + void AddConsoleCommand(sol::this_state s, sol::this_environment e, const std::string& command, sol::function func); + void RemoveConsoleCommand(sol::this_state s, sol::this_environment e, const std::string& command, sol::function func); + void RemoveAllConsoleCommands(); + + uint32_t RegisterHotkey(sol::this_state s, sol::this_environment e, sol::variadic_args va); + void RemoveHotkey(sol::this_state s, sol::this_environment e, uint32_t, sol::function func); + void RemoveAllHotkeys(); + + void ValidateHotkeys(); + + //std::vector GetHooks(const char* hookName); + //void RegisterUtility(); + +private: + void registerBase(); + void loadlibs(); + void adjustRequirePath(); + void removeAbusableFuncs(); +public: + sol::state* lua; + CRITICAL_SECTION luaCritSec; + std::unordered_map>> procEventHooks; + std::unordered_map> consoleCommands; + std::vector> registeredHotkeys; + + std::vector loadedComponents; +private: + sol::table callbacks; + sol::environment consoleEnvironment; +}; + diff --git a/BL3Lua.vcxproj b/BL3Lua.vcxproj new file mode 100644 index 0000000..a4e797c --- /dev/null +++ b/BL3Lua.vcxproj @@ -0,0 +1,188 @@ + + + + + Debug-JIT + x64 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {df0b7199-3ed8-49dc-9544-46b6315d4390} + BL3Lua + 10.0 + BLua + + + + DynamicLibrary + true + ClangCL + Unicode + + + DynamicLibrary + true + ClangCL + Unicode + + + DynamicLibrary + false + ClangCL + true + Unicode + + + + + + + + + + + + + + + + + + true + C:\SDK_GEN\BL3-2021-4-9\SDK;F:\Programmieren\Lua\sol2\single\single\include;F:\Programmieren\Lua\lua-5.4.2_Win64_vc16_lib\include;F:\Programmieren\Lua;F:\Programmieren\C++\BL3\BL3Lua\include;$(IncludePath) + F:\Programmieren\C++\BL3\BL3Lua\lib;$(LibraryPath) + $(SourcePath) + + + true + C:\SDK_GEN\BL3-2021-4-9\SDK;F:\Programmieren\Lua\sol2\single\single\include;F:\Programmieren\Lua\lua-5.4.2_Win64_vc16_lib\include;F:\Programmieren\Lua;F:\Programmieren\C++\BL3\BL3Lua\include;$(IncludePath) + F:\Programmieren\C++\BL3\BL3Lua\lib;$(LibraryPath) + $(SourcePath) + + + false + C:\SDK_GEN\BL3-2021-4-9\SDK;F:\Programmieren\Lua\sol2\single\single\include;F:\Programmieren\Lua\lua-5.4.2_Win64_vc16_lib\include;F:\Programmieren\Lua;F:\Programmieren\C++\BL3\BL3Lua\include;$(IncludePath) + F:\Programmieren\C++\BL3\BL3Lua\lib;$(LibraryPath) + $(SourcePath) + + + false + + + + Level3 + true + _DEBUG;BL3LUA_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + + + stdcpp17 + stdc17 + /bigobj %(AdditionalOptions) + true + false + Async + false + + + Windows + true + false + lua5.4.2-static.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + true + _DEBUG;BL3LUA_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + + + stdcpp17 + stdc17 + /bigobj %(AdditionalOptions) + true + false + Async + false + + + Windows + true + false + lua5.4.2-static.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;BL3LUA_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + + + stdcpp17 + stdc17 + Speed + true + Async + false + -m64 /GR- %(AdditionalOptions) + false + + + Windows + true + true + true + false + lua5.4.2-static.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + UseLinkTimeCodeGeneration + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BL3Lua.vcxproj.filters b/BL3Lua.vcxproj.filters new file mode 100644 index 0000000..c7e0f75 --- /dev/null +++ b/BL3Lua.vcxproj.filters @@ -0,0 +1,86 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {f40848c6-786f-425a-aeb2-8f803968ded9} + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + SDK-CPP + + + SDK-CPP + + + SDK-CPP + + + SDK-CPP + + + SDK-CPP + + + SDK-CPP + + + SDK-CPP + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + Resource Files + + + \ No newline at end of file diff --git a/BL3Lua.vcxproj.user b/BL3Lua.vcxproj.user new file mode 100644 index 0000000..8c8a144 --- /dev/null +++ b/BL3Lua.vcxproj.user @@ -0,0 +1,18 @@ + + + + E:\SteamLibrary\steamapps\common\Borderlands 3\OakGame\Binaries\Win64\Borderlands3.exe + true + WindowsLocalDebugger + + + E:\SteamLibrary\steamapps\common\Borderlands 3\OakGame\Binaries\Win64\Borderlands3.exe + true + WindowsLocalDebugger + + + E:\SteamLibrary\steamapps\common\Borderlands 3\OakGame\Binaries\Win64\Borderlands3.exe + true + WindowsLocalDebugger + + \ No newline at end of file diff --git a/BL3LuaDLL.cpp b/BL3LuaDLL.cpp new file mode 100644 index 0000000..3e49452 --- /dev/null +++ b/BL3LuaDLL.cpp @@ -0,0 +1,2928 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Link with ws2_32.lib +#pragma comment(lib, "Ws2_32.lib") +#include "F:/Programmieren/C++/json.hpp" +using json = nlohmann::json; + +#include "include/easywsclient.hpp" +#include "include/easywsclient.cpp" + +#include "include/MinHook.h" +#ifdef __clang__ +#pragma comment(lib, "lib/libMinHook-MD.x64.Clang.lib") +#else +#pragma comment(lib, "lib/libMinHook-MD.x64.lib") +#endif + +//#include "F:/Programmieren/C++/myhelpers/xorstr.h" +#define JM_XORSTR_DISABLE_AVX_INTRINSICS 1 +#include "F:/Programmieren/C++/xorstr-master/include/xorstr.hpp" +#include "F:/Programmieren/C++/myhelpers/SuspendResume.hpp" +#include "include/patternscan.hpp" +// +#include "F:/Programmieren/C++/myhelpers/DebugHelper.hpp" +// +//#include "C:/SDK_GEN/BL3/SDK/BL3_Basic.hpp" +//#include "C:/SDK_GEN/BL3/SDK/BL3_CoreUObject_classes.hpp" +#include "C:/SDK_GEN/BL3-2021-4-9/SDK.hpp" +#include "BL3Lua.h" +#include "LuaUtility.h" +#include "Patterns.h" + +#include "spdlog/spdlog.h" +#include "spdlog/sinks/basic_file_sink.h" +#pragma comment(lib, "lib/spdlog.lib") + +using namespace SDK; + +std::string proxyURLA = "https://127.0.0.1:"; +std::string proxyPort = "9999"; + +//static bool alreadyRunning = false; +static bool gObjInit; +static bool gNameInit; +static bool wantsExit; +static bool inCustomOptionsMenu; +//CRITICAL_SECTION critsec; + +BL3Lua *luamgr = NULL; +sol::environment* nonScriptEnv; +using easywsclient::WebSocket; +WebSocket::pointer ws = NULL; +WebSocket::pointer iws = NULL; +bool disconnected = false; +bool idisconnected = false; +spdlog::logger* Logger; + +struct UIData { +public: + std::string DisplayName; + EOptionItemType UIElementType; +}; + +struct UITitle : UIData { +public: +}; + +struct UIButton : UIData { +public: + std::string DescriptionTitle; + std::string DescriptionText; + sol::function OnChangedOrClicked; + lua_State* state; +}; + +struct UISlider : UIData { + std::string DescriptionTitle; + std::string DescriptionText; + sol::function OnChangedOrClicked; + float Value; + float Min; + float Max; + float Step; + bool bIsInt; + lua_State* state; +}; + +struct UISpinner : UIData { + std::string DescriptionTitle; + std::string DescriptionText; + sol::function OnChangedOrClicked; + int Index; + sol::table Options; + lua_State* state; +}; + +struct UIBoolSpinner : UIData { + std::string DescriptionTitle; + std::string DescriptionText; + sol::function OnChangedOrClicked; + bool Value; + std::string OnText; + std::string OffText; + lua_State* state; +}; + +struct UIDropdown : UIData { + std::string DescriptionTitle; + std::string DescriptionText; + sol::function OnChangedOrClicked; + int Index; + sol::table Options; + lua_State* state; +}; + +struct UIControl : UIData { + std::string DescriptionTitle; + std::string DescriptionText; + sol::function OnChangedOrClicked; + lua_State* state; +}; + +struct MenuToCreate { + lua_State* s; + std::string ButtonText; + sol::function OnClick; + sol::function BuildUI; + sol::function ConditionToShow; + + MenuToCreate() + { + s = NULL; + ButtonText = ""; + OnClick = sol::lua_nil; + BuildUI = sol::lua_nil; + ConditionToShow = sol::lua_nil; + } +}; + +static UFunction* onGraphicsClickedFunc = NULL; + +UGbxGFxButton* lastPressedButton = NULL; +UGbxGFxButton* bLuaMenuBtn = NULL; + +std::unordered_map> createdMenuItems; +std::unordered_map uiData; + +std::stack myMenuStack; + +MenuToCreate* bluaMenu = new MenuToCreate; +std::vector menusToCreate; + +sol::table b3hmTable; + +void WebSocketClientThread() +{ + INT rc; + WSADATA wsaData; + + rc = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (rc) + { + printf("WSAStartup Failed.\n"); + return; + } + EnterCriticalSection(&luamgr->luaCritSec); + b3hmTable.set_function("Send", [](const std::string& message) {if (ws) ws->send(message); }); + LeaveCriticalSection(&luamgr->luaCritSec); + while (true) + { + ws = WebSocket::from_url("ws://localhost:9998/ws", "",true); + if (!ws) + continue; + + while (ws->getReadyState() != easywsclient::WebSocket::OPEN) + { + Sleep(5); + } + EnterCriticalSection(&luamgr->luaCritSec); + luamgr->lua->set("websocket", std::ref(ws)); + LeaveCriticalSection(&luamgr->luaCritSec); + disconnected = false; + while (ws->getReadyState() != WebSocket::CLOSED) + { + ws->poll(); + ws->dispatch([&](const std::string &msg) { + json j = json::from_msgpack(msg); + auto eventName = j["EventName"]; + auto content = j["Content"]; + auto bindata = content.get_binary(); + auto output = std::string(bindata.begin(), bindata.end()); + json fin; + fin["EventName"] = j["EventName"]; + fin["Content"] = output; + fin["Error"] = j["Error"]; + if (luamgr) + { + luamgr->RunCallbacks("OnSocketMessage", sol::make_object(*luamgr->lua, fin.dump())); + } + }); + } + EnterCriticalSection(&luamgr->luaCritSec); + luamgr->lua->set("websocket", sol::nil); + LeaveCriticalSection(&luamgr->luaCritSec); + disconnected = true; + delete ws; + ws = NULL; + } +} + +void iWebSocketClientThread() +{ + INT rc; + WSADATA wsaData; + + rc = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (rc) + { + printf("WSAStartup Failed.\n"); + return; + } + EnterCriticalSection(&luamgr->luaCritSec); + /*luamgr->lua->new_usertype( + "ImmediateWebSocket", sol::no_constructor,*/ + b3hmTable.set_function("Connected", []() {return iws != NULL; }); + b3hmTable.set_function("SendAndReceive", + /*"SendData", */[](/*WebSocket::pointer pSocket,*/sol::this_state s, const std::string& msg, const std::string& eventToReceive = "") + { + if (!iws) + return sol::make_object(s.L, sol::lua_nil); + if (iws && iws->getReadyState() != WebSocket::CLOSED) + iws->send(msg); + while (iws && iws->getReadyState() != WebSocket::CLOSED) { + iws->poll(); + std::string response; + iws->dispatch([&](const std::string& incMsg) { + try { + json j = json::from_msgpack(incMsg); + auto eventName = j["EventName"]; + auto content = j["Content"]; + auto bindata = content.get_binary(); + auto output = std::string(bindata.begin(), bindata.end()); + json fin; + fin["EventName"] = j["EventName"]; + fin["Content"] = output; + fin["Error"] = j["Error"]; + if (eventToReceive.size() > 0 && eventName == eventToReceive) + response = fin.dump(); + } + catch (...) {} + }); + if (response.size() > 0) + return sol::make_object(s.L, response); + }}); + /*});*/ + LeaveCriticalSection(&luamgr->luaCritSec); + while (true) + { + iws = WebSocket::from_url("ws://localhost:9998/ws", "", false); + if (!iws || !ws) + continue; + + while (iws->getReadyState() != easywsclient::WebSocket::OPEN) + { + Sleep(5); + } + EnterCriticalSection(&luamgr->luaCritSec); + //luamgr->lua->set("iwebsocket", std::ref(iws)); + //luamgr->lua->set("iwebsocket", [&]() {return iws; }); + LeaveCriticalSection(&luamgr->luaCritSec); + idisconnected = false; + while (ws && ws->getReadyState() != WebSocket::CLOSED) + { + /*iws->poll(-1); + iws->dispatch([&](const std::string& msg) { + json j = json::from_msgpack(msg); + auto eventName = j["EventName"]; + auto content = j["Content"]; + auto bindata = content.get_binary(); + auto output = std::string(bindata.begin(), bindata.end()); + json fin; + fin["EventName"] = j["EventName"]; + fin["Content"] = output; + fin["Error"] = j["Error"]; + if (luamgr) + { + luamgr->RunCallbacks("OnSocketMessage", sol::make_object(*luamgr->lua, fin.dump())); + } + });*/ + Sleep(10); + } + EnterCriticalSection(&luamgr->luaCritSec); + luamgr->lua->set("iwebsocket", sol::nil); + LeaveCriticalSection(&luamgr->luaCritSec); + idisconnected = true; + delete iws; + iws = NULL; + } +} + +void callAtExit() +{ + wantsExit = true; + if (!disconnected) + { + if (ws) + ws->close(); + } + if (!idisconnected) + { + if (iws) + iws->close(); + } + MH_DisableHook(MH_ALL_HOOKS); + MH_Uninitialize(); + + if (luamgr && luamgr->lua) + { + b3hmTable.clear(); + b3hmTable = sol::lua_nil; + lua_close(*luamgr->lua); + } +} + +static UConsole *console = NULL; + +void ConsoleSetup() +{ + while (!*GEngine) + Sleep(20); + if (*GEngine && !console) + { + UEngine *eng = *GEngine; + UInputSettings *settings = NULL; + FName key = FName("F6"); + static FKey consoleKey = {0}; + consoleKey.KeyName = key; + + while (!settings) + settings = UObject::FindObject("InputSettings Engine.Default__InputSettings"); + UInputSettings *staticSettings = settings->GetInputSettings(); + + staticSettings->ConsoleKeys.Data = nullptr; + staticSettings->ConsoleKeys.Count = 0; + staticSettings->ConsoleKeys.Add(consoleKey); + + while (!eng->GameViewport) + { + Sleep(20); + } + if (eng && eng->GameViewport && eng->GameViewport->ViewportConsole) + { + console = eng->GameViewport->ViewportConsole; + } + else + { + UConsole *defaultCon = UObject::FindObject("Console Engine.Default__Console"); + console = reinterpret_cast(StaticConstructObject(defaultCon->Class, defaultCon->Outer, FName("UConsole"), EObjectFlags(0))); + } + + while (true) + { + try + { + if (eng->GameViewport && eng->GameViewport->World && eng->GameViewport->World->OwningGameInstance && eng->GameViewport->World->OwningGameInstance->LocalPlayers.Num() > 0) + { + console->ConsoleTargetPlayer = eng->GameViewport->World->OwningGameInstance->LocalPlayers[0]; + eng->GameViewport->ViewportConsole = console; + break; + } + } + catch (...) + { + } + Sleep(10); + } + } +} + +void AddConsoleKey(const std::string &keyName) +{ + static UInputSettings *settings = NULL; + FName key = FName(keyName); + FKey consoleKey = {0}; + consoleKey.KeyName = key; + while (!settings) + settings = UObject::FindObject("InputSettings Engine.Default__InputSettings"); + UInputSettings *staticSettings = settings->GetInputSettings(); + staticSettings->ConsoleKeys.Add(consoleKey); + staticSettings->ConsoleKeys.Count++; +} + +void SetConsoleKey(const std::string &keyName) +{ + static UInputSettings *settings = NULL; + FName key = FName(keyName); + FKey consoleKey = {0}; + consoleKey.KeyName = key; + + while (!settings) + settings = UObject::FindObject("InputSettings Engine.Default__InputSettings"); + if (settings && settings->IsValidLowLevel()) + { + UInputSettings *staticSettings = settings->GetInputSettings(); + staticSettings->ConsoleKeys.Data = nullptr; + staticSettings->ConsoleKeys.Count = 0; + staticSettings->ConsoleKeys.Add(consoleKey); + } +} + +static UOakCheatManager *cheatManager = NULL; +void CreateCheatManager() +{ + AOakPlayerController* pc = UObject::GetPlayerController(); + while (!pc) { + pc = UObject::GetPlayerController(); + Sleep(20); + } + if (!pc->CheatManager) { + pc->CheatManager = static_cast(StaticConstructObject(pc->CheatClass, pc, FName("UOakCheatManager"))); + pc->CheatManager->ReceiveInitCheatManager(); + } + + if (pc->CheatManager) { + EnterCriticalSection(&luamgr->luaCritSec); + UWorld* world = UObject::GetCurrentWorld(); + while (!world){ + world = UObject::GetCurrentWorld(); + Sleep(20); + } + + luamgr->lua->set_function("TutorialMessage", []() {((UOakCheatManager*)UObject::GetPlayerController()->CheatManager)->TestTutorialWidget(); }); + luamgr->lua->set_function("DumpSave2Json", []() {((UOakCheatManager*)UObject::GetPlayerController()->CheatManager)->DumpSaveToJson(); }); + LeaveCriticalSection(&luamgr->luaCritSec); + } +} + +sol::object GetParamData(lua_State *s, UProperty *property, void *params) +{ + int32_t currentOffset = property->Offset_Internal; + if (property->IsA(UObjectProperty::StaticClass())) + { + sol::table objectData(s, sol::create); + UProperty *current = static_cast(property->Next); + if (!current) + { + return sol::make_object(s, reinterpret_cast((uintptr_t)params + currentOffset)); + } + while (current && current->IsValidLowLevel()) + { + std::string propName = current->GetName(); + if (propName.size() > 0) + { + objectData[propName] = GetParamData(s, current, params); + } + current = static_cast(current->Next); + } + return objectData; + } + else if (property->IsA(UStrProperty::StaticClass())) + { + return sol::make_object(s, *reinterpret_cast((uintptr_t)params + property->Offset_Internal)); + } + else if (property->IsA(UIntProperty::StaticClass())) + { + return sol::make_object(s, *reinterpret_cast((uintptr_t)params + property->Offset_Internal)); + } + else if (property->IsA(UFloatProperty::StaticClass())) + { + return sol::make_object(s, *reinterpret_cast((uintptr_t)params + property->Offset_Internal)); + } + else if (property->IsA(USoftObjectProperty::StaticClass())) + { + return sol::make_object(s, *reinterpret_cast((uintptr_t)params + property->Offset_Internal)); + } + // I don't think this is working correctly + else if (property->IsA(UStructProperty::StaticClass())) + { + std::string currentName = property->GetName(); + UStructProperty *currentStructProperty = reinterpret_cast((uintptr_t)property); + if (!currentStructProperty) + return sol::nil; + UStruct *currentStruct = currentStructProperty->Struct; + sol::table objectData(s, sol::create); + while (currentStruct) + { + for (UProperty *current = static_cast(currentStruct->Children); current; current = static_cast(current->Next)) + { + std::string propName = current->GetName(); + if (propName.size() > 0) + { + objectData[propName] = GetParamData(s, current, reinterpret_cast((uintptr_t)params + property->Offset_Internal)); + } + } + currentStruct = currentStruct->SuperField; + } + return objectData; + } + // Implement proper reading/writing of these + else if (property->IsA(UBoolProperty::StaticClass())) + { + return sol::make_object(s, *reinterpret_cast((uintptr_t)params + property->Offset_Internal)); + } + else if (property->IsA(UNameProperty::StaticClass())) + { + return sol::make_object(s, *reinterpret_cast((uintptr_t)params + property->Offset_Internal)); + } + else if (property->IsA(UInt64Property::StaticClass())) + { + return sol::make_object(s, *reinterpret_cast((uintptr_t)params + property->Offset_Internal)); + } + else if (property->IsA(UUInt64Property::StaticClass())) + { + return sol::make_object(s, *reinterpret_cast((uintptr_t)params + property->Offset_Internal)); + } + //else if (property->IsA(UFunction::StaticClass())) { + // return sol::make_object(s, *reinterpret_cast(property); + //} + else if (property->IsA(UEnumProperty::StaticClass())) + { + return sol::make_object(s, *reinterpret_cast((uintptr_t)params + property->Offset_Internal)); + } + else + { + char buffer[255]; + sprintf_s(buffer, "\"%s\" is a property that is currently not being handled!", property->Class->GetName().c_str()); + std::cout << buffer << std::endl; + return sol::make_object(s, sol::nil); + //throw(buffer); + } + + return sol::nil; +} + +sol::object GetObjectMembers(lua_State *s, UObject *obj) +{ + if (!obj) + return sol::nil; + UStruct *currentClass = static_cast(obj->Class); + sol::table objectData(s, sol::create); + while (currentClass) + { + for (UProperty *current = static_cast(currentClass->Children); current; current = reinterpret_cast(current->Next)) + { + std::string propName = current->GetName(); + if (propName.size() > 0 && !current->IsA(UFunction::StaticClass())) + { + objectData[propName] = GetParamData(s, current, obj); + } + } + currentClass = currentClass->SuperField; + } + return objectData; +} + +sol::object GetFuncParams(lua_State *s, UFunction *func, void *params) +{ + uint8_t numParams = func->NumParms; + uint16_t returnValueOffset = func->ReturnValueOffset; + bool doesReturnAnything = returnValueOffset != 0xFFFF; + + sol::table paramTable(s, sol::create); + UProperty *current = static_cast(func->Children); + while (current && current->IsValidLowLevel()) + { + std::string name = current->GetName(); + if (name.size() > 0) + paramTable[name] = GetParamData(s, current, params); + current = static_cast(current->Next); + } + //if (!doesReturnAnything) + // return sol::nil; + return paramTable; +} + +std::string GetFuncReturnType(UFunction *func) +{ + uint8_t numParams = func->NumParms; + uint16_t returnValueOffset = func->ReturnValueOffset; + bool doesReturnAnything = returnValueOffset != 0xFFFF; + if (!doesReturnAnything) + return ""; + UProperty *current = static_cast(func->Children); + while (current && current->IsValidLowLevel()) + { + std::string name = current->GetName(); + if (name.size() > 0) + if (current->PropertyFlags & EPropertyFlags::CPF_ReturnParm) + return current->Class->GetName(); + current = static_cast(current->Next); + } +} + +static void PrintGameConsole(const std::string &text) +{ + if (console) + { + FString fStringToPrint = FString(text.c_str()); + console->OutputText(&fStringToPrint); + } +} + +void hkProcessEvent(UObject *obj, UFunction *fn, void *parms) +{ + std::string fName = fn->GetFullName(); + const char* funcName = fName.c_str(); + + /* Possibly interesting to inject menudata + Function GbxUI.GbxHintBarWidgetContainer.HintBarInitOwnerInfo + Function GbxUI.GbxHintBarWidgetContainer.HintBarSetVisible + Function GbxUI.GbxGFxButton.K2_HandleFocusableWidgetStateChanged + Function OakGame.OakPlayerController.OnUIMenuActivated + Function OakGame.OakPlayerController.ServerOnUIMenuActivated + Function OakGame.GFxFrontendMenu.OnMenuStackChanged + Function OakGame.MenuMapMenuFlow.OnMenuStackPush + Function GbxUI.GbxHintBarWidgetContainer.HintBarClearAllHints + Function GbxUI.GbxHintBarWidgetContainer.HintBarAppendHint + Function GbxUI.GbxHintWidget.HintWidgetSetVisible + */ + //if (funcName.find("Function OakGame.GFxFrontendMenu.OnMenuStackChanged") != std::string::npos) { + //if (funcName.find("Function OakGame.GFxLoadedNewsImage.OnNewsImageLoadComplete") != std::string::npos) { + /*if (funcName.find("Function Engine.HUD.ReceiveDrawHUD") != std::string::npos) { + + }*/ + + /*if(strcmp(funcName, "Function Engine.Actor.ReceiveTick") != 0 && + strcmp(funcName, "Function Engine.ActorComponent.ReceiveTick") != 0 && + strcmp(funcName, "Function Engine.HUD.ReceiveDrawHUD") != 0 && + strcmp(funcName, "Function Engine.CameraModifier.BlueprintModifyCamera") != 0 && + strcmp(funcName, "Function Engine.CameraModifier.BlueprintModifyPostProcess") != 0 && + strcmp(funcName, "Function Engine.GameMode.ReadyToEndMatch") != 0 && + strcmp(funcName, "Function GbxCameraModes.CameraBehavior.Update") != 0 && + strcmp(funcName, "Function MenuMap_P.MenuMap_P_C.HandRotate__UpdateFunc") != 0 && + strcmp(funcName, "Function BP_TimeOfDay_Base.BP_TimeOfDay_Base_C.ReceiveTick") != 0 && + strcmp(funcName, "Function Engine.AnimNotifyState.Received_NotifyTick") != 0 && + strcmp(funcName, "Function Engine.ActorComponent.ReceiveEndPlay") != 0 && + strcmp(funcName, "Function Engine.AnimInstance.BlueprintInitializeAnimation") != 0 && + strcmp(funcName, "Function Engine.ActorComponent.ReceiveBeginPlay") != 0 && + strcmp(funcName, "Function Engine.Actor.ReceiveBeginPlay") != 0 && + strcmp(funcName, "Function Engine.AnimInstance.BlueprintPostEvaluateAnimation") != 0) + std::cout << funcName << std::endl;*/ + + if (strcmp(funcName, "Function GbxUI.GbxGFxListItemSpinner.OnSpinnerValueUpdated") == 0) { + EnterCriticalSection(&luamgr->luaCritSec); + if (uiData.find(obj) != uiData.end()) { + UGbxGFxListItemSpinner* current = reinterpret_cast(obj); + int index = current->GetSelectionIndex(); + UISpinner* spinner = static_cast(uiData[obj]); + spinner->Index = index; + sol::protected_function func = spinner->OnChangedOrClicked; + if (func.valid()) { + + sol::protected_function_result result; + if (spinner->UIElementType == EOptionItemType::BooleanSpinner) + { + UIBoolSpinner* bSp = reinterpret_cast(spinner); + result = func(bSp, bSp->Value); + } + else { + result = func(spinner, index); + } + if (!result.valid()) + { + PrintGameConsole("Failed to call spinner-callback"); + sol::error err = result; + PrintGameConsole(err.what()); + Logger->error(err.what()); + } + else { + LeaveCriticalSection(&luamgr->luaCritSec); + // Call default handler to update UI-Value and such + return oProcessEvent(obj, fn, parms); + } + } + } + LeaveCriticalSection(&luamgr->luaCritSec); + } + else if (strcmp(funcName, "Function GbxUI.GbxGFxListItemNumber.OnSliderUpdated") == 0) { + EnterCriticalSection(&luamgr->luaCritSec); + if (uiData.find(obj) != uiData.end()) { + UGbxGFxListItemNumber* current = reinterpret_cast(obj); + float value = current->GetCurrentValue(); + UISlider* slider = static_cast(uiData[obj]); + slider->Value = value; + sol::protected_function func = slider->OnChangedOrClicked; + if (func.valid()) { + sol::protected_function_result result; + if(slider->bIsInt) + result = func(slider, (int64_t)value); + else + result = func(slider, value); + if (!result.valid()) + { + PrintGameConsole("Failed to call slider-callback"); + sol::error err = result; + PrintGameConsole(err.what()); + Logger->error(err.what()); + } + else { + LeaveCriticalSection(&luamgr->luaCritSec); + // Call default handler to update UI-Value and such + return oProcessEvent(obj, fn, parms); + } + } + } + LeaveCriticalSection(&luamgr->luaCritSec); + } + else if (strcmp(funcName, "Function OakGame.GFxGraphicsOptions.OnResolutionDropDownSelectionChanged") == 0) { + UGFxGraphicsOptions_OnResolutionDropDownSelectionChanged_Params* thisParams = reinterpret_cast(parms); + EnterCriticalSection(&luamgr->luaCritSec); + if (uiData.find(thisParams->ComboBoxItem) != uiData.end()) { + UGbxGFxListItemComboBox* current = thisParams->ComboBoxItem; + int index = current->GetSelectedIndex(); + UIDropdown* dropdown = static_cast(uiData[thisParams->ComboBoxItem]); + dropdown->Index = index; + sol::protected_function func = dropdown->OnChangedOrClicked; + if (func.valid()) { + sol::protected_function_result result = func(dropdown, index); + if (!result.valid()) + { + PrintGameConsole("Failed to call slider-callback"); + sol::error err = result; + PrintGameConsole(err.what()); + Logger->error(err.what()); + } + } + LeaveCriticalSection(&luamgr->luaCritSec); + // Call default handler to update UI-Value and such + return oProcessEvent(obj, fn, parms); + } + LeaveCriticalSection(&luamgr->luaCritSec); + } + else if (strcmp(funcName, "Function OakGame.GFxGraphicsOptions.OnBenchmarkRunClicked") == 0) { + EnterCriticalSection(&luamgr->luaCritSec); + UGFxMainAndPauseBaseMenu_OnGraphicsClicked_Params* thisParams = reinterpret_cast(parms); + + if (uiData.find(thisParams->PressedButton) != uiData.end()) { + UIButton* btn = static_cast(uiData[thisParams->PressedButton]); + sol::protected_function func = btn->OnChangedOrClicked; + if (func.valid()) { + sol::protected_function_result result = func(btn); + if (!result.valid()) + { + PrintGameConsole("Failed to call button-callback"); + sol::error err = result; + PrintGameConsole(err.what()); + Logger->error(err.what()); + } + else { + LeaveCriticalSection(&luamgr->luaCritSec); + return; + } + } + } + // This really can only be the intended "benchmark"-button + else { + LeaveCriticalSection(&luamgr->luaCritSec); + // Call default handler to update UI-Value and such + return oProcessEvent(obj, fn, parms); + } + } + + // Handle button-submenu MAIN menu + else if (strcmp(funcName, "Function OakGame.GFxOakMainMenu.OnOptionsClicked") == 0) { + UGFxOakMainMenu_OnOptionsClicked_Params* thisParams = static_cast(parms); + if(thisParams->PressedButton == bLuaMenuBtn){ + lastPressedButton = bLuaMenuBtn; + inCustomOptionsMenu = true; + EnterCriticalSection(&luamgr->luaCritSec); + UGFxOakMainMenu* menuToUse = NULL; + if (oGetMapName && oGetMapName().ToString().find("MenuMap_P") != std::string::npos) { + menuToUse = mainMenu; + oProcessEvent(obj, fn, parms); + UGbxGFxGridScrollingList* list = menuToUse->MenuList; + if (list) { + menuToUse->RemoveAllItems(); + for (auto& c : menusToCreate) { + if (menuToUse && menuToUse->IsValidLowLevel()) + { + bool bShow = false; + if (c.ConditionToShow.valid()) { + sol::protected_function_result result = c.ConditionToShow(); + if (result.valid()) + bShow = result; + } + else { + bShow = true; + } + if (bShow) { + int newIndex = menuToUse->AddMenuItem(c.ButtonText, "", false, -1); + if (menuToUse->MenuItems.Data && menuToUse->MenuItems[newIndex].MenuItem) + { + createdMenuItems[menuToUse->MenuItems[newIndex].MenuItem] = std::make_tuple(c.OnClick, c.BuildUI, c.s); + /*if (c.BuildUI.valid()) {*/ + menuToUse->MenuItems[newIndex].MenuItem->OnClicked.FunctionName = onGraphicsClickedFunc->Name; + //} + } + } + } + } + LeaveCriticalSection(&luamgr->luaCritSec); + return; + } + } + LeaveCriticalSection(&luamgr->luaCritSec); + return; + } + else { + lastPressedButton = thisParams->PressedButton; + } + } + // Handle button-submenu PAUSE menu + else if (strcmp(funcName, "Function OakGame.GFxPauseMenu.OnOptionsClicked") == 0) { + UGFxPauseMenu_OnOptionsClicked_Params* thisParams = static_cast(parms); + if (thisParams->PressedButton == bLuaMenuBtn) { + lastPressedButton = bLuaMenuBtn; + inCustomOptionsMenu = true; + EnterCriticalSection(&luamgr->luaCritSec); + UGFxPauseMenu* menuToUse = NULL; + if (oGetMapName && oGetMapName().ToString().find("MenuMap_P") == std::string::npos) { + menuToUse = pauseMenu; + oProcessEvent(obj, fn, parms); + UGbxGFxGridScrollingList* list = menuToUse->MenuList; + if (list) { + menuToUse->RemoveAllItems(); + for (auto& c : menusToCreate) { + if (menuToUse && menuToUse->IsValidLowLevel()) + { + bool bShow = false; + if (c.ConditionToShow.valid()) { + sol::protected_function_result result = c.ConditionToShow(); + if (result.valid()) + bShow = result; + } + else { + bShow = true; + } + if (bShow) { + int newIndex = menuToUse->AddMenuItem(c.ButtonText, "", false, -1); + if (menuToUse->MenuItems.Data && menuToUse->MenuItems[newIndex].MenuItem) + { + createdMenuItems[menuToUse->MenuItems[newIndex].MenuItem] = std::make_tuple(c.OnClick, c.BuildUI, c.s); + /*if (c.BuildUI.valid()) {*/ + menuToUse->MenuItems[newIndex].MenuItem->OnClicked.FunctionName = onGraphicsClickedFunc->Name; + //} + } + } + } + } + LeaveCriticalSection(&luamgr->luaCritSec); + return; + } + } + LeaveCriticalSection(&luamgr->luaCritSec); + return; + } + else { + lastPressedButton = NULL; + } + } + //else if (strcmp(funcName, "Function GbxUI.GbxGFxButton.K2_HandleFocusableWidgetStateChanged") == 0) { + // UGbxGFxButton_K2_HandleFocusableWidgetStateChanged_Params* params = reinterpret_cast(parms); + // if (!parms || !reinterpret_cast(obj)->IsFocusableWidgetStateDown(params->NewState)) + // { + // return oProcessEvent(obj, fn, parms); + // } + // EnterCriticalSection(&luamgr->luaCritSec); + // for (auto& cell : createdMenuItems) { + // UGbxGFxListCell* target = reinterpret_cast(cell.first); + // if (target) { + // if (target == obj) { + // lua_State* lua = std::get<2>(cell.second); + // // Check if there is a "buildUI"-function bound to this + // //sol::reference current(lua, sol::ref_index(std::get<1>(cell.second).registry_index())); + // sol::reference current = std::get<1>(cell.second); + // if (current.valid()) { + // // We don't need to actually call the "buildUI"-func in here. That's handled somewhere else + // UGFxMainAndPauseBaseMenu* menuToUse = NULL; + // if (oGetMapName && oGetMapName().ToString().find("MenuMap_P") != std::string::npos) { + // menuToUse = mainMenu; + // if (menuToUse->MenuItems.Count == 5) + // createdMenuItems.clear(); + // } + // else { + // menuToUse = pauseMenu; + // if (menuToUse->MenuItems.Count == 6) + // createdMenuItems.clear(); + // } + // if (menuToUse) { + // FGbxMenuInputEvent irrelevant; + // // TODO: This appears to cause the bug that randomly opens the graphics options menu + // menuToUse->OnGraphicsClicked(target, irrelevant); + // } + // LeaveCriticalSection(&luamgr->luaCritSec); + // return; + // + // } + // // If there's no "buildUI"-function assigned to this, this is a simple click-handler + // else { + // //sol::reference current(lua, sol::ref_index(std::get<0>(cell.second).registry_index())); + // sol::protected_function current = std::get<0>(cell.second); + // if (current.valid()) { + // //sol::protected_function func(lua, sol::ref_index(std::get<0>(cell.second).registry_index())); + // //sol::protected_function func = std::get<0>(cell.second); + // sol::protected_function_result result = current(obj, fn, parms); + // if (!result.valid()) + // { + // sol::error err = result; + // std::cout << "Error running button handler: " << err.what() << std::endl; + // } + // else + // { + // LeaveCriticalSection(&luamgr->luaCritSec); + // return; + // } + // } + // } + // } + // } + // } + // LeaveCriticalSection(&luamgr->luaCritSec); + //} + else if (strcmp(funcName, "Function OakGame.GFxMainAndPauseBaseMenu.OnGraphicsClicked") == 0) { + UGFxMainAndPauseBaseMenu_OnGraphicsClicked_Params* thisParam = reinterpret_cast(parms); + lastPressedButton = thisParam->PressedButton; + + EnterCriticalSection(&luamgr->luaCritSec); + for (auto& cell : createdMenuItems) { + UGbxGFxListCell* target = reinterpret_cast(cell.first); + if (target) { + if (target == lastPressedButton) { + sol::reference clickHandler = std::get<0>(cell.second); + if (clickHandler.valid()) { + // Call click-handler if there is one + sol::protected_function current = clickHandler; + if (current.valid()) { + sol::protected_function_result result = current(obj, fn, parms); + if (!result.valid()) + { + sol::error err = result; + std::cout << "Error running button handler: " << err.what() << std::endl; + Logger->error(err.what()); + } + } + } + + //Check if there is a "buildUI"-function bound to this + sol::reference buildUIFunc = std::get<1>(cell.second); + if (!buildUIFunc.valid()) { + LeaveCriticalSection(&luamgr->luaCritSec); + return; + } + /*for (auto& m : menusToCreate) { + if (m.BuildUI.valid() && m.BuildUI == buildUIFunc) { + myMenuStack.push(m); + break; + } + }*/ + } + } + } + LeaveCriticalSection(&luamgr->luaCritSec); + return oProcessEvent(obj, fn, parms); + } + else if (strcmp(funcName, "Function OakGame.GFxFrontendMenu.OnMenuStackChanged") == 0) { + UGFxFrontendMenu_OnMenuStackChanged_Params* thisParam = reinterpret_cast(parms); + + UGFxMainAndPauseBaseMenu* menuToUse = NULL; + if (oGetMapName && oGetMapName().ToString().find("MenuMap_P") != std::string::npos) { + menuToUse = mainMenu; + if (menuToUse->MenuItems.Count == 5) + createdMenuItems.clear(); + } + else { + menuToUse = pauseMenu; + if (menuToUse->MenuItems.Count == 6) + createdMenuItems.clear(); + } + // TODO: If other projects figure out or use the same approach, this will most likely fail again :( + // TODO: Properly work out a solution with my own MenuStack + if (menuToUse && menuToUse->MenuList && inCustomOptionsMenu + && wcscmp(menuToUse->MenuList->AllCells[0].Cell->Label->TextFieldData->Text, L"VISUALS") == NULL + && wcscmp(menuToUse->MenuList->AllCells[1].Cell->Label->TextFieldData->Text, L"SOUND") == NULL) { + //if (menuToUse && menuToUse->MenuList && inCustomOptionsMenu && !myMenuStack.empty()) { + oProcessEvent(obj, fn, parms); + menuToUse->RemoveAllItems(); + for (auto& c : menusToCreate) { + if (menuToUse && menuToUse->IsValidLowLevel()) + { + bool bShow = false; + if (c.ConditionToShow.valid()) { + sol::protected_function_result result = c.ConditionToShow(); + if (result.valid()) + bShow = result; + } + else { + bShow = true; + } + if (bShow) { + int newIndex = menuToUse->AddMenuItem(c.ButtonText, "", false, -1); + if (menuToUse->MenuItems.Data && menuToUse->MenuItems[newIndex].MenuItem) + { + createdMenuItems[mainMenu->MenuItems[newIndex].MenuItem] = std::make_tuple(c.OnClick, c.BuildUI, c.s); + /*if (c.BuildUI.valid()) {*/ + menuToUse->MenuItems[newIndex].MenuItem->OnClicked.FunctionName = onGraphicsClickedFunc->Name; + //} + } + } + } + } + //LeaveCriticalSection(&luamgr->luaCritSec); + //myMenuStack.pop(); + return; + } + return oProcessEvent(obj, fn, parms); + } + //if (strstr(funcName, "GbxGFxButton") != 0) { + // std::cout << funcName << std::endl; + //} + //if (strstr(funcName, "MenuStack") != 0) { + // std::cout << funcName << std::endl; + //} + //if (strstr(funcName, "OnBLuaClicked") != 0) { + // std::cout << funcName << std::endl; + //} + //if (strcmp(funcName, "Function ScaleformUI.GFxMoviePlayer.ReceiveGFxCommand") == 0) { + // std::cout << funcName << std::endl; + //} + if (luamgr && luamgr->lua) + { + EnterCriticalSection(&luamgr->luaCritSec); + // TODO: Probably need to move this into a lua-function to avoid jumping into and out of lua + if (luamgr->procEventHooks.find(funcName) != luamgr->procEventHooks.end()) + { + for (auto &hook : luamgr->procEventHooks[funcName]) + { + try { + lua_State* lua = std::get<1>(hook); + //sol::protected_function current(lua, sol::ref_index(std::get<0>(hook).registry_index())); + sol::reference current = std::get<0>(hook); + if (current.valid()) + { + /* sol::object objData = GetObjectMembers(lua, obj); + sol::object paramObj = GetFuncParams(lua, fn, parms); + if (!objData.valid())*/ + //sol::object objData = sol::make_object(lua, obj); + //if (!paramObj.valid()) + //sol::object paramObj = sol::make_object(lua, parms); + bool isPreHook = std::get<2>(hook); + if (isPreHook) + { + /*sol::protected_function func(lua, sol::ref_index(std::get<0>(hook).registry_index())); + if (!func.valid()) { + LeaveCriticalSection(&luamgr->procEventCritSec); + return oProcessEvent(obj, fn, parms); + }*/ + sol::protected_function_result result; + //result = func(objData, fn, paramObj); + sol::protected_function func = current; + result = func(obj); + if (!result.valid()) + { + sol::error err = result; + std::cout << "Error running hook: " << err.what() << std::endl; + Logger->error(err.what()); + } + else + { + if (result.return_count() == 1) + { + bool returnValue = result; + if (returnValue) + { + LeaveCriticalSection(&luamgr->luaCritSec); + return; + } + } + //else if (result.return_count() == 3) { + // /*std::tuple returnedObjects = result; + // obj = std::get<0>(returnedObjects); + // fn = reinterpret_cast(std::get<1>(returnedObjects)); + // parms = std::get<2>(returnedObjects);*/ + //} + } + } + else + { + oProcessEvent(obj, fn, parms); + if (fn->ReturnValueOffset == 0xFFFF) + { + LeaveCriticalSection(&luamgr->luaCritSec); + return; + } + + //Handle postHook to pass and return result + sol::object paramObj = GetFuncParams(lua, fn, parms); + std::string returnValueTypeName = GetFuncReturnType(fn); + + //sol::protected_function func(lua, sol::ref_index(std::get<0>(hook).registry_index())); + sol::reference funcRef = std::get<0>(hook); + if (funcRef.valid()) { + sol::protected_function func = std::get<0>(hook); + sol::protected_function_result result; + result = func(obj, fn, parms); + if (!result.valid()) + { + sol::error err = result; + std::cout << "Error running hook: " << err.what() << std::endl; + Logger->error(err.what()); + } + else + { + if (result.return_count() == 1) + { + sol::object returnValue = result; + if (returnValue.valid()) + { + if (returnValueTypeName == "FloatProperty") + { + if (returnValue.is()) + { + (*(float*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "DoubleProperty") + { + if (returnValue.is()) + { + (*(double*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "BoolProperty") + { + if (returnValue.is()) + { + (*(bool*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "ByteProperty") + { + if (returnValue.is()) + { + (*(uint8_t*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "IntProperty") + { + if (returnValue.is()) + { + (*(int*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "Int8Property") + { + if (returnValue.is()) + { + (*(int8_t*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "Int16Property") + { + if (returnValue.is()) + { + (*(int16_t*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "Int64Property") + { + if (returnValue.is()) + { + (*(int64_t*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "UInt16Property") + { + if (returnValue.is()) + { + (*(uint32_t*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "UInt32Property") + { + if (returnValue.is()) + { + (*(uint32_t*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "UInt64Property") + { + if (returnValue.is()) + { + (*(uint64_t*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "UInt64Property") + { + if (returnValue.is()) + { + (*(uint64_t*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "StrProperty") + { + if (returnValue.is()) + { + (*(FString*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "TextProperty") + { + if (returnValue.is()) + { + (*(FText*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "NameProperty") + { + if (returnValue.is()) + { + (*(FName*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "VectorProperty") + { + if (returnValue.is()) + { + (*(FVector*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + else if (returnValueTypeName == "RotatorProperty") + { + if (returnValue.is()) + { + (*(FRotator*)((uintptr_t)parms + fn->ReturnValueOffset)) = returnValue.as(); + } + } + } + LeaveCriticalSection(&luamgr->luaCritSec); + return; + } + } + } + } + } + } + catch(...){} + } + } + LeaveCriticalSection(&luamgr->luaCritSec); + } + return oProcessEvent(obj, fn, parms); +} + +static bool endsWith(std::wstring str, std::wstring suffix) +{ + return str.size() >= suffix.size() && 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix); +} + +/* + * Find Case Insensitive Sub String in a given substring + */ +static size_t findCaseInsensitive(std::string data, std::string toSearch, size_t pos = 0) +{ + // Convert complete given String to lower case + std::transform(data.begin(), data.end(), data.begin(), ::tolower); + // Convert complete given Sub String to lower case + std::transform(toSearch.begin(), toSearch.end(), toSearch.begin(), ::tolower); + // Find sub string in given string + return data.find(toSearch, pos); +} + +static void ToggleNoclip() +{ + static bool flyIsActive = false; + AOakPlayerController *pc = UObject::GetPlayerController(); + if (pc) + { + ACharacter *character = pc->GetAssociatedCharacter(); + if (character) + { + if (flyIsActive) + { + character->CharacterMovement->bCheatFlying = 0; + character->CharacterMovement->MovementMode = EMovementMode::MOVE_Walking; + character->CharacterMovement->MaxFlySpeed.Value = 600.0f; + character->SetActorEnableCollision(true); + flyIsActive = false; + PrintGameConsole("noclip off"); + } + else + { + character->CharacterMovement->bCheatFlying = 1; + character->CharacterMovement->MovementMode = EMovementMode::MOVE_Flying; + character->CharacterMovement->MaxFlySpeed.Value = 2000.0f; + character->SetActorEnableCollision(false); + flyIsActive = true; + PrintGameConsole("noclip on"); + } + } + } +} + +static void ToggleCam() +{ + static bool isThirdPerson = false; + static FName defaultCam = FName("Default"); + static FName thirdPerson = FName("ThirdPerson"); + AOakPlayerController *pc = UObject::GetPlayerController(); + if (pc) + { + if (!isThirdPerson) + { + pc->ClientSetCameraMode(thirdPerson); + isThirdPerson = true; + PrintGameConsole("Thirdperson cam activated!"); + } + else + { + pc->ClientSetCameraMode(defaultCam); + isThirdPerson = false; + PrintGameConsole("Firstperson cam activated!"); + } + } +} + +bool hkStaticExec(UWorld *world, const wchar_t *input, FOutputDevice &outputDevice) +{ + if (wcsncmp(input, L"cls", 3) == NULL) + { + console->ClearOutput(); + console->SBHead = 0; + system("cls"); + return true; + } + else if (wcsncmp(input, L"noclip", 6) == NULL) + { + ToggleNoclip(); + return true; + } + else if (wcsncmp(input, L"togglecam", 9) == NULL) + { + ToggleCam(); + return true; + } + else if (wcsncmp(input, L"lua ", 4) == NULL) + { + if (wcslen(input) <= 5) + return false; + std::wstring data(input + 5); + std::string luaData(data.begin(), data.end()); + if (luamgr) + luamgr->ExecuteScriptString(luaData.c_str(), true); + return true; + } + else if (wcsncmp(input, L"luaex", 5) == NULL) + { + if (wcslen(input) <= 6) + return false; + std::wstring data(input + 6); + std::string luaData(data.begin(), data.end()); + if (findCaseInsensitive(luaData, "./lua/") == std::string::npos) { + luaData = "./lua/" + luaData; + } + if (luamgr) + return luamgr->ExecuteScriptFile(luaData.c_str(), true); + return true; + } + // The returned numbers did not make any sense? + // Memory usage was the same with this injected and without + /*else if (wcsncmp(input, L"luamem", 6) == NULL) + { + sol::protected_function func = (*luamgr->lua)["PrintGameConsole"]; + if (func.valid()) { + char buf[150]; + size_t memory = sol::total_memory_used(luamgr->lua->lua_state()); + sprintf_s(buf, sizeof(buf), "%dkB (%dMB) memory in use by sol (lua).", memory, memory / 1024); + func(buf); + } + return true; + }*/ + if (luamgr && luamgr->lua) + { + EnterCriticalSection(&luamgr->luaCritSec); + for (auto &commands : luamgr->consoleCommands) + { + size_t prefixLength = std::get<2>(commands.second); + std::wstring widenedPrefix = std::wstring(commands.first.begin(), commands.first.end()); + if (!endsWith(widenedPrefix, std::wstring(L" "))) + widenedPrefix.append(L" "); + if (widenedPrefix.size() >= wcslen(input)) + { + LeaveCriticalSection(&luamgr->luaCritSec); + return false; + } + if (wcsncmp(widenedPrefix.c_str(), input, widenedPrefix.size()) == NULL) + { + lua_State *lua = std::get<1>(commands.second); + //sol::reference current(lua, sol::ref_index(std::get<0>(commands.second).registry_index())); + sol::protected_function current = std::get<0>(commands.second); + if (current.valid()) + { + //sol::protected_function func(lua, sol::ref_index(std::get<0>(commands.second).registry_index())); + sol::protected_function_result result = current(input + prefixLength + 1); + if (!result.valid()) + { + sol::error err = result; + std::cout << "Error running console command: " << err.what() << std::endl; + Logger->error(err.what()); + } + else + { + if (result.return_count() == 1) + { + bool returnValue = result; + LeaveCriticalSection(&luamgr->luaCritSec); + return returnValue; + } + } + } + } + } + LeaveCriticalSection(&luamgr->luaCritSec); + } + bool returnVal = oStaticExec(world, input, outputDevice); + // This was not handled by anything. Possibly invalid, but still trying to run it as lua-code + if (!returnVal) + { + if (luamgr) + { + EnterCriticalSection(&luamgr->luaCritSec); + std::wstring tmp(input); + std::string narrowed(tmp.begin(), tmp.end()); + bool result = luamgr->ExecuteScriptString(narrowed.c_str()); + LeaveCriticalSection(&luamgr->luaCritSec); + return result; + } + } + return returnVal; +} + +void hkCallFunction(UObject* thisZ, FFrame& frame, void* paramResult, UFunction* func) { + const char* funcName = func->GetFullNameC(); + if (strstr(funcName, "BLua") != 0) { + std::cout << "Tried to call a func containing \"BLua\"" << std::endl; + } + if (thisZ == bLuaMenuBtn) { + std::cout << "BLuaMenuBtn called: " << funcName << std::endl; + } + if (!oCallFunc) return; + return oCallFunc(thisZ, frame, paramResult, func); +} + +struct FUniqueNetId { + char unknown[0x18]; +}; + +typedef void(__fastcall* tDoSparkAuth)(void* FOakPlayerManager, const TSharedRef& something, void* something2); +tDoSparkAuth oDoSparkAuth = NULL; + +void* oakPlayerManager = NULL; +TSharedRef* Something = new TSharedRef; +void* Something2 = NULL; +void hkDoSparkAuth(void* FOakPlayerManager, const TSharedRef& something, void* something2) { + oakPlayerManager = FOakPlayerManager; + //Something = something; + something2 = something2; + memcpy(Something, &something, sizeof(TSharedRef)); + + return oDoSparkAuth(FOakPlayerManager, something, something2); +} + +class FOakStartupProcess +{ +public: + char pad_0000[60]; //0x0000 + uint8_t StepToPerform; //0x003C + bool bInitDone; //0x003D + char pad_003E[162]; //0x003E +}; //Size: 0x00E0 +static_assert(sizeof(FOakStartupProcess) == 0xE0); + +typedef void(__fastcall* tStartupProcessPerformStep)(FOakStartupProcess* startupProcess/*, const FName& seemsWrong*/); +tStartupProcessPerformStep oStartupProcessPerformStep = NULL; + +FOakStartupProcess* oakStartupProcess = new FOakStartupProcess; +//void* Something2 = NULL; +void hkStartupProcessPerformStep(FOakStartupProcess* startupProcess/*, void* seemsWrong*/) { + if (startupProcess->StepToPerform == 0) + memcpy(oakStartupProcess, startupProcess, sizeof(FOakStartupProcess)); + //oakStartupProcess = startupProcess; + return oStartupProcessPerformStep(startupProcess/*, NULL*/); +} + +void* FGbxSparkModule = NULL; +typedef void* (__fastcall* tGbxSparkModuleGet)(); +tGbxSparkModuleGet oGbxSparkModuleGet = NULL; + +struct testStruct { + char unknown[0x340]; +}; + +typedef bool (__fastcall* tFSparkInitProcessStartProcess)(testStruct*); +tFSparkInitProcessStartProcess oFSparkInitProcessStartProcess = NULL; + +testStruct* sparkInitProcess = new testStruct; +testStruct* gameSparkInitProcess = NULL; +bool hkSparkInitProcessStartProcess(testStruct* thisZ) { + bool result = false; + gameSparkInitProcess = thisZ; + if (oFSparkInitProcessStartProcess && thisZ != sparkInitProcess) { + memcpy(sparkInitProcess, thisZ, sizeof(testStruct)); + } + result = oFSparkInitProcessStartProcess(thisZ); + return result; +} + +typedef void* (__fastcall* tFSparkInitProcessReadDiscovery)(testStruct*, bool baseInitWasDone); +tFSparkInitProcessReadDiscovery oFSparkInitProcessReadDiscovery = NULL; + +void* hkSparkInitProcessReadDiscovery(testStruct* thisZ, bool baseInitWasDone) { + void* result = NULL; + if (oFSparkInitProcessReadDiscovery) + result = oFSparkInitProcessReadDiscovery(thisZ, baseInitWasDone); + return result; +} + +static void* somethingNecessary = NULL; +static void* somethingNecessary2 = NULL; +void* hkOakGameInstanceProcessPendingMicropatches(UOakGameInstance* thisZ) { + if(!somethingNecessary) + somethingNecessary = *(void**)((uintptr_t)thisZ + 0x11A0); + if(!somethingNecessary2) + somethingNecessary2 = *(void**)((uintptr_t)thisZ + 0x11A8); + //void* somethingNecessary = (void*)&thisZ->UnknownData10[0x27B]; + void* result = NULL; + result = oOakGameInstanceProcessPendingMicropatches(thisZ); + return result; +} + +FOakStartupProcess* test = NULL; +void RefreshHotfixes() +{ + if (oFSparkInitProcessReadDiscovery && gameSparkInitProcess){ + UOakGameInstance* instance = UObject::GetGameInstance(); + if (instance) { + if (somethingNecessary && somethingNecessary2) { + *(void**)((uintptr_t)instance + 0x11A0) = somethingNecessary; + *(void**)((uintptr_t)instance + 0x11A8) = somethingNecessary2; + } + oFSparkInitProcessReadDiscovery(gameSparkInitProcess, false); + oFSparkInitProcessReadDiscovery(gameSparkInitProcess, true); + + //hkOakGameInstanceProcessPendingMicropatches(instance); + if (somethingNecessary && somethingNecessary2) { + *(void**)((uintptr_t)instance + 0x11A0) = somethingNecessary; + *(void**)((uintptr_t)instance + 0x11A8) = somethingNecessary2; + } + FGbxMenuInputEvent event = { 0 }; + instance->OnConfirmApplyMicropatches(NULL, FName("Yes"), event); + + AOakGameState* state = UObject::GetGameState(); + if (state) + state->RefreshMicropatchSwitches(); + } + } + /*if (oFSparkInitProcessStartProcess && sparkInitProcess) + hkSparkInitProcessStartProcess(sparkInitProcess);*/ + /*if (oGbxSparkModuleGet) + FGbxSparkModule = oGbxSparkModuleGet();*/ + /*if (oStartupProcessPerformStep && oakStartupProcess) { + if (test) + free(test); + test = new FOakStartupProcess; + memcpy(test, oakStartupProcess, sizeof(FOakStartupProcess)); + test->bInitDone = true; + oStartupProcessPerformStep(test); + }*/ + /*if (oDoSparkAuth && oakPlayerManager) { + AOakPlayerController* pc = UObject::GetPlayerController(); + if(pc){ + FUniqueNetIdRepl id = ((AOakPlayerState*)(pc->PlayerState))->ShiftUniqueId; + oDoSparkAuth(oakPlayerManager, *Something, Something2); + } + }*/ + + /*static uintptr_t doSparkAuth = PatternScan::FindSignature(NULL, FOakPlayerManager__DoSparkAuthentication.crypt_get()); + while(!doSparkAuth) + doSparkAuth = PatternScan::FindSignature(NULL, FOakPlayerManager__DoSparkAuthentication.crypt_get());*/ + /*UOakGameInstance* gameInstance = UObject::GetGameInstance(); + while(!gameInstance) + gameInstance = UObject::GetGameInstance();*/ + + /*typedef char(__fastcall* tProcessPendingMicropatches)(UOakGameInstance*); + static tProcessPendingMicropatches oProcessPendingMicropatches = (tProcessPendingMicropatches)test;*/ + + //char result = oProcessPendingMicropatches(gameInstance); + + /*typedef LRESULT(__cdecl * tRefresh)(); + static tRefresh oRefresh = NULL; + + if (!oRefresh) + { + HMODULE proxyDLLHandle = GetModuleHandleA("BL3ProxySettings.dll"); + if (proxyDLLHandle) + { + FARPROC address = GetProcAddress(proxyDLLHandle, "RefreshHotfixes"); + if (address) + { + oRefresh = (tRefresh)address; + } + } + } + if (oRefresh) + oRefresh();*/ +} + +HMODULE thisDLL; +static HMODULE forRefCount; + +void FindPatternsForTypedefs() +{ + while (!oGetTransientPackage) + { + uintptr_t patternAddress = PatternScan::FindSignature(NULL, getTransientPackagePattern.crypt_get()); + if (patternAddress) + { + patternAddress -= 72 + 5; + int32_t offset = *(int32_t *)(patternAddress + 1); + if (offset) + { + oGetTransientPackage = (tGetTransientPackage)(patternAddress + offset + 5); + } + } + } + while (!oMalloc) + oMalloc = (tMalloc)PatternScan::FindSignature(NULL, mallocPattern.crypt_get()); + while (!oRealloc) + oRealloc = (tRealloc)PatternScan::FindSignature(NULL, reallocPattern.crypt_get()); + while (!oFNameCtor) + oFNameCtor = (tFNameCtor)PatternScan::FindSignature(NULL, fnamectorPattern.crypt_get()); + while (!oFTextFromString) + oFTextFromString = (tFtextFromString)PatternScan::FindSignature(NULL, ftextfromstringPattern.crypt_get()); + while (!oGetWorldFromContextObject) + oGetWorldFromContextObject = (tGetWorldFromContextObject)PatternScan::FindSignature(NULL, getWorldFromContextObjectPattern.crypt_get()); + while (!oIsValidLowLevel) + oIsValidLowLevel = (tIsValidLowLevel)PatternScan::FindSignature(NULL, isValidLowLevelPattern.crypt_get()); + while (!oStaticConstructObject) + oStaticConstructObject = (tStaticConstructObject)PatternScan::FindSignature(NULL, staticConstructObjectPattern.crypt_get()); + while (!oStaticDuplicateObject) + oStaticDuplicateObject = (tStaticDuplicateObject)PatternScan::FindSignature(NULL, staticDuplicateObjectPattern.crypt_get()); + /*while (!oNewObject) + oNewObject = (tNewObject)PatternScan::FindSignature(NULL, newObjectPattern.crypt_get());*/ + while (!oGetWorld) + oGetWorld = (tGetWorld)PatternScan::FindSignature(NULL, getWorldPattern.crypt_get()); + while (!oSpawnActor) + oSpawnActor = (tSpawnActor)PatternScan::FindSignature(NULL, spawnActorPattern.crypt_get()); + while (!oGetMapName) + oGetMapName = (tGetMapName)PatternScan::FindSignature(NULL, getMapNamePattern.crypt_get()); + while (!oFuncInvoke) + oFuncInvoke = (tFuncInvoke)PatternScan::FindSignature(NULL, functionInvokePattern.crypt_get()); + while (!oCallFunc) + { + uintptr_t patternAddress = PatternScan::FindSignature(NULL, callFuncPattern.crypt_get()); + if (patternAddress) + { + patternAddress += 100; + int32_t offset = *(int32_t *)(patternAddress + 1); + if (offset) + { + oCallFunc = (tCallFunc)(patternAddress + offset + 5); + } + } + } + while (!oStaticExec) + { + uintptr_t patternAddress = PatternScan::FindSignature(NULL, staticExecPattern.crypt_get()); + if (patternAddress) + { + patternAddress += 102; + int32_t offset = *(int32_t *)(patternAddress + 1); + if (offset) + { + oStaticExec = (tStaticExec)(patternAddress + offset + 5); + } + } + } + while (!oGetGlobalLogSingleton) + oGetGlobalLogSingleton = (tGetGlobalLogSingleton)PatternScan::FindSignature(NULL, getGlobalLogSingletonPattern.crypt_get()); + while (!oOutputDeviceShow) + oOutputDeviceShow = (tOutputDeviceShow)PatternScan::FindSignature(NULL, consoleOutputDeviceShowPattern.crypt_get()); + while (!oAddOutputDevice) + oAddOutputDevice = (tAddOutputDevice)PatternScan::FindSignature(NULL, addOutputDevicePattern.crypt_get()); + while (!oCreateConsoleOutputDevice) + oCreateConsoleOutputDevice = (tCreateConsoleOutputDevice)PatternScan::FindSignature(NULL, createConsoleOutputDevice.crypt_get()); + while (!oConsoleOutputText) + oConsoleOutputText = (tConsoleOutputText)PatternScan::FindSignature(NULL, consoleOutputTextPattern.crypt_get()); + while (!oConsoleClearOutput) + oConsoleClearOutput = (tConsoleClearOutput)PatternScan::FindSignature(NULL, consoleClearOutputPattern.crypt_get()); + while (!oAddToViewport) + oAddToViewport = (tAddToViewport)PatternScan::FindSignature(NULL, addToViewportPattern.crypt_get()); + while (!oStaticFindObject) + oStaticFindObject = (tStaticFindObject)PatternScan::FindSignature(NULL, staticFindObjectPattern.crypt_get()); + while (!oMainAndPauseMenuAddMenuItem) + oMainAndPauseMenuAddMenuItem = (tMainAndPauseMenuAddMenuItem)PatternScan::FindSignature(NULL, mainAndPauseMenuAddMenuItemPattern.crypt_get()); + while (!oMainAndPauseMenuRemoveAllItems) + oMainAndPauseMenuRemoveAllItems = (tMainAndPauseMenuRemoveAllItems)PatternScan::FindSignature(NULL, mainAndPauseMenuRemoveAllItemsPattern.crypt_get()); + while (!oMainAndPauseMenuStartMenuTransition) + oMainAndPauseMenuStartMenuTransition = (tMainAndPauseMenuStartMenuTransition)PatternScan::FindSignature(NULL, mainAndPauseMenuStartMenuTransitionPattern.crypt_get()); + while (!oGridScrollingListInsertListItem) + oGridScrollingListInsertListItem = (tGridScrollingListInsertListItem)PatternScan::FindSignature(NULL, scrollingListInsertListItemPattern.crypt_get()); + while (!oListItemNumberInitializeItem) + oListItemNumberInitializeItem = (tListItemNumberInitializeItem)PatternScan::FindSignature(NULL, listItemNumberInitializeItemPattern.crypt_get()); + while (!oOptionBaseCreateContentPanel) + oOptionBaseCreateContentPanel = (tOptionBaseCreateContentPanel)PatternScan::FindSignature(NULL, optionBaseCreateContentPanelPattern.crypt_get()); + while (!oOptionBaseCreateContentPanelItem) + oOptionBaseCreateContentPanelItem = (tOptionBaseCreateContentPanelItem)PatternScan::FindSignature(NULL, optionBaseCreateContentPanelItemPattern.crypt_get()); + + while (!oOptionBaseSetupSpinnerItem) + oOptionBaseSetupSpinnerItem = (tOptionBaseSetupSpinnerItem)PatternScan::FindSignature(NULL, optionBaseSetupSpinnerItemPattern.crypt_get()); + while (!oOptionBaseSetupSpinnerItemAsBoolean) + oOptionBaseSetupSpinnerItemAsBoolean = (tOptionBaseSetupSpinnerItemAsBoolean)PatternScan::FindSignature(NULL, optionBaseSetupSpinnerItemAsBooleanPattern.crypt_get()); + while (!oOptionBaseSetupSpinnerItemWithText) + oOptionBaseSetupSpinnerItemWithText = (tOptionBaseSetupSpinnerItemWithText)PatternScan::FindSignature(NULL, optionBaseSetupSpinnerItemWithTextPattern.crypt_get()); + while (!oOptionBaseSetupButtonItem) + oOptionBaseSetupButtonItem = (tOptionBaseSetupButtonItem)PatternScan::FindSignature(NULL, optionBaseSetupButtonItemPattern.crypt_get()); + while (!oOptionBaseSetupTitleItem) + oOptionBaseSetupTitleItem = (tOptionBaseSetupTitleItem)PatternScan::FindSignature(NULL, optionBaseSetupTitleItemPattern.crypt_get()); + while (!oOptionBaseSetupSliderItem) + oOptionBaseSetupSliderItem = (tOptionBaseSetupSliderItem)PatternScan::FindSignature(NULL, optionBaseSetupSliderItemPattern.crypt_get()); + while (!oOptionBaseSetupDropdownListItem) + oOptionBaseSetupDropdownListItem = (tOptionBaseSetupDropdownListItem)PatternScan::FindSignature(NULL, optionBaseSetupDropdownListItemPattern.crypt_get()); + while (!oOptionBaseSetupControlsItem) + oOptionBaseSetupControlsItem = (tOptionBaseSetupControlsItem)PatternScan::FindSignature(NULL, optionBaseSetupControlsItemPattern.crypt_get()); + + while (!oGFxOptionsMenuLoadBaseOptionsMenu) + oGFxOptionsMenuLoadBaseOptionsMenu = (tGFxOptionsMenuLoadBaseOptionsMenu)PatternScan::FindSignature(NULL, optionBaseMenuLoadBaseOptionsMenuPattern.crypt_get()); + while (!oGFxOptionsMenuCreateOptionPanel) + oGFxOptionsMenuCreateOptionPanel = (tGFxOptionsMenuCreateOptionPanel)PatternScan::FindSignature(NULL, gfxOptionsMenuCreateOptionPanelPattern.crypt_get()); + while (!oGFxOptionsMenuOnGFxMenuIsInitedAndStartedBegin) + oGFxOptionsMenuOnGFxMenuIsInitedAndStartedBegin = (tGFxOptionsMenuOnGFxMenuIsInitedAndStartedBegin)PatternScan::FindSignature(NULL, gfxOptionsMenuOnGFxMenuIsInitedAndStartedBeginPattern.crypt_get()); + while (!oListItemSpinnerSetDefaultValue) + oListItemSpinnerSetDefaultValue = (tListItemSpinnerSetDefaultValue)PatternScan::FindSignature(NULL, listItemSpinnerSetDefaultValuePattern.crypt_get()); + while (!oListItemNumberGetCurrentValue) + { + uintptr_t patternAddress = PatternScan::FindSignature(NULL, listItemNumberGetCurrentValuePattern.crypt_get()); + if (patternAddress) + { + patternAddress += 41; + int32_t offset = *(int32_t*)(patternAddress + 1); + if (offset) + { + oListItemNumberGetCurrentValue = (tListItemNumberGetCurrentValue)(patternAddress + offset + 5); + } + } + } + while (!oListItemNumberSetDefaultValue) + oListItemNumberSetDefaultValue = (tListItemNumberSetDefaultValue)PatternScan::FindSignature(NULL, listItemNumberSetDefaultValuePattern.crypt_get()); + while (!oListItemComboBoxGetSelectedIndex) + oListItemComboBoxGetSelectedIndex = (tListItemComboBoxGetSelectedIndex)PatternScan::FindSignature(NULL, listItemComboBoxGetSelectedIndexPattern.crypt_get()); + while (!oListItemComboBoxSetDefaultValue) + oListItemComboBoxSetDefaultValue = (tListItemComboBoxSetDefaultValue)PatternScan::FindSignature(NULL, listItemComboBoxSetDefaultValuePattern.crypt_get()); + while (!oListItemSpinnerGetSelectionIndex) + oListItemSpinnerGetSelectionIndex = (tListItemSpinnerGetSelectionIndex)PatternScan::FindSignature(NULL, listItemSpinnerGetCurrentSelectionIndexPattern.crypt_get()); + while (!oGbxSparkModuleGet) + oGbxSparkModuleGet = (tGbxSparkModuleGet)PatternScan::FindSignature(NULL, FGbxSparkModuleGetPattern.crypt_get()); + while (!oFSparkInitProcessStartProcess) + oFSparkInitProcessStartProcess = (tFSparkInitProcessStartProcess)PatternScan::FindSignature(NULL, FSparkInitProcessStartProcessPattern.crypt_get()); + while (!oFSparkInitProcessReadDiscovery) + oFSparkInitProcessReadDiscovery = (tFSparkInitProcessReadDiscovery)PatternScan::FindSignature(NULL, FSparkInitProcessReadDiscoveryPattern.crypt_get()); + while (!oOakGameInstanceProcessPendingMicropatches) + oOakGameInstanceProcessPendingMicropatches = (tOakGameInstanceProcessPendingMicropatches)PatternScan::FindSignature(NULL, oakGameInstanceProcessPendingMicropatchesPattern.crypt_get()); + while (!oOakGameStateRefreshMicropatchSwitches) + oOakGameStateRefreshMicropatchSwitches = (tOakGameStateRefreshMicropatchSwitches)PatternScan::FindSignature(NULL, oakGameStateRefreshMicropatchSwitchesPattern.crypt_get()); + /*while (!oDumpObjectToString) + oDumpObjectToString = (tDumpObjectToString)PatternScan::FindSignature(NULL, dumpObjectToStringPattern.crypt_get());*/ +} + +static FWindowsConsoleOutputDevice *myConsoleOutputDevice; + +static AOakPlayerController *Test(UOakDeveloperPerks *thisz) +{ + return UObject::GetPlayerController(); +} + +void hkAddToViewport(UUserWidget *thisZ, int32_t ZOrder) +{ + std::cout << thisZ->GetFullName() << std::endl; + return oAddToViewport(thisZ, ZOrder); +} + +typedef void(__fastcall *tRegisterFunction)(UClass *pClass, const char *funcName, void *pFunc); +tRegisterFunction oRegisterFunction = NULL; + +// +// Blueprint VM intrinsic return value declaration. +// +#define RESULT_PARAM Z_Param__Result +#define RESULT_DECL void *const RESULT_PARAM + +/** The type of a native function callable by script */ +typedef void (*FNativeFuncPtr)(UObject *Context, FFrame &TheStack, RESULT_DECL); +// This class is deliberately simple (i.e. POD) to keep generated code size down. +struct FNameNativePtrPair +{ + const char *NameUTF8; + FNativeFuncPtr Pointer; +}; + +typedef void(__fastcall *tRegisterFunctionOther)(UClass *pClass, const FNameNativePtrPair *InArray, int32_t NumFunctions); +tRegisterFunctionOther oRegisterFunctionOther = NULL; + +void hkRegisterFunction(UClass *pClass, const char *funcName, void *pFunc) +{ + char buffer[1024]; + sprintf_s(buffer, sizeof(buffer), "Function \"%s\" is at %X", funcName, (uintptr_t)pFunc); + std::cout << buffer << std::endl; + return oRegisterFunction(pClass, funcName, pFunc); +} + +void hkRegisterFunctionOther(UClass *pClass, const FNameNativePtrPair *InArray, int32_t NumFunctions) +{ + //char buffer[1024]; + uintptr_t startArray = (uintptr_t)InArray; + int32_t oldNumFunctions = NumFunctions; + /*for (; NumFunctions; ++InArray, --NumFunctions) + { + uintptr_t address = (uintptr_t)InArray->Pointer; + sprintf_s(buffer, sizeof(buffer), "Function %s is at 0x%" PRIx64"", InArray->NameUTF8, address); + std::cout << buffer << std::endl; + }*/ + + return oRegisterFunctionOther(pClass, reinterpret_cast(startArray), oldNumFunctions); +} +/*const FLinkerInstancingContext**/ +typedef UObject *(__fastcall *tStaticLoadObject)(UClass *pClass, UObject *pObj, const wchar_t *pName, const wchar_t *pFileName, uint32_t LoadFlags, UPackageMap *Sandbox, bool bAllowObjectReconciliation/*, void *InstancingContext*/); +tStaticLoadObject oStaticLoadObject = NULL; + +UObject *hkStaticLoadObject(UClass *pClass, UObject *pOuter, const wchar_t *pName, const wchar_t *pFileName, uint32_t LoadFlags, UPackageMap *Sandbox, bool bAllowObjectReconciliation/*, void *InstancingContext*/) +{ + //std::wcout << L"******* StaticLoadObject *******" << std::endl; + //std::wcout << L"Class: " << pClass->GetName().c_str() << std::endl; + //if (pOuter) + // std::wcout << L"Outer: " << pOuter->GetName().c_str() << std::endl; + //std::wcout << L"Name: " << pName << std::endl; + //if (pFileName) + // std::wcout << L"FileName: " << pFileName << std::endl; + //std::wcout << std::endl; + + //if (wcsstr(pName, L"/Game/UI/MainMenu/MainMenu.MainMenu") != NULL) + //{ + // USwfMovie *result = static_cast(oStaticLoadObject(pClass, pOuter, pName, pFileName, LoadFlags, Sandbox, bAllowObjectReconciliation)); + // if (result && result->RawData.Count > 0) + // { + // std::ofstream fout; + // fout.open("mainmenu.swf", std::ios::binary | std::ios::out); + // fout.write((char *)result->RawData.Data, result->RawData.Max); + // fout.close(); + // } + // return result; + //} + //else if (wcsstr(pName, L"/Game/UI/FrontEndContainers/FrontEndContainer.FrontEndContainer") != NULL) + //{ + // USwfMovie *result = static_cast(oStaticLoadObject(pClass, pOuter, pName, pFileName, LoadFlags, Sandbox, bAllowObjectReconciliation)); + // if (result && result->RawData.Count > 0) + // { + // std::ofstream fout; + // fout.open("frontendContainer.swf", std::ios::binary | std::ios::out); + // fout.write((char *)result->RawData.Data, result->RawData.Max); + // fout.close(); + // } + // return result; + //} + //else if (wcsstr(pName, L"/Game/UI/_Shared/Fonts/fonts_en.fonts_en") != NULL) + //{ + // USwfMovie *result = static_cast(oStaticLoadObject(pClass, pOuter, pName, pFileName, LoadFlags, Sandbox, bAllowObjectReconciliation)); + // if (result && result->RawData.Count > 0) + // { + // std::ofstream fout; + // fout.open("../_Shared/Fonts/fonts_en.swf", std::ios::binary | std::ios::out); + // fout.write((char *)result->RawData.Data, result->RawData.Max); + // fout.close(); + // } + // return result; + //} + //else if (wcsstr(pName, L"/Game/UI/HintBar/HintBar.HintBar") != NULL) + //{ + // USwfMovie *result = static_cast(oStaticLoadObject(pClass, pOuter, pName, pFileName, LoadFlags, Sandbox, bAllowObjectReconciliation)); + // if (result && result->RawData.Count > 0) + // { + // std::ofstream fout; + // fout.open("../HintBar/HintBar.swf", std::ios::binary | std::ios::out); + // fout.write((char *)result->RawData.Data, result->RawData.Max); + // fout.close(); + // } + // return result; + //} + //else if (wcsstr(pName, L"/Game/UI/DialogWindow/DialogWindow.DialogWindow") != NULL) + //{ + // USwfMovie *result = static_cast(oStaticLoadObject(pClass, pOuter, pName, pFileName, LoadFlags, Sandbox, bAllowObjectReconciliation)); + // if (result && result->RawData.Count > 0) + // { + // std::ofstream fout; + // fout.open("DialogWindow.swf", std::ios::binary | std::ios::out); + // fout.write((char *)result->RawData.Data, result->RawData.Max); + // fout.close(); + // } + // return result; + //} + //else if (wcsstr(pName, L"/Game/UI/MainMenu/BPMenu_GFxMainMenu.BPMenu_GFxMainMenu_C") != NULL) + //{ + // UGFxOakMainMenu *result = static_cast(oStaticLoadObject(pClass, pOuter, pName, pFileName, LoadFlags, Sandbox, bAllowObjectReconciliation)); + // return result; + //} + //else if (wcsstr(pName, L"/Game/UI/Options/OptionsMenu.OptionsMenu") != NULL) + //{ + // UObject *result = static_cast(oStaticLoadObject(pClass, pOuter, pName, pFileName, LoadFlags, Sandbox, bAllowObjectReconciliation)); + // std::cout << pClass->GetFullName() << std::endl; + // return result; + //} + //else if (wcsstr(pName, L"/Game/UI/Options/BPMenu_OptionsMenu.BPMenu_OptionsMenu_C") != NULL) + //{ + // UObject* result = static_cast(oStaticLoadObject(pClass, pOuter, pName, pFileName, LoadFlags, Sandbox, bAllowObjectReconciliation)); + // /*std::cout << pClass->GetFullName() << std::endl; + // for (auto super = result->Class; super; super = static_cast(super->SuperField)) + // { + // std::cout << super->GetFullName() << std::endl; + // }*/ + // return result; + //} + return oStaticLoadObject(pClass, pOuter, pName, pFileName, LoadFlags, Sandbox, bAllowObjectReconciliation); +} + +void CreateMenuItems(sol::this_state s, const std::string& btnText, sol::function func) { + EnterCriticalSection(&luamgr->luaCritSec); + UGFxMainAndPauseBaseMenu* menuToUse = NULL; + if (oGetMapName && oGetMapName().ToString().find("MenuMap_P") != std::string::npos) { + menuToUse = mainMenu; + if (menuToUse->MenuItems.Count == 5) { + createdMenuItems.clear(); + /*createdButtonItems.clear(); + createdDropdownListItems.clear(); + createdSliderItems.clear(); + createdSpinnerItems.clear();*/ + } + } + else { + menuToUse = pauseMenu; + if (menuToUse->MenuItems.Count == 6) { + createdMenuItems.clear(); + /*createdButtonItems.clear(); + createdDropdownListItems.clear(); + createdSliderItems.clear(); + createdSpinnerItems.clear();*/ + } + } + if (menuToUse && menuToUse->IsValidLowLevel()) + { + int newIndex = menuToUse->AddMenuItem(btnText, "OnGraphicsClicked", false, -1); + if (menuToUse->MenuItems.Data && menuToUse->MenuItems[newIndex].MenuItem) + { + createdMenuItems[menuToUse->MenuItems[newIndex].MenuItem] = std::make_tuple(func, sol::lua_nil, s.L); + } + } + LeaveCriticalSection(&luamgr->luaCritSec); +} + +void CreateMenuItemUI(sol::this_state s, const std::string& btnText, sol::function clickFunc, sol::function buildUIFunc, sol::function conditionToShow) { + EnterCriticalSection(&luamgr->luaCritSec); + + MenuToCreate menuToCreate; + menuToCreate.s = s.L; + menuToCreate.ButtonText = btnText; + if(clickFunc.valid()) + menuToCreate.OnClick = clickFunc; + if(buildUIFunc.valid()) + menuToCreate.BuildUI = buildUIFunc; + if(conditionToShow.valid()) + menuToCreate.ConditionToShow = conditionToShow; + if (!buildUIFunc.valid()) + menuToCreate.BuildUI = sol::lua_nil; + + menusToCreate.push_back(menuToCreate); + LeaveCriticalSection(&luamgr->luaCritSec); + return; +} + +typedef void* (__fastcall* tFuncBind)(UObject* thisZ, const FFrame& frame, void* something, void* something2); +tFuncBind oFuncBind = NULL; + +void* hkUFuncBind(UObject* thisZ, FFrame& frame, UFunction* something, void* something2) { + //std::cout << thisZ->GetFullName() << " wants to bind: " << something->GetFullName() << std::endl; + void* result = oFuncBind(thisZ, frame, something, something2); + return result; +} + +typedef void *(__fastcall *tBuildMainMenu)(UGFxOakMainMenu *); +tBuildMainMenu oBuildMainMenu = NULL; + +void MyTest(class UGbxGFxButton* PressedButton, const struct FGbxMenuInputEvent& InputInfo) { + std::cout << "Hello" << std::endl; +} + +void* hkBuildMainMenu(UGFxOakMainMenu* menuToUse) +{ + inCustomOptionsMenu = false; + static UFunction* onOptionsClickedFunc = UObject::FindObject("Function OakGame.GFxOakMainMenu.OnOptionsClicked"); + + void* result = oBuildMainMenu(menuToUse); + if (menuToUse && menuToUse->MenuList) + menuToUse->MenuList->PositionOfFirstItem.Y = 0.0f; + int newIndex = menuToUse->AddMenuItem("BLua", "", false, -1); + if (newIndex > 0) { + bLuaMenuBtn = menuToUse->MenuItems.Data[newIndex].MenuItem; + bLuaMenuBtn->OnClicked.FunctionName = onOptionsClickedFunc->Name; + } + while (!onGraphicsClickedFunc) + onGraphicsClickedFunc = UObject::FindObject("Function OakGame.GFxMainAndPauseBaseMenu.OnGraphicsClicked"); + return result; +} + +typedef void* (__fastcall* tBuildPauseMenu)(UGFxPauseMenu*); +tBuildPauseMenu oBuildPauseMenu = NULL; +void* hkBuildPauseMenu(UGFxPauseMenu* menuToUse) +{ + inCustomOptionsMenu = false; + static UFunction* onOptionsClickedFunc = UObject::FindObject("Function OakGame.GFxPauseMenu.OnOptionsClicked"); + + void* result = oBuildPauseMenu(menuToUse); + if(menuToUse && menuToUse->MenuList) + menuToUse->MenuList->PositionOfFirstItem.Y = 0.0f; + int newIndex = menuToUse->AddMenuItem("BLua", "", false, -1); + if (newIndex > 0) { + bLuaMenuBtn = menuToUse->MenuItems.Data[newIndex].MenuItem; + bLuaMenuBtn->OnClicked.FunctionName = onOptionsClickedFunc->Name; + } + return result; +} + +void* hkOptionBaseCreatePanel(UGFxOptionBase* thisZ, UGFxOptionsMenu* pMenu, TArray& items, UGbxGFxGridScrollingList* scrollingList, AOakPlayerController* pc, UOakProfile* profile, UOakGameInstance* instance) { + if (!thisZ->IsA(UGFxGraphicsOptions::StaticClass())) { + void* result = oOptionBaseCreateContentPanel(thisZ, pMenu, items, scrollingList, pc, profile, instance); + return result; + } + EnterCriticalSection(&luamgr->luaCritSec); + + for (auto& cell : createdMenuItems) { + UGbxGFxListCell* target = reinterpret_cast(cell.first); + if (target) { + if (target == lastPressedButton && inCustomOptionsMenu) { + lastPressedButton = NULL; + lua_State* lua = std::get<2>(cell.second); + sol::protected_function current = std::get<1>(cell.second); + if (current.valid()) { + sol::protected_function_result result = current(); + if (!result.valid()) + { + sol::error err = result; + std::cout << "Error creating UI: " << err.what() << std::endl; + PrintGameConsole(err.what()); + Logger->error(err.what()); + } + else + { + if (result.return_count() == 1) + { + sol::table elements = result; + std::vector returnValue = elements.as>(); + if (returnValue.size() > 0) + { + UOptionDescriptionItem** tempPrevious = items.Data; + int32_t prevCount = items.Count; + int32_t prevMax = items.Max; + // Save previous items, otherwise they'll be gone "forever" + items.Data = NULL; + items.Count = 0; + items.Max = 0; + + ZeroMemory(&pMenu->MainPanel->SubtitleBarItems, sizeof(FSubtitleBarItems)); + void* result = oOptionBaseCreateContentPanel(thisZ, pMenu, items, scrollingList, pc, profile, instance); + items.Data = tempPrevious; + items.Count = prevCount; + items.Max = prevMax; + for (auto& i : returnValue) + { + switch (i->UIElementType) + { + case EOptionItemType::Title:{ + thisZ->SetupTitleItem(i->DisplayName); + break; + } + case EOptionItemType::Spinner:{ + UISpinner* current = static_cast(i); + UOptionDescriptionItem* item = static_cast(StaticConstructObject(UOptionDescriptionItem::StaticClass(), GetTransientPackage(), FName(""))); + if(item){ + item->OptionItemType = i->UIElementType; + item->OptionDescriptionText = current->DescriptionText; + item->OptionDescriptionTitle = current->DescriptionTitle; + item->OptionItemName = current->DisplayName; + std::vector options = current->Options.as>(); + for (auto& o : options) + item->SpinnerOptions.Add(FText(o)); + + UGbxGFxListItemSpinner* spinner = thisZ->SetupSpinnerItem(item, current->Index, "OnAnisotropicFilteringChanged"); + if (spinner) { + if (current->Index < 0 || current->Index > options.size() - 1) + spinner->SetDefaultValue(0); + else + spinner->SetDefaultValue(current->Index); + uiData[spinner] = i; + } + } + break; + } + case EOptionItemType::BooleanSpinner: { + UIBoolSpinner* current = static_cast(i); + UOptionDescriptionItem* item = static_cast(StaticConstructObject(UOptionDescriptionItem::StaticClass(), GetTransientPackage(), FName(""))); + if (item) { + item->OptionItemType = i->UIElementType; + item->OptionDescriptionText = current->DescriptionText; + item->OptionDescriptionTitle = current->DescriptionTitle; + item->OptionItemName = current->DisplayName; + item->BooleanOnText = current->OnText; + item->BooleanOffText = current->OffText; + + UGbxGFxListItemSpinner* spinner = thisZ->SetupSpinnerItemAsBoolean(item, current->Value, "OnAnisotropicFilteringChanged"); + if (spinner) { + spinner->SetDefaultValue((int)current->Value); + //spinner->SetDefaultValue(current->Index); + //createdSpinnerItems.push_back(spinner); + uiData[spinner] = i; + } + } + break; + } + case EOptionItemType::Button:{ + UIButton* current = static_cast(i); + UOptionDescriptionItem* item = static_cast(StaticConstructObject(UOptionDescriptionItem::StaticClass(), GetTransientPackage(), FName(""))); + if (item) { + item->OptionItemType = i->UIElementType; + item->OptionDescriptionText = current->DescriptionText; + item->OptionDescriptionTitle = current->DescriptionTitle; + item->OptionItemName = current->DisplayName; + + UGbxGFxListCellWithData* btn = thisZ->SetupButtonItem(item, "OnBenchmarkRunClicked"); + //createdButtonItems.push_back(btn); + uiData[btn] = i; + } + break; + } + case EOptionItemType::Slider: { + UISlider* current = static_cast(i); + UOptionDescriptionItem* item = static_cast(StaticConstructObject(UOptionDescriptionItem::StaticClass(), GetTransientPackage(), FName(""))); + if (item) { + item->OptionItemType = i->UIElementType; + item->OptionDescriptionText = current->DescriptionText; + item->OptionDescriptionTitle = current->DescriptionTitle; + item->OptionItemName = current->DisplayName; + item->SliderMax = current->Max; + item->SliderMin = current->Min; + item->SliderStep = current->Step; + item->SliderIsInteger = current->bIsInt; + UGbxGFxListItemNumber* number = thisZ->SetupSliderItem(item, current->Value, ""); + + if (current->Value < current->Min || current->Value > current->Max) + number->SetDefaultValue(current->Min); + else + number->SetDefaultValue(current->Value); + // TODO: Add function to set default value + //createdSliderItems.push_back(number); + uiData[number] = i; + } + break; + } + case EOptionItemType::DropDownList: { + UIDropdown* current = static_cast(i); + UOptionDescriptionItem* item = static_cast(StaticConstructObject(UOptionDescriptionItem::StaticClass(), GetTransientPackage(), FName(""))); + if (item) { + item->OptionItemType = i->UIElementType; + item->OptionDescriptionText = current->DescriptionText; + item->OptionDescriptionTitle = current->DescriptionTitle; + item->OptionItemName = current->DisplayName; + + std::vector options = current->Options.as>(); + for (auto& o : options) + item->DropDownOptions.Add(FText(o)); + + UGbxGFxListItemComboBox* obj = thisZ->SetupDropDownlistItem(item, item->DropDownOptions, current->Index, "OnResolutionDropDownSelectionChanged"); + if(current->Index < 0 || current->Index > options.size() - 1) + obj->SetDefaultValue(0); + else + obj->SetDefaultValue(current->Index); + //createdDropdownListItems.push_back(obj); + uiData[obj] = i; + } + break; + } + // TODO: Figure out how to get this to work instead of crashing + case EOptionItemType::Keybinding_Button: { + UIControl* current = static_cast(i); + UOptionDescriptionItem* item = static_cast(StaticConstructObject(UOptionDescriptionItem::StaticClass(), GetTransientPackage(), FName(""))); + if (item) { + item->OptionItemType = i->UIElementType; + item->OptionDescriptionText = current->DescriptionText; + item->OptionDescriptionTitle = current->DescriptionTitle; + item->OptionItemName = current->DisplayName; + + UGFxObject* obj = thisZ->SetupControlsItem( + item, + FText(R"()"), + FText(R"()"), + EBindingType::Common, + NULL); + //createdDropdownListItems.push_back(obj); + uiData[obj] = i; + } + break; + } + } + } + LeaveCriticalSection(&luamgr->luaCritSec); + return result; + } + } + } + } + } + } + } + LeaveCriticalSection(&luamgr->luaCritSec); + void* result = oOptionBaseCreateContentPanel(thisZ, pMenu, items, scrollingList, pc, profile, instance); + return result; +} + +void hkOptionBaseCreatePanelItem(UGFxOptionBase* thisZ, UOptionDescriptionItem* item) { + return oOptionBaseCreateContentPanelItem(thisZ, item); +} + +typedef void* (__fastcall* tBuildOptionsMenu)(UGFxOptionsMenu*); +tBuildOptionsMenu oBuildOptionsMenu = NULL; + +void* hkBuildOptionsMenu(UGFxOptionsMenu* menuToUse) { + void* result = oBuildOptionsMenu(menuToUse); + return result; +} + +UGFxObject* hkSetupControlsItem(UGFxOptionBase* thisZ, UOptionDescriptionItem* item, const FText& unknownText, const FText& unknownText2, EBindingType type, void* someThing) { + void* mysomething = oRealloc(0, 144, 0); + + UGFxObject* result = oOptionBaseSetupControlsItem(thisZ, item, unknownText, unknownText2, type, someThing); + return result; +} + +void ApplyBaseHooks() +{ + uintptr_t staticExec = NULL; + while (!staticExec) + { + uintptr_t patternAddress = PatternScan::FindSignature(NULL, staticExecPattern.crypt_get()); + if (patternAddress) + { + patternAddress += 110; + int32_t offset = *(int32_t *)(patternAddress + 1); + if (offset) + { + staticExec = (patternAddress + offset + 5); + } + } + } + + /*uintptr_t addToViewport = PatternScan::FindSignature(NULL, addToViewportPattern.crypt_get()); + while (addToViewport == NULL) { + addToViewport = PatternScan::FindSignature(NULL, addToViewportPattern.crypt_get()); + Sleep(20); + }*/ + + /*uintptr_t registerFunc = PatternScan::FindSignature(NULL, registerFunctionAPattern.crypt_get()); + while (registerFunc == NULL) { + registerFunc = PatternScan::FindSignature(NULL, registerFunctionAPattern.crypt_get()); + Sleep(20); + } + oRegisterFunction = (tRegisterFunction)registerFunc;*/ + /* + uintptr_t registerFuncOther = PatternScan::FindSignature(NULL, registerFunctionOtherPattern.crypt_get()); + while (registerFuncOther == NULL) { + registerFuncOther = PatternScan::FindSignature(NULL, registerFunctionOtherPattern.crypt_get()); + Sleep(20); + }*/ + /*uintptr_t callFunc = NULL; + while (!callFunc) + { + uintptr_t patternAddress = PatternScan::FindSignature(NULL, callFuncPattern.crypt_get()); + if (patternAddress) + { + patternAddress += 100; + int32_t offset = *(int32_t*)(patternAddress + 1); + if (offset) + { + oCallFunc = (tCallFunc)(patternAddress + offset + 5); + callFunc = (patternAddress + offset + 5); + } + } + }*/ + + uintptr_t staticLoadObject = PatternScan::FindSignature(NULL, staticLoadObjectPattern.crypt_get()); + while(!staticLoadObject){ + staticLoadObject = PatternScan::FindSignature(NULL, staticLoadObjectPattern.crypt_get()); + Sleep(20); + } + + /*uintptr_t funcBind = PatternScan::FindSignature(NULL, ufunctionBindPattern.crypt_get()); + while (!funcBind) { + funcBind = PatternScan::FindSignature(NULL, ufunctionBindPattern.crypt_get()); + Sleep(20); + }*/ + + /*uintptr_t staticFindObject = PatternScan::FindSignature(NULL, staticFindObjectPattern.crypt_get()); + while (!staticFindObject) { + staticFindObject = PatternScan::FindSignature(NULL, staticFindObjectPattern.crypt_get()); + Sleep(20); + }*/ + + uintptr_t staticConstructObject = PatternScan::FindSignature(NULL, staticConstructObjectPattern.crypt_get()); + while (!staticConstructObject) + { + staticConstructObject = PatternScan::FindSignature(NULL, staticConstructObjectPattern.crypt_get()); + Sleep(20); + } + + uintptr_t optionBaseCreateContentPanel = PatternScan::FindSignature(NULL, optionBaseCreateContentPanelPattern.crypt_get()); + while (!optionBaseCreateContentPanel) + { + optionBaseCreateContentPanel = PatternScan::FindSignature(NULL, optionBaseCreateContentPanelPattern.crypt_get()); + Sleep(20); + } + + uintptr_t optionBaseCreateContentPanelItem = PatternScan::FindSignature(NULL, optionBaseCreateContentPanelItemPattern.crypt_get()); + while (!optionBaseCreateContentPanelItem) + { + optionBaseCreateContentPanelItem = PatternScan::FindSignature(NULL, optionBaseCreateContentPanelItemPattern.crypt_get()); + Sleep(20); + } + + uintptr_t buildMainMenu = PatternScan::FindSignature(NULL, buildMainMenuPattern.crypt_get()); + while (!buildMainMenu) { + buildMainMenu = PatternScan::FindSignature(NULL, buildMainMenuPattern.crypt_get()); + Sleep(20); + } + + uintptr_t buildPauseMenu = PatternScan::FindSignature(NULL, buildPauseMenuPattern.crypt_get()); + while (!buildPauseMenu) { + buildPauseMenu = PatternScan::FindSignature(NULL, buildPauseMenuPattern.crypt_get()); + Sleep(20); + } + + uintptr_t buildOptionsMenu = PatternScan::FindSignature(NULL, buildOptionsMenuPattern.crypt_get()); + while (!buildOptionsMenu) { + buildOptionsMenu = PatternScan::FindSignature(NULL, buildOptionsMenuPattern.crypt_get()); + Sleep(20); + } + + uintptr_t doSparkAuth = PatternScan::FindSignature(NULL, FOakPlayerManager__DoSparkAuthenticationPattern.crypt_get()); + while (!doSparkAuth) { + doSparkAuth = PatternScan::FindSignature(NULL, FOakPlayerManager__DoSparkAuthenticationPattern.crypt_get()); + Sleep(20); + } + uintptr_t startupProcessPerformstep = PatternScan::FindSignature(NULL, FOakStartupProcess__PerformStepPattern.crypt_get()); + while (!startupProcessPerformstep) { + startupProcessPerformstep = PatternScan::FindSignature(NULL, FOakStartupProcess__PerformStepPattern.crypt_get()); + Sleep(20); + } + + uintptr_t gbxSparkModuleGet = PatternScan::FindSignature(NULL, FGbxSparkModuleGetPattern.crypt_get()); + while (!gbxSparkModuleGet) { + gbxSparkModuleGet = PatternScan::FindSignature(NULL, FGbxSparkModuleGetPattern.crypt_get()); + Sleep(20); + } + + uintptr_t sparkInitProcessStartProcess = PatternScan::FindSignature(NULL, FSparkInitProcessStartProcessPattern.crypt_get()); + while (!sparkInitProcessStartProcess) { + sparkInitProcessStartProcess = PatternScan::FindSignature(NULL, FSparkInitProcessStartProcessPattern.crypt_get()); + Sleep(20); + } + uintptr_t sparkInitProcessReadDiscovery = PatternScan::FindSignature(NULL, FSparkInitProcessReadDiscoveryPattern.crypt_get()); + while (!sparkInitProcessReadDiscovery) { + sparkInitProcessReadDiscovery = PatternScan::FindSignature(NULL, FSparkInitProcessReadDiscoveryPattern.crypt_get()); + Sleep(20); + } + uintptr_t oakGameInstanceProcessPendingMicropatches = PatternScan::FindSignature(NULL, oakGameInstanceProcessPendingMicropatchesPattern.crypt_get()); + while (!oakGameInstanceProcessPendingMicropatches) { + oakGameInstanceProcessPendingMicropatches = PatternScan::FindSignature(NULL, oakGameInstanceProcessPendingMicropatchesPattern.crypt_get()); + Sleep(20); + } + + uintptr_t setupControlsItem = PatternScan::FindSignature(NULL, optionBaseSetupControlsItemPattern.crypt_get()); + while (!setupControlsItem) { + setupControlsItem = PatternScan::FindSignature(NULL, optionBaseSetupControlsItemPattern.crypt_get()); + Sleep(20); + } + + /*std::string test = xorstr("4D 89 ?? ?? 4D 89 ?? ?? 4C 8B ?? 44 89 ?? ?? ?? 4C 39 ?? ?? 0F 84 ?? ?? ?? ?? 48 8B ?? ?? 48 85 ?? 0F 84 ?? ?? ?? ?? 33 C0 F0 44 ?? ?? ?? ?? 85 C0 7E ?? 49 8B ?? ?? 4C 89 ?? ?? ?? 48 89 ?? ?? ?? 48 85 ?? 74 ?? 33 C0 F0 44 ?? ?? ?? ?? 74 ?? 8D 48 ?? F0 0F ?? ?? ?? 74 ?? 33 C0 F0 44 ?? ?? ?? ?? 75 ?? 4C 8B ?? ?? ?? 48 8D ?? ?? ?? 4C 89 ?? ?? ?? 41 BE ?? ?? ?? ?? EB ?? 48 8B ?? ?? ?? 48 85 ?? 74 ?? 49 8B ?? ?? 48 89 ?? ?? ?? 4C 8B ?? ?? ?? 48 8D ?? ?? ?? 41 BE ?? ?? ?? ?? EB ?? 0F 57 ?? 48 8D ?? ?? ?? F3 0F ?? ?? ?? ?? 41 BE ?? ?? ?? ?? 4D 8B ?? 48 8B ?? ?? BF ?? ?? ?? ?? 4C 89 ?? ?? 4C 89 ?? 48 89 ?? ?? ?? ?? ?? ?? 41 F6 C6 ?? 74 ?? 48 8B ?? ?? 41 83 E6 ?? 48 85 ?? 74 ?? 8B C7 F0 0F ?? ?? ?? 83 F8 ?? 75 ?? 48 8B ?? ?? 48 8B ?? 48 8B ?? FF 10 8B C7 F0 0F ?? ?? ?? 83 F8 ?? 75 ?? 48 8B ?? ?? 8D 57 ?? 48 8B ?? FF 50 ?? 41 F6 C6 ?? 4C 8B ?? ?? ?? ?? ?? ?? 74 ?? 48 8B ?? ?? ?? 48 85 ?? 74 ?? 8B C7 F0 0F ?? ?? ?? 83 F8 ?? 75 ?? 48 8B ?? ?? ?? 48 8B ?? 48 8B ?? FF 10 8B C7 F0 0F ?? ?? ?? 83 F8 ?? 75 ?? 48 8B ?? ?? ?? BA ?? ?? ?? ?? 48 8B ?? FF 50 ?? 4D 85 ?? 0F 84 ?? ?? ?? ?? 48 8D ?? ?? 4C 89 ?? ?? B9 ?? ?? ?? ?? 44 89 ?? ?? E8 ?? ?? ?? ?? 48 85 ?? 74 ?? 48 8D ?? ?? ?? ?? ?? 4C 89 ?? ?? 48 89 ?? 48 8D ?? ?? ?? ?? ?? 4C 89 ?? ?? ?? 33 D2 48 89 ?? ?? 48 8D ?? ?? ?? 48 C7 44 24 60 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B ?? ?? ?? 4C 8D ?? ?? ?? ?? ?? 41 B9 ?? ?? ?? ?? B8 ?? ?? ?? ?? 41 8B ?? 66 89 ?? ?? ?? E8 ?? ?? ?? ?? 4C 8D ?? ?? 48 8D ?? ?? ?? 48 8D ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8D ?? ?? FF 15 ?? ?? ?? ?? 48 8B ?? ?? B9 ?? ?? ?? ?? 48 89 ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 ?? 74 ?? 48 8B ?? E8 ?? ?? ?? ?? 48 8B ?? EB ?? 49 8B ?? B9 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 ?? 74 ?? 48 8D ?? ?? ?? ?? ?? C7 40 08 ?? ?? ?? ?? 48 89 ?? C7 40 0C ?? ?? ?? ?? 48 89 ?? ?? EB ?? 49 8B ?? 4C 8B ?? 48 89 ?? ?? ?? 48 8B ?? 48 89 ?? ?? ?? 48 8D ?? ?? ?? E8 ?? ?? ?? ?? 48 8B ?? ?? ?? C6 40 10 ?? 48 8B ?? ?? 48 8B ?? 48 85 ?? 74 ?? F0 FF ?? ?? 48 89 ?? ?? ?? 48 8D ?? ?? ?? ?? ?? 48 89 ?? ?? ?? ?? ?? 48 8D ?? ?? ?? 4C 89 ?? ?? ?? E8 ?? ?? ?? ?? 48 8B ?? ?? ?? 48 85 ?? 74 ?? 8B C7 F0 0F ?? ?? ?? 83 F8 ?? 75 ?? 48 8B ?? ?? ?? 48 8B ?? 48 8B ?? FF 10 8B C7 F0 0F ?? ?? ?? 83 F8 ?? 75 ?? 48 8B ?? ?? ?? BA ?? ?? ?? ?? 48 8B ?? FF 50 ?? 48 8D ?? ?? ?? ?? ?? 4C 89 ?? ?? ?? ?? ?? B9 ?? ?? ?? ?? 44 89 ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 85 ?? 74 ?? 48 8D ?? ?? ?? ?? ?? 48 89 ?? 48 8D ?? ?? ?? ?? ?? 48 89 ?? ?? 48 8D ?? ?? ?? ?? ?? 48 89 ?? ?? ?? ?? ?? 48 8B ?? ?? ?? 48 89 ?? ?? 48 8B ?? ?? ?? 48 89 ?? ?? 48 85 ?? 74 ?? F0 FF ?? ?? 48 8D ?? ?? ?? ?? ?? 48 8D ?? ?? E8 ?? ?? ?? ?? 48 8D ?? ?? 4C 89 ?? ?? B9 ?? ?? ?? ?? 44 89 ?? ?? E8 ?? ?? ?? ?? 48 8B ?? 48 85 ?? 74 ?? 48 8D ?? ?? ?? ?? ?? 48 89 ?? 48 8B ?? ?? 48 89 ?? ?? 48 8B ?? ?? 48 89 ?? ?? 48 85 ?? 74 ?? 48 8B ?? ?? F0 FF ?? ?? 48 8D ?? ?? 48 8D ?? ?? E8 ?? ?? ?? ?? 48 8D ?? ?? ?? ?? ?? 48 8B ?? ?? 48 89 ?? ?? 44 39 ?? ?? ?? ?? ?? 74 ?? 48 85 ?? 4C 8D ?? ?? 4D 0F ?? ?? 4C 0B ?? 74 ?? 49 8B ?? 33 D2 49 8B ?? FF 50 ?? 48 8B ?? ?? 48 85 ?? 74 ?? E8 ?? ?? ?? ?? 48 8B ?? ?? 48 85 ?? 74 ?? 8B C7 F0 0F ?? ?? ?? 83 F8 ?? 75 ?? 48 8B ?? 48 8B ?? FF 10 8B C7 F0 0F ?? ?? ?? 83 F8 ?? 75 ?? 48 8B ?? BA ?? ?? ?? ?? 48 8B ?? FF 50 ?? 48 8B ?? ?? ?? ?? ?? 44 39 ?? ?? ?? ?? ?? 74 ?? 48 85 ?? 4C 8D ?? ?? ?? ?? ?? 4D 0F ?? ?? 4C 0B ?? 74 ?? 49 8B ?? 33 D2 49 8B ?? FF 50 ?? 48 8B ?? ?? ?? ?? ?? 48 85 ?? 74 ?? E8 ?? ?? ?? ?? 48 8B ?? ?? ?? 48 85 ?? 74 ?? 8B C7 F0 0F ?? ?? ?? 83 F8 ?? 75 ?? 48 8B ?? 48 8B ?? FF 10 8B C7 F0 0F ?? ?? ?? 83 F8 ?? 75 ?? 48 8B ?? BA ?? ?? ?? ?? 48 8B ?? FF 50 ?? 48 8D ?? ?? 49 8B ?? E8 ?? ?? ?? ?? 48 8D ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8D ?? ?? ?? ?? ?? E8 ?? ?? ?? ?? 4C 8B ?? ?? ?? ?? ?? ?? 4C 8B ?? ?? ?? ?? ?? ?? 4C 8B ?? ?? ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? ?? 48 85 ?? 74 ?? 8B C7 F0 0F ?? ?? ?? 83 F8 ?? 75 ?? 48 8B ?? 48 8B ?? FF 10 F0 0F ?? ?? ?? 83 FF ?? 75 ?? 48 8B ?? 8B D7 48 8B ?? FF 50 ?? 48 8B ?? ?? ?? ?? ?? 48 33 ?? E8 ?? ?? ?? ?? 48 81 C4 ?? ?? ?? ?? 5F 5E 5D C3 CC CC CC CC CC CC CC CC CC 4C 8B"); + uintptr_t removeAllListItems = PatternScan::FindSignature(NULL, test.crypt_get()); + while (!removeAllListItems) { + removeAllListItems = PatternScan::FindSignature(NULL, test.crypt_get()); + Sleep(20); + }*/ + + SuspendResume::Suspend(); + while (MH_CreateHookEx((LPVOID)staticExec, &hkStaticExec, &oStaticExec) != MH_OK) + Sleep(200); + /*while (MH_CreateHookEx((LPVOID)addToViewport, &hkAddToViewport, &oAddToViewport) != MH_OK) + Sleep(200); + while (MH_CreateHookEx((LPVOID)registerFunc, &hkRegisterFunction, &oRegisterFunction) != MH_OK) + Sleep(200); + while (MH_CreateHookEx((LPVOID)registerFuncOther, &hkRegisterFunctionOther, &oRegisterFunctionOther) != MH_OK) + Sleep(200);*/ + while (MH_CreateHookEx((LPVOID)staticLoadObject, &hkStaticLoadObject, &oStaticLoadObject) != MH_OK) + Sleep(200); + /*while (MH_CreateHookEx((LPVOID)staticFindObject, &StaticFindObject, &oStaticFindObject) != MH_OK) + Sleep(200);*/ + /*while (MH_CreateHookEx((LPVOID)callFunc, &hkCallFunction, &oCallFunc) != MH_OK) + Sleep(200); */ + /*while (MH_CreateHookEx((LPVOID)funcBind, &hkUFuncBind, &oFuncBind) != MH_OK) + Sleep(200);*/ + while (MH_CreateHookEx((LPVOID)staticConstructObject, &StaticConstructObject, &oStaticConstructObject) != MH_OK) + Sleep(200); + while (MH_CreateHookEx((LPVOID)optionBaseCreateContentPanel, &hkOptionBaseCreatePanel, &oOptionBaseCreateContentPanel) != MH_OK) + Sleep(200); + while (MH_CreateHookEx((LPVOID)optionBaseCreateContentPanelItem, &hkOptionBaseCreatePanelItem, &oOptionBaseCreateContentPanelItem) != MH_OK) + Sleep(200); + while (MH_CreateHookEx((LPVOID)buildMainMenu, &hkBuildMainMenu, &oBuildMainMenu) != MH_OK) + Sleep(200); + while (MH_CreateHookEx((LPVOID)buildPauseMenu, &hkBuildPauseMenu, &oBuildPauseMenu) != MH_OK) + Sleep(200); + while (MH_CreateHookEx((LPVOID)buildOptionsMenu, &hkBuildOptionsMenu, &oBuildOptionsMenu) != MH_OK) + Sleep(200); + while (MH_CreateHookEx((LPVOID)doSparkAuth, &hkDoSparkAuth, &oDoSparkAuth) != MH_OK) + Sleep(200); + while (MH_CreateHookEx((LPVOID)startupProcessPerformstep, &hkStartupProcessPerformStep, &oStartupProcessPerformStep) != MH_OK) + Sleep(200); + while (MH_CreateHookEx((LPVOID)sparkInitProcessStartProcess, &hkSparkInitProcessStartProcess, &oFSparkInitProcessStartProcess) != MH_OK) + Sleep(200); + while (MH_CreateHookEx((LPVOID)sparkInitProcessReadDiscovery, &hkSparkInitProcessReadDiscovery, &oFSparkInitProcessReadDiscovery) != MH_OK) + Sleep(200); + while (MH_CreateHookEx((LPVOID)oakGameInstanceProcessPendingMicropatches, &hkOakGameInstanceProcessPendingMicropatches, &oOakGameInstanceProcessPendingMicropatches) != MH_OK) + Sleep(200); + while (MH_CreateHookEx((LPVOID)setupControlsItem, &hkSetupControlsItem, &oOptionBaseSetupControlsItem) != MH_OK) + Sleep(200); + /*while (MH_CreateHookEx((LPVOID)removeAllListItems, &hkRemoveAllListItems, &oRemoveAllListItems) != MH_OK) + Sleep(200);*/ + MH_EnableHook(MH_ALL_HOOKS); + SuspendResume::Resume(); +} + +void HotkeyThread() +{ + std::string hk1; + std::string hk2; + std::string hk3; + while (true) + { + if (wantsExit) + ExitThread(0); + //SetConsoleKey("F6"); + if (!luamgr) + { + Sleep(50); + continue; + } + luamgr->ValidateHotkeys(); + EnterCriticalSection(&luamgr->luaCritSec); + std::unordered_map funcToCall; + for (auto &hk : luamgr->registeredHotkeys) + { + hk1 = std::get<2>(hk); + hk2 = std::get<3>(hk); + hk3 = std::get<4>(hk); + + if (hk1.size() > 0) + { + if (hk2.size() > 0) + { + if (hk3.size() > 0) + { + if (LuaUtility::IsKeyPressed(hk1, hk2, hk3)) + { + lua_State *lua = std::get<1>(hk); + //sol::protected_function func(lua, sol::ref_index(std::get<0>(hk).registry_index())); + sol::protected_function func = std::get<0>(hk); + if (func.valid()) + { + funcToCall.insert_or_assign(std::get<0>(hk).registry_index(), func); + } + } + } + else + { + if (LuaUtility::IsKeyPressed(hk1, hk2)) + { + lua_State *lua = std::get<1>(hk); + //sol::protected_function func(lua, sol::ref_index(std::get<0>(hk).registry_index())); + sol::protected_function func = std::get<0>(hk); + if (func.valid()) + { + funcToCall.insert_or_assign(std::get<0>(hk).registry_index(), func); + } + } + } + } + else + { + if (LuaUtility::IsKeyPressed(hk1)) + { + lua_State *lua = std::get<1>(hk); + //sol::protected_function func(lua, sol::ref_index(std::get<0>(hk).registry_index())); + sol::protected_function func = std::get<0>(hk); + if (func.valid()) + { + funcToCall.insert_or_assign(std::get<0>(hk).registry_index(), func); + } + } + } + } + } + if (hk1.size() > 0) + { + if (hk2.size() > 0) + { + if (hk3.size() > 0) + { + while (LuaUtility::IsKeyPressed(hk1, hk2, hk3)) + Sleep(5); + } + else + { + while (LuaUtility::IsKeyPressed(hk1, hk2)) + Sleep(5); + } + } + else + { + while (LuaUtility::IsKeyPressed(hk1)) + Sleep(5); + } + } + for (auto &f : funcToCall) + { + f.second(); + } + LeaveCriticalSection(&luamgr->luaCritSec); + Sleep(20); + } +} + +void CreateGUIBindings() { + // UIData + sol::usertype uidatatype = luamgr->lua->new_usertype("UIData", sol::no_constructor); + + // UIButton + sol::usertype uibuttontype = luamgr->lua->new_usertype("UIButton", sol::no_constructor, + sol::base_classes, sol::bases()); + uibuttontype.set_function("New", [](sol::this_state s) {UIButton* data = new UIButton; data->UIElementType = EOptionItemType::Button; data->state = s.L; return data; }); + uibuttontype.set("DisplayName", &UIButton::DisplayName); + uibuttontype.set("DescriptionText", &UIButton::DescriptionText); + uibuttontype.set("DescriptionTitle", &UIButton::DescriptionTitle); + uibuttontype.set("OnClicked", &UIButton::OnChangedOrClicked); + + // UISlider + sol::usertype uislidertype = luamgr->lua->new_usertype("UISlider", sol::no_constructor, + sol::base_classes, sol::bases()); + uislidertype.set_function("New", [](sol::this_state s) {UISlider* data = new UISlider; data->UIElementType = EOptionItemType::Slider; data->state = s.L; return data; }); + uislidertype.set("DisplayName", &UISlider::DisplayName); + uislidertype.set("DescriptionText", &UISlider::DescriptionText); + uislidertype.set("DescriptionTitle", &UISlider::DescriptionTitle); + uislidertype.set("OnValueChanged", &UISlider::OnChangedOrClicked); + uislidertype.set("Value", &UISlider::Value); + uislidertype.set("Min", &UISlider::Min); + uislidertype.set("Max", &UISlider::Max); + uislidertype.set("Step", &UISlider::Step); + uislidertype.set("bIsInt", &UISlider::bIsInt); + + // UISpinner + sol::usertype uispinnertype = luamgr->lua->new_usertype("UISpinner", sol::no_constructor, + sol::base_classes, sol::bases()); + uispinnertype.set_function("New", [](sol::this_state s) {UISpinner* data = new UISpinner; data->UIElementType = EOptionItemType::Spinner; data->state = s.L; return data; }); + uispinnertype.set("DisplayName", &UISpinner::DisplayName); + uispinnertype.set("DescriptionText", &UISpinner::DescriptionText); + uispinnertype.set("DescriptionTitle", &UISpinner::DescriptionTitle); + uispinnertype.set("OnSelectionChanged", &UISpinner::OnChangedOrClicked); + uispinnertype.set("Index", &UISpinner::Index); + uispinnertype.set("Options", &UISpinner::Options); + + // UIBoolSpinner + sol::usertype uiboolspinnertype = luamgr->lua->new_usertype("UIBoolSpinner", sol::no_constructor, + sol::base_classes, sol::bases()); + uiboolspinnertype.set_function("New", [](sol::this_state s) {UIBoolSpinner* data = new UIBoolSpinner; data->UIElementType = EOptionItemType::BooleanSpinner; data->state = s.L; return data; }); + uiboolspinnertype.set("DisplayName", &UIBoolSpinner::DisplayName); + uiboolspinnertype.set("DescriptionText", &UIBoolSpinner::DescriptionText); + uiboolspinnertype.set("DescriptionTitle", &UIBoolSpinner::DescriptionTitle); + uiboolspinnertype.set("OnSelectionChanged", &UIBoolSpinner::OnChangedOrClicked); + uiboolspinnertype.set("Value", &UIBoolSpinner::Value); + uiboolspinnertype.set("OnText", &UIBoolSpinner::OnText); + uiboolspinnertype.set("OffText", &UIBoolSpinner::OffText); + + // UIDropdown + sol::usertype uidropdowntype = luamgr->lua->new_usertype("UIDropdown", sol::no_constructor, + sol::base_classes, sol::bases()); + uidropdowntype.set_function("New", [](sol::this_state s) {UIDropdown* data = new UIDropdown; data->UIElementType = EOptionItemType::DropDownList; data->state = s.L; return data; }); + uidropdowntype.set("DisplayName", &UIDropdown::DisplayName); + uidropdowntype.set("DescriptionText", &UIDropdown::DescriptionText); + uidropdowntype.set("DescriptionTitle", &UIDropdown::DescriptionTitle); + uidropdowntype.set("OnSelectionChanged", &UIDropdown::OnChangedOrClicked); + uidropdowntype.set("Index", &UIDropdown::Index); + uidropdowntype.set("Options", &UIDropdown::Options); + + // UIControl + sol::usertype uicontrolstype = luamgr->lua->new_usertype("UIControl", sol::no_constructor, + sol::base_classes, sol::bases()); + uicontrolstype.set_function("New", [](sol::this_state s) {UIControl* data = new UIControl; data->UIElementType = EOptionItemType::Keybinding_Button; data->state = s.L; return data; }); + uicontrolstype.set("DisplayName", &UIControl::DisplayName); + uicontrolstype.set("DescriptionText", &UIControl::DescriptionText); + uicontrolstype.set("DescriptionTitle", &UIControl::DescriptionTitle); + uicontrolstype.set("OnSelectionChanged", &UIControl::OnChangedOrClicked); + + // UITitle + sol::usertype uititletype = luamgr->lua->new_usertype("UITitle", sol::no_constructor, + sol::base_classes, sol::bases()); + uititletype.set_function("New", [](sol::this_state s) {UITitle* data = new UITitle; data->UIElementType = EOptionItemType::Title; return data; }); + uititletype.set("DisplayName", &UITitle::DisplayName); + + luamgr->lua->set_function("AddMenuButton", sol::overload( + [](sol::this_state s, const std::string& btnText, sol::object clickFunc) { + CreateMenuItemUI(s, btnText, clickFunc, sol::lua_nil, sol::lua_nil); + }, + [](sol::this_state s, const std::string& btnText, sol::lua_nil_t clickFunc, sol::object buildGUIFunc) { + CreateMenuItemUI(s, btnText, sol::lua_nil, buildGUIFunc, sol::lua_nil); + }, + [](sol::this_state s, const std::string& btnText, sol::lua_nil_t clickFunc, sol::object buildGUIFunc, sol::object conditionToShow) { + CreateMenuItemUI(s, btnText, sol::lua_nil, buildGUIFunc, conditionToShow); + }, + [](sol::this_state s, const std::string& btnText, sol::lua_nil_t clickFunc, sol::object buildGUIFunc, sol::lua_nil_t conditionToShow) { + CreateMenuItemUI(s, btnText, sol::lua_nil, buildGUIFunc, sol::lua_nil); + }, + [](sol::this_state s, const std::string& btnText, sol::object clickFunc, sol::object buildGUIFunc) { + CreateMenuItemUI(s, btnText, clickFunc, buildGUIFunc, sol::lua_nil); + }, + [](sol::this_state s, const std::string& btnText, sol::object clickFunc, sol::object buildGUIFunc, sol::object conditionToShow) { + CreateMenuItemUI(s, btnText, clickFunc, buildGUIFunc, conditionToShow); + } + )); + + // UEditableText + /*sol::usertype editabletexttype = luamgr->lua->new_usertype("UEditableText", sol::no_constructor, + sol::meta_function::index, &UObject::GetPropertyByName, + sol::meta_function::new_index, &UObject::SetPropertyByNameLua, + sol::meta_function::to_string, &UObject::to_string, + sol::base_classes, sol::bases()); + editabletexttype.set_function("New", []() + { + UEditableText* rv = static_cast(StaticConstructObject(UEditableText::StaticClass(), GetTransientPackage(), FName("EditableText"))); + if (rv) + rv->SetText(FText("Testtext")); + rv->SetIsEnabled(true); + rv->SetVisibility(ESlateVisibility::Visible); + return rv; + }); + editabletexttype.set("HintText", &UEditableText::HintText); + editabletexttype.set("MinWidth", &UEditableText::MinimumDesiredWidth); + editabletexttype.set("Text", &UEditableText::Text); + editabletexttype.set("TooltipText", &UEditableText::ToolTipText);*/ +} + +void CreateBLuaMenu() { + bluaMenu->ButtonText = "BLua"; +} + +void LoadAllCustomUIScripts() { + EnterCriticalSection(&luamgr->luaCritSec); + if (!std::filesystem::exists("./lua")) + std::filesystem::create_directory("./lua"); + if(!std::filesystem::exists("./lua/UI")) + std::filesystem::create_directory("./lua/UI"); + for (auto& p : std::filesystem::directory_iterator("./lua/UI")) { + try{ + sol::protected_function_result result = luamgr->lua->safe_script_file(p.path().string()); + if (!result.valid()) { + sol::error err = result; + PrintGameConsole(err.what()); + Logger->error(err.what()); + } + } + catch(...){} + } + LeaveCriticalSection(&luamgr->luaCritSec); +} + +void RefreshCustomUIs() { + EnterCriticalSection(&luamgr->luaCritSec); + createdMenuItems.clear(); + menusToCreate.clear(); + LoadAllCustomUIScripts(); + LeaveCriticalSection(&luamgr->luaCritSec); +} + +void SetupB3HMTable() { + sol::table globals = luamgr->lua->globals(); + b3hmTable = luamgr->lua->create_table(); + globals["B3HM"] = b3hmTable; +} + +void BindLogger() { + if (!Logger) return; + luamgr->lua->set_function("LogInfo", [](const std::string& message) {if (Logger) Logger->info(message); }); + luamgr->lua->set_function("LogError", [](const std::string& message) {if (Logger) Logger->error(message); }); + luamgr->lua->set_function("LogCritical", [](const std::string& message) {if (Logger) Logger->critical(message); }); + luamgr->lua->set_function("LogWarning", [](const std::string& message) {if (Logger) Logger->warn(message); }); +} + +void ReadAndBindConfig() { + sol::table global = luamgr->lua->globals(); + sol::protected_function_result funcResult = luamgr->lua->safe_script_file("./lua/Configs/config.lua"); + if (!funcResult.valid()) { + sol::error err = funcResult; + PrintGameConsole(err.what()); + Logger->error("Failed to read config-file: {}", err.what()); + } + if (!global["config"].valid()) { + sol::table configTable = luamgr->lua->create_table(); + global["config"] = configTable; + } +} + +DWORD WINAPI MainThread(LPVOID param) +{ + atexit(callAtExit); + DebugHelper::CreateConsole(); + GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_PIN, L"BL3Lua.dll", &forRefCount); + + uintptr_t procEvent = PatternScan::FindSignature(NULL, processEventPattern.crypt_get()); + while (procEvent == NULL) + { + procEvent = PatternScan::FindSignature(NULL, processEventPattern.crypt_get()); + Sleep(50); + } + + MH_STATUS status; + status = MH_Initialize(); + while (status == MH_ERROR_NOT_INITIALIZED) + { + Sleep(200); + status = MH_Initialize(); + if (status == MH_ERROR_ALREADY_INITIALIZED) + break; + } + + try { + Logger = spdlog::basic_logger_mt("Testlogger", "./logs/BLua.log", true).get(); + Logger->set_pattern("[%H:%M:%S %z] [%l] %v"); + spdlog::flush_on(spdlog::level::level_enum::info); + } + catch (spdlog::spdlog_ex& ex) { + std::cout << "Failed to initialize logger: " << ex.what() << std::endl; + } + + uintptr_t gObjAddress = NULL; + uintptr_t gNameAddress = NULL; + int tries = 0; + + while (tries < 20) + { + if (gObjAddress == NULL) + { + gObjAddress = PatternScan::FindSignature(NULL, gObjPattern.crypt_get()); + } + if (gObjAddress != NULL) + { + const auto gObjOffset = *reinterpret_cast(gObjAddress + 8); + UObject::GObjects = reinterpret_cast(gObjAddress + 12 + (int)gObjOffset); + gObjInit = true; + } + if (gNameAddress == NULL) + { + gNameAddress = PatternScan::FindSignature(NULL, gNamesPattern.crypt_get()); + } + if (gNameAddress != NULL) + { + const auto gNameOffset = *reinterpret_cast(gNameAddress + 7); + FName::GNames = reinterpret_cast(*reinterpret_cast(gNameAddress + 4 + 7 + (int)gNameOffset)); + gNameInit = true; + } + if (gObjInit && gNameInit) + { + SuspendResume::Suspend(); + while (MH_CreateHookEx((LPVOID)procEvent, &hkProcessEvent, &oProcessEvent) != MH_OK) + Sleep(200); + MH_EnableHook(MH_ALL_HOOKS); + SuspendResume::Resume(); + + Logger->info("Create LuaMgr"); + luamgr = new BL3Lua(); + luamgr->lua->set_function("AddHook", sol::overload( + [](sol::this_state s, sol::this_environment e, const std::string &funcName, sol::function func) { luamgr->AddHook(s, e, funcName, func, true); }, + [](sol::this_state s, sol::this_environment e, const std::string &funcName, sol::function func, bool isPreHook) { luamgr->AddHook(s, e, funcName, func, isPreHook); })); + luamgr->lua->set_function("RemoveHook", &BL3Lua::RemoveHook, luamgr); + luamgr->lua->set_function("tdump", &BL3Lua::TDump, luamgr); + luamgr->lua->set_function("AddConsoleCommand", &BL3Lua::AddConsoleCommand, luamgr); + luamgr->lua->set_function("RemoveConsoleCommand", &BL3Lua::RemoveConsoleCommand, luamgr); + luamgr->lua->set_function("togglecam", &ToggleCam); + luamgr->lua->set_function("noclip", &ToggleNoclip); + luamgr->lua->set_function("AddConsoleKey", &AddConsoleKey); + luamgr->lua->set_function("SetConsoleKey", &SetConsoleKey); + //luamgr->lua->set_function("GetGlobalLog", []() { return GetGlobalLogSingleton(); }); + /*luamgr->lua->set_function("SetConsoleLogging", [&](bool enable) { + FOutputDeviceRedirector *logSingleton = GetGlobalLogSingleton(); + if (!myConsoleOutputDevice) + { + myConsoleOutputDevice = static_cast(oCreateConsoleOutputDevice()); + logSingleton->AddOutputDevice(myConsoleOutputDevice); + } + myConsoleOutputDevice->Show(enable); + });*/ + BindLogger(); + Logger->info("Create GUI-Bindings"); + CreateGUIBindings(); + CreateBLuaMenu(); + luamgr->lua->set_function("PrintGameConsole", &PrintGameConsole); + luamgr->lua->set_function("RefreshHotfixes", &RefreshHotfixes); + luamgr->lua->set_function("ReloadUI", &RefreshCustomUIs); + //ReadAndBindConfig(); + + Logger->info("Applying BaseHooks"); + ApplyBaseHooks(); + Logger->info("Scanning for more patterns."); + FindPatternsForTypedefs(); + Logger->info("Create & setup Console"); + ConsoleSetup(); + + Logger->info("Load UI-Files"); + LoadAllCustomUIScripts(); + nonScriptEnv = std::make_unique(sol::environment(luamgr->lua->lua_state(), sol::create, luamgr->lua->globals())).get(); + luamgr->lua->collect_garbage(); + + UObject::GetGameplayGlobals(); + SetupB3HMTable(); + //CreateCheatManager(); + break; + } + Sleep(150); + } + CreateThread(nullptr, NULL, (LPTHREAD_START_ROUTINE)WebSocketClientThread, NULL, NULL, nullptr); + CreateThread(nullptr, NULL, (LPTHREAD_START_ROUTINE)iWebSocketClientThread, NULL, NULL, nullptr); + CreateThread(nullptr, NULL, (LPTHREAD_START_ROUTINE)HotkeyThread, NULL, NULL, nullptr); + return TRUE; +} + +//extern "C" __declspec(dllexport) DWORD __stdcall RunScriptFile(const char *filePath) +//{ +// while (!luamgr) +// { +// Sleep(50); +// } +// if (luamgr) +// if (strstr(filePath, "NewDevScript") != NULL) +// { +// luamgr->ClearCallbacks(); +// luamgr->RemoveAllHooks(); +// } +// luamgr->ExecuteScriptFile(filePath); +// return true; +//} + +//extern "C" __declspec(dllexport) DWORD __stdcall RunLuaFunc(const char *funcName) +//{ +// while (!luamgr) +// { +// Sleep(50); +// } +// if (luamgr) +// { +// sol::protected_function f = (*luamgr->lua)[funcName]; +// sol::protected_function_result result = f(); +// if (!result.valid()) +// { +// sol::error err = result; +// std::cout << err.what() << std::endl; +// Logger->error(err.what()); +// } +// } +// return true; +//} + +//extern "C" __declspec(dllexport) LRESULT CALLBACK WndProc(int nCode, WPARAM wParam, LPARAM lParam) +//{ +// //std::cout << "Looks like this is working!" << std::endl; +// return CallNextHookEx(0, nCode, wParam, lParam); +//}; + +BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + //InitializeCriticalSection(&critsec); + DisableThreadLibraryCalls(hModule); + thisDLL = hModule; + CreateThread(nullptr, NULL, (LPTHREAD_START_ROUTINE)MainThread, hModule, NULL, nullptr); + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} diff --git a/BitFields.hpp b/BitFields.hpp new file mode 100644 index 0000000..076b030 --- /dev/null +++ b/BitFields.hpp @@ -0,0 +1,88 @@ +#pragma once +#include +#include +#include +#include + +// https://stackoverflow.com/a/263738 +/* a=target variable, b=bit number to act upon 0-n */ +#define BIT_SET(a,b) ((a) |= (1ULL<<(b))) +#define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b))) +#define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b))) +#define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b)))) // '!!' to make sure this returns 0 or 1 + +/* x=target variable, y=mask */ +#define BITMASK_SET(x,y) ((x) |= (y)) +#define BITMASK_CLEAR(x,y) ((x) &= (~(y))) +#define BITMASK_FLIP(x,y) ((x) ^= (y)) +#define BITMASK_CHECK_ALL(x,y) (!(~(x) & (y))) +#define BITMASK_CHECK_ANY(x,y) ((x) & (y)) + +namespace itsy_bitsy { + + template + struct bit_type { + typedef uint64_t type; + }; + + template + struct bit_type> { + typedef bool type; + }; + + template + struct bit_type 2 && sz <= 16)>> { + typedef uint16_t type; + }; + + template + struct bit_type 16 && sz <= 32)>> { + typedef uint32_t type; + }; + + template + struct bit_type 32 && sz <= 64)>> { + typedef uint64_t type; + }; + + template + using bit_type_t = typename bit_type::type; + + template + bool vcxx_warning_crap(std::true_type, V val) { + return val != 0; + } + + template + T vcxx_warning_crap(std::false_type, V val) { + return static_cast(val); + } + + template + auto vcxx_warning_crap(V val) { + return vcxx_warning_crap(std::is_same(), val); + } + + template + void write(Base& b, bit_type_t bits) { + uintptr_t baseAddr = reinterpret_cast(&b); + uintptr_t offs = baseOffset; + unsigned char* byt = reinterpret_cast(baseAddr + offs); + char testb = *byt & (1 << bit_target); + if (bits) + BIT_SET(*byt, bit_target); + else + BIT_CLEAR(*byt, bit_target); + } + + template + bit_type_t read(Base& b) { + uintptr_t baseAddr = reinterpret_cast(&b); + uintptr_t offs = baseOffset; + unsigned char* byt = reinterpret_cast(baseAddr + offs); + char test = *byt & (1 << bit_target); + if (test) + return true; + return false; + } +} \ No newline at end of file diff --git a/Helper.hpp b/Helper.hpp new file mode 100644 index 0000000..0a7ddf3 --- /dev/null +++ b/Helper.hpp @@ -0,0 +1,78 @@ +#pragma once +#include + +/* +A safer replacement for the obsolete IsBadReadPtr() and IsBadWritePtr() WinAPI functions +on top of VirtualQuery() which respects Windows guard pages. It does not use SEH +and is designed to be compatible with the above-mentioned functions. +The calls to the IsBadReadPtr() and IsBadWritePtr() can be replaced with the calls to +the IsBadMemPtr() as follows: +- IsBadReadPtr(...) => IsBadMemPtr(FALSE, ...) +- IsBadWritePtr(...) => IsBadMemPtr(TRUE, ...) +*/ +BOOL IsBadMemPtr(/*BOOL write, */void* ptr, size_t size) +{ + MEMORY_BASIC_INFORMATION mbi; + BOOL ok; + DWORD mask; + BYTE* p = (BYTE*)ptr; + BYTE* maxp = p + size; + BYTE* regend = NULL; + + if (size == 0) + { + return FALSE; + } + + if (p == NULL) + { + return TRUE; + } + + /*if (write == FALSE) + { + mask = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; + } + else + {*/ + mask = PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY; + /*}*/ + + do + { + if (p == ptr || p == regend) + { + if (VirtualQuery((LPCVOID)p, &mbi, sizeof(mbi)) == 0) + { + return TRUE; + } + else + { + regend = ((BYTE*)mbi.BaseAddress + mbi.RegionSize); + } + } + + ok = (mbi.Protect & mask) != 0; + + if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) + { + ok = FALSE; + } + + if (!ok) + { + return TRUE; + } + + if (maxp <= regend) /* the whole address range is inside the current memory region */ + { + return FALSE; + } + else if (maxp > regend) /* this region is a part of (or overlaps with) the address range we are checking */ + { + p = regend; /* lets move to the next memory region */ + } + } while (p < maxp); + + return FALSE; +} \ No newline at end of file diff --git a/LuaComponent.cpp b/LuaComponent.cpp new file mode 100644 index 0000000..63269f4 --- /dev/null +++ b/LuaComponent.cpp @@ -0,0 +1,61 @@ +#include "LuaComponent.h" + +LuaComponent::LuaComponent() { + this->m_Env = nullptr; + this->m_FileName = ""; +} + +LuaComponent::LuaComponent(const std::string& fileName, sol::state* state) { + this->m_FileName = fileName; + this->state = state; +} +LuaComponent::~LuaComponent() { + if (m_Env) { + sol::protected_function releaseFunc = m_Env["OnRelease"]; + if (releaseFunc.valid()) { + releaseFunc.call(); + } + m_Env.clear(); + m_Env = sol::lua_nil; + } +} + +void LuaComponent::Init() { + LoadScript(m_FileName); + OnInit(); +} + +void LuaComponent::OnInit() { + if (m_Env) { + sol::protected_function initFunc = m_Env["OnInit"]; + if (initFunc.valid()) { + initFunc.call(); + } + } +} + +void LuaComponent::LoadScript(const std::string& fileName) { + m_FileName = fileName; + //m_Env = std::make_unique(sol::environment(*state, sol::create, state->globals())).get(); + m_Env = sol::environment(*state, sol::create, state->globals()); + sol::load_result error = state->load_file(fileName); + + if (error.status() == sol::load_status::ok) { + try { + auto func_result = state->safe_script_file(fileName, m_Env); + if (!func_result.valid()) { + sol::error err = error; + std::cout << "[LUA] lua_dofile failed with error: " << err.what() << std::endl; + sol::protected_function printFunc = (*(state))["PrintGameConsole"]; + if (printFunc.valid()) { + std::string errorMessage = std::string("[LUA] failed with error: ").append(err.what()); + printFunc(errorMessage); + } + } + } + catch (...) { + + } + } + state->collect_garbage(); +} \ No newline at end of file diff --git a/LuaUtility.cpp b/LuaUtility.cpp new file mode 100644 index 0000000..06e0cd0 --- /dev/null +++ b/LuaUtility.cpp @@ -0,0 +1,151 @@ +#include "LuaUtility.h" +#include +#include +#include + +static std::unordered_map keyMap; + +LuaUtility::LuaUtility() +{ + keyMap = GetKeyMap(); +} + +bool LuaUtility::IsKeyPressed(std::string keyName) +{ + if (keyName.length() > 1) { + std::transform(keyName.begin(), keyName.end(), keyName.begin(), [](unsigned char c) {return std::tolower(c); }); + auto keycode = GetKeyMap()[keyName]; + return (GetAsyncKeyState(GetKeyMap()[keyName]) & 0x8000) != 0; + } + else { + std::transform(keyName.begin(), keyName.end(), keyName.begin(), [](unsigned char c) {return std::toupper(c); }); + return (GetAsyncKeyState(keyName.c_str()[0]) & 0x8000) != 0; + } + return false; +} + +bool LuaUtility::IsKeyPressed(std::string keyName, std::string keyName2) +{ + std::vector keys; + std::string current = keyName; + for (int i = 0; i < 2; i++) { + if (current.length() > 1) { + std::transform(current.begin(), current.end(), current.begin(), [](unsigned char c) {return std::tolower(c); }); + keys.push_back(GetKeyMap()[current]); + } + else { + std::transform(current.begin(), current.end(), current.begin(), [](unsigned char c) {return std::toupper(c); }); + keys.push_back(current.c_str()[0]); + } + current = keyName2; + } + return ((GetAsyncKeyState(keys[0]) & 0x8000) && GetAsyncKeyState(keys[1]) & 0x8000) != 0; +} + +bool LuaUtility::IsKeyPressed(std::string keyName, std::string keyName2, std::string keyName3) +{ + std::vector keys; + std::string current = keyName; + for (int i = 0; i < 3; i++) { + if (current.length() > 1) { + std::transform(current.begin(), current.end(), current.begin(), [](unsigned char c) {return std::tolower(c); }); + keys.push_back(GetKeyMap()[current]); + } + else { + std::transform(current.begin(), current.end(), current.begin(), [](unsigned char c) {return std::toupper(c); }); + keys.push_back(current.c_str()[0]); + } + if (i == 0) + current = keyName2; + else + current = keyName3; + } + return ((GetAsyncKeyState(keys[0]) & 0x8000) && GetAsyncKeyState(keys[1] & 0x8000) && GetAsyncKeyState(keys[2]) & 0x8000) != 0; +} + +bool LuaUtility::ContainsKey(std::string keyName) { + if (keyMap.size() == 0) + keyMap = GetKeyMap(); + std::transform(keyName.begin(), keyName.end(), keyName.begin(), [](unsigned char c) {return std::tolower(c); }); + return keyMap.find(keyName) != keyMap.end(); +} + +std::unordered_map LuaUtility::GetKeyMap() +{ + if (!keyMap.size()) { + keyMap.insert(std::pair("backspace", VK_BACK)); + keyMap.insert(std::pair("tab", VK_TAB)); + keyMap.insert(std::pair("clear", VK_CLEAR)); + keyMap.insert(std::pair("return", VK_RETURN)); + keyMap.insert(std::pair("enter", VK_RETURN)); + keyMap.insert(std::pair("shift", VK_SHIFT)); + keyMap.insert(std::pair("ctrl", VK_CONTROL)); + keyMap.insert(std::pair("alt", VK_MENU)); + keyMap.insert(std::pair("pause", VK_PAUSE)); + keyMap.insert(std::pair("capslock", VK_CAPITAL)); + keyMap.insert(std::pair("escape", VK_ESCAPE)); + keyMap.insert(std::pair("space", VK_SPACE)); + keyMap.insert(std::pair("pageup", VK_PRIOR)); + keyMap.insert(std::pair("pagedown", VK_NEXT)); + keyMap.insert(std::pair("end", VK_END)); + keyMap.insert(std::pair("home", VK_HOME)); + keyMap.insert(std::pair("left", VK_LEFT)); + keyMap.insert(std::pair("up", VK_UP)); + keyMap.insert(std::pair("right", VK_RIGHT)); + keyMap.insert(std::pair("down", VK_DOWN)); + keyMap.insert(std::pair("printscr", VK_SNAPSHOT)); + keyMap.insert(std::pair("insert", VK_INSERT)); + keyMap.insert(std::pair("delete", VK_DELETE)); + + keyMap.insert(std::pair("f1", VK_F1)); + keyMap.insert(std::pair("f2", VK_F2)); + keyMap.insert(std::pair("f3", VK_F3)); + keyMap.insert(std::pair("f4", VK_F4)); + keyMap.insert(std::pair("f5", VK_F5)); + keyMap.insert(std::pair("f6", VK_F6)); + keyMap.insert(std::pair("f7", VK_F7)); + keyMap.insert(std::pair("f8", VK_F8)); + keyMap.insert(std::pair("f9", VK_F9)); + keyMap.insert(std::pair("f10", VK_F10)); + keyMap.insert(std::pair("f11", VK_F11)); + keyMap.insert(std::pair("f12", VK_F12)); + + keyMap.insert(std::pair("numpad0", VK_NUMPAD0)); + keyMap.insert(std::pair("numpad1", VK_NUMPAD1)); + keyMap.insert(std::pair("numpad2", VK_NUMPAD2)); + keyMap.insert(std::pair("numpad3", VK_NUMPAD3)); + keyMap.insert(std::pair("numpad4", VK_NUMPAD4)); + keyMap.insert(std::pair("numpad5", VK_NUMPAD5)); + keyMap.insert(std::pair("numpad6", VK_NUMPAD6)); + keyMap.insert(std::pair("numpad7", VK_NUMPAD7)); + keyMap.insert(std::pair("numpad8", VK_NUMPAD8)); + keyMap.insert(std::pair("numpad9", VK_NUMPAD9)); + + keyMap.insert(std::pair("multiply", VK_MULTIPLY)); + keyMap.insert(std::pair("add", VK_ADD)); + keyMap.insert(std::pair("subtract", VK_SUBTRACT)); + keyMap.insert(std::pair("decimal", VK_DECIMAL)); + keyMap.insert(std::pair("divide", VK_DIVIDE)); + keyMap.insert(std::pair("numlock", VK_NUMLOCK)); + + keyMap.insert(std::pair("0", 0x30)); + keyMap.insert(std::pair("1", 0x31)); + keyMap.insert(std::pair("2", 0x32)); + keyMap.insert(std::pair("3", 0x33)); + keyMap.insert(std::pair("4", 0x34)); + keyMap.insert(std::pair("5", 0x35)); + keyMap.insert(std::pair("6", 0x36)); + keyMap.insert(std::pair("7", 0x37)); + keyMap.insert(std::pair("8", 0x38)); + keyMap.insert(std::pair("9", 0x39)); + + keyMap.insert(std::pair("scrollock", VK_SCROLL)); + keyMap.insert(std::pair("lshift", VK_LSHIFT)); + keyMap.insert(std::pair("rshift", VK_RSHIFT)); + keyMap.insert(std::pair("lctrl", VK_LCONTROL)); + keyMap.insert(std::pair("rctrl", VK_RCONTROL)); + keyMap.insert(std::pair("lalt", VK_LMENU)); + keyMap.insert(std::pair("ralt", VK_RMENU)); + } + return keyMap; +} diff --git a/LuaUtility.h b/LuaUtility.h new file mode 100644 index 0000000..d061165 --- /dev/null +++ b/LuaUtility.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include +#include + +class LuaUtility +{ +public: + + LuaUtility(); + static bool IsKeyPressed(std::string keyName); + static bool IsKeyPressed(std::string keyName, std::string keyName2); + static bool IsKeyPressed(std::string keyName, std::string keyName2, std::string keyName3); + static bool ContainsKey(std::string keyName); + +private: + static std::unordered_map GetKeyMap(); +}; \ No newline at end of file diff --git a/include/LuaComponent.h b/include/LuaComponent.h new file mode 100644 index 0000000..b233069 --- /dev/null +++ b/include/LuaComponent.h @@ -0,0 +1,21 @@ +#pragma once +#include "F:/Programmieren/Lua/sol2/single/single/include/sol/sol.hpp" +#include + +class LuaComponent +{ +public: + LuaComponent(); + LuaComponent(const std::string& fileName, sol::state* state); + ~LuaComponent(); + + void Init(); + void OnInit(); + void LoadScript(const std::string& fileName); + +public: + std::string m_FileName; + sol::environment m_Env; + sol::state* state; +}; + diff --git a/include/Patterns.h b/include/Patterns.h new file mode 100644 index 0000000..5e2dc69 --- /dev/null +++ b/include/Patterns.h @@ -0,0 +1,73 @@ +#pragma once +#include "F:/Programmieren/C++/xorstr-master/include/xorstr.hpp" + +auto gObjPattern = xorstr("44 8b 54 24 50 48 8d 05 ?? ?? ?? ?? ?? f6 48 89 01 48 89 71 10"); +auto gNamesPattern = xorstr("48 83 EC 28 48 8B 05 ?? ?? ?? ?? 48 85 C0 75 5F B9 08 04 ?? ?? 48 89 5C 24 20 E8"); +auto processEventPattern = xorstr("40 55 56 57 41 54 41 55 41 56 41 57 48 81 EC ?? ?? ?? ?? 48 8D ?? ?? ?? 48 89 9D ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C5 48 89 85 ?? ?? ?? ?? 8B 41"); +auto getTransientPackagePattern = xorstr("48 8b 4c 24 50 48 89 05 ?? ?? ?? ?? 48 85 c9 74 05 e8"); +auto mallocPattern = xorstr("48 89 5C 24 ?? 57 48 83 EC 20 48 8B F9 8B DA 48 8B 0D ?? ?? ?? ?? 48 85 C9 75 0C E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B 01 44 8B C3 48 8B D7 48 8B 5C 24 ?? 48 83 C4 20 5F 48 FF 60 10"); +auto reallocPattern = xorstr("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B F1 41 8B D8 48 8B 0D ?? ?? ?? ?? 48 8B FA 48 85 C9 75 0C E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B 01 44 8B CB 4C 8B C7 48 8B D6 48 8B 5C 24 ?? 48 8B 74 24 ?? 48 83 C4 20 5F 48 FF 60 18"); +auto fnamectorPattern = xorstr("40 53 48 83 EC 30 C7 44 24"); +auto ufunctionBindPattern = xorstr("40 ?? 48 83 EC ?? 48 8B ?? E8 ?? ?? ?? ?? F7 83 88 00 00 00"); +auto ftextfromstringPattern = xorstr("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 30 48 63 7A ?? 33 C0 48 8B 32 48 8B D9 48 89 44 24 ?? 89 7C 24 ?? 85 FF 75 ?? 89 44 24 ?? EB ?? 45 33 C0 48 8D 4C 24 ?? 8B D7 E8 ?? ?? ?? ?? 48 8B"); +auto getWorldFromContextObjectPattern = xorstr("48 89 5c 24 18 56 48 83 ec 40 41 8b d8 48 8b f2 48 85 d2 75 2a 83 fb 01 75 18 33 c0 48 8d"); +auto isValidLowLevelPattern = xorstr("4C 8B C1 48 85 C9 74 4F"); +auto staticConstructObjectPattern = xorstr("48 89 5C 24 ?? 55 56 57 41 54 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 85 ?? ?? ?? ?? 44 8B A5"); +auto staticDuplicateObjectPattern = xorstr("4C 89 44 24 ?? 55 53 56 57 41 54 41 56 41 57 48 8D 6C 24 ?? 48 81 EC ?? ?? ?? ?? 48 8B 05"); +auto newObjectPattern = xorstr("48 89 5c 24 18 55 56 57 41 54 41 55 41 56 41 57 48 8d ac 24 40 ff ff ff 48 81 ec c0 01 00 00 48 8b 05 ?? ?? ?? ?? 48 33 c4 48 89 85 b0 00 00 00 44 8b a5 20 01 00 00"); +auto getWorldPattern = xorstr("40 53 48 83 EC 20 8B 41 ?? 48 8B D9 C1 E8 04 A8 01 75 ?? 48 8B 49"); +auto spawnActorPattern = xorstr("40 53 56 57 48 83 EC 70 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 44 24 ?? 0F 28 1D"); +auto getMapNamePattern = xorstr("48 89 5C 24 ?? 56 48 83 EC 30 33 F6 48 8B D9 48 89 31 44 8B C6"); +auto functionInvokePattern = xorstr("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 20 8B 41 08 4D 8B F1"); +auto callFuncPattern = xorstr("48 89 ?? ?? ?? 48 89 ?? ?? ?? 57 48 83 EC ?? 48 8B ?? ?? 48 8B ?? 49 8B ?? 48 8B ?? F2 0F"); +auto staticExecPattern = xorstr("4C 89 ?? ?? ?? 55 53 56 41 ?? 41 ?? 48 8B"); +auto getGlobalLogSingletonPattern = xorstr("48 83 EC 28 65 48 8B 04 25 ?? ?? ?? ?? 8B 0D ?? ?? ?? ?? BA ?? ?? ?? ?? 48 8B 0C C8 8B 04 0A 39 05 ?? ?? ?? ?? 7F 0C 48 8D 05 ?? ?? ?? ?? 48 83 C4 28 C3 48 8D 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 83 3D ?? ?? ?? ?? ?? 75 DF 48 8D 05 ?? ?? ?? ?? 66 C7 05"); +auto consoleOutputDeviceShowPattern = xorstr("40 55 53 57 48 8D 6C 24 ?? 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 45 2F 48 8B 41 20"); +auto addOutputDevicePattern = xorstr("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 54 24 ?? 57 48 83 EC 20 48 8B F1 48 8B FA 48 83 C1 48"); +auto createConsoleOutputDevice = xorstr("48 83 EC 28 B9 ?? ?? ?? ?? E8 ?? ?? ?? ?? 33 C9 48 85 C0 74 25 66 C7 40"); +auto consoleOutputTextPattern = xorstr("48 8B C4 55 56 48 8D 68 A1 48 81 EC ?? ?? ?? ?? 48 89 58 08 48 63 5A 08"); +auto consoleClearOutputPattern = xorstr("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8B 59 50 48 8D 71 50"); +auto addToViewportPattern = xorstr("48 8B 01 44 8B C2 33 D2"); +auto registerFunctionAPattern = xorstr("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 49 8B F0 48 8B D9 41 B8"); +auto registerFunctionOtherPattern = xorstr("45 85 C0 0F 84 ?? ?? ?? ?? 4C 8B DC 41 56"); +auto staticLoadObjectPattern = xorstr("40 ?? 53 56 57 41 ?? 41 ?? 41 ?? 41 ?? 48 8D ?? ?? ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? 48 33 ?? 48 89 ?? ?? ?? ?? ?? 0F B6 ?? ?? ?? ?? ?? 49 8B ?? 4C 8B ?? ?? ?? ?? ?? 33 DB"); +auto staticFindObjectPattern = xorstr("48 89 5C 24 ?? 48 89 74 24 ?? 55 57 41 54 41 56 41 57 48 8B EC 48 83 EC 60 80 3D"); +auto dumpObjectToStringPattern = xorstr("4C 8B ?? 53 48 81 EC ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? 48 33 ?? 48 89 ?? ?? ?? ?? ?? ?? 49 89 ?? ?? B9"); +auto FOakPlayerManager__DoSparkAuthenticationPattern = xorstr("48 8B ?? 55 53 56 48 8D ?? ?? 48 81 EC ?? ?? ?? ?? 48 89 ?? ?? 33 FF"); +auto FOakStartupProcess__PerformStepPattern = xorstr("40 ?? 53 56 57 48 8D ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B ?? ?? 48 8B ?? 48 89"); +auto FGbxSparkModuleGetPattern = xorstr("48 83 EC ?? 48 8B ?? ?? ?? ?? ?? 48 85 ?? 75 ?? 44 8D ?? ?? 48 89 ?? ?? ?? 48 8D ?? ?? ?? ?? ?? 48 8D ?? ?? ?? E8 ?? ?? ?? ?? 48 8B ?? E8 ?? ?? ?? ?? 48 8B ?? 48 8B ?? E8 ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? 48 8B ?? ?? ?? 48 83 C4 ?? C3 CC CC 48 89 ?? ?? ?? 57"); +auto FSparkInitProcessStartProcessPattern = xorstr("40 57 48 83 EC 20 48 83 3D ?? ?? ?? ?? ?? 48 8B F9 75 34"); +auto FSparkInitProcessReadDiscoveryPattern = xorstr("48 8B ?? 55 48 8D ?? ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 89 ?? ?? 48 89 ?? ?? 0F B6"); +auto oakGameInstanceProcessPendingMicropatchesPattern = xorstr("4C 8B ?? 55 56 41 ?? 49 8D ?? ?? 48 81 EC ?? ?? ?? ?? 4C 8D"); +auto oakGameStateRefreshMicropatchSwitchesPattern = xorstr("40 ?? 48 83 EC ?? 48 8B ?? ?? ?? ?? ?? 48 33 ?? 48 89 ?? ?? ?? 80 B9 28 01 00 00"); + +auto buildMainMenuPattern = xorstr("40 ?? 56 41 ?? 48 8D ?? ?? ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? 48 33 ?? 48 89 ?? ?? ?? ?? ?? 45 33"); +auto buildPauseMenuPattern = xorstr("48 8B ?? 55 53 57 48 8D ?? ?? 48 81 EC ?? ?? ?? ?? 48 89 ?? ?? 48 8B ?? 4C 89 ?? ?? 4C 89 ?? ?? 4C 89"); +auto buildOptionsMenuPattern = xorstr("48 8B C4 55 56 48 8D 68 A1 48 81 EC ?? ?? ?? ?? 48 83 B9 ?? ?? ?? ?? ?? 48 8B F1 0F 84 ?? ?? ?? ?? 80 B9 ?? ?? ?? ?? ?? 48 89 58 E8"); +auto mainAndPauseMenuAddMenuItemPattern = xorstr("48 89 54 24 ?? 48 89 4C 24 ?? 55 53 56 57 41 55 41 56 41 57 48 8D 6C 24 ?? 48 81 EC ?? ?? ?? ?? 48 83 B9"); +auto mainAndPauseMenuRemoveAllItemsPattern = xorstr("40 ?? 48 83 EC ?? 48 8B ?? 48 81 C1 ?? ?? ?? ?? 83 79 0C ?? C7 41 08 ?? ?? ?? ?? 74 ?? 33 D2 E8 ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? 48 85"); +auto mainAndPauseMenuStartMenuTransitionPattern = xorstr("48 89 5C 24 ?? 55 57 41 56 48 81 EC ?? ?? ?? ?? 83 B9"); +auto scrollingListInsertListItemPattern = xorstr("48 8B ?? 4C 89 ?? ?? 55 48 8D ?? ?? 48 81 EC ?? ?? ?? ?? 48 89 ?? ?? 4C 8B"); +auto listItemNumberInitializeItemPattern = xorstr("40 ?? 48 83 EC ?? 0F 29 ?? ?? ?? 48 8B ?? 48 8B ?? ?? ?? ?? ?? 0F 28 ?? 0F 29 ?? ?? ?? 0F 28 ?? 48 85 ?? 74 ?? 48 8B ?? 45 33"); +auto optionBaseCreateContentPanelPattern = xorstr("40 ?? 48 83 EC ?? 48 8B ?? ?? ?? 48 8B ?? 48 89 ?? ?? 48 8B"); +auto optionBaseCreateContentPanelItemPattern = xorstr("48 85 ?? 0F 84 ?? ?? ?? ?? 56 57 48 83 EC ?? 48 8B ?? 48 8B ?? E8 ?? ?? ?? ?? 84 C0"); +auto optionBaseMenuLoadBaseOptionsMenuPattern = xorstr("40 ?? 41 ?? 48 8B ?? 48 83 EC ?? 4C 8B ?? E8 ?? ?? ?? ?? 49 83 BE 10 06 00 00"); +auto gfxOptionsMenuCreateOptionPanelPattern = xorstr("89 54 ?? ?? 55 56 41 ?? 48 8D ?? ?? ?? ?? ?? ?? 48 81 EC"); +auto gfxOptionsMenuOnGFxMenuIsInitedAndStartedBeginPattern = xorstr("40 ?? 48 83 EC ?? 48 8B ?? E8 ?? ?? ?? ?? 48 8B ?? E8 ?? ?? ?? ?? 8B 15"); + +auto optionBaseSetupSpinnerItemPattern = xorstr("48 89 ?? ?? ?? 48 89 ?? ?? ?? 48 89 ?? ?? ?? 57 48 83 EC ?? 49 8B ?? 41 8B ?? 48 8B ?? 48 8B ?? 48 85 ?? 0F 84 ?? ?? ?? ?? 48 8D ?? ?? ?? 48 8B ?? E8 ?? ?? ?? ?? 4C 8D ?? ?? ?? ?? ?? 48 89 ?? ?? ?? 44 8B ?? 48 8D ?? ?? ?? 48 8B ?? E8 ?? ?? ?? ?? 48 8B ?? 48 85 ?? 74 ?? 48 8B ?? 48 8B ?? E8 ?? ?? ?? ?? 48 83 7F 70"); +auto optionBaseSetupSpinnerItemAsBooleanPattern = xorstr("48 8B ?? 48 89 ?? ?? 55 57 41 ?? 48 81 EC ?? ?? ?? ?? 49 8B ?? 41 0F"); +auto optionBaseSetupSpinnerItemWithTextPattern = xorstr("48 89 ?? ?? ?? 56 57 41 ?? 48 83 EC ?? 48 83 B9 80 00 00 00 ?? 41 8B"); +auto optionBaseSetupTitleItemPattern = xorstr("4C 8B ?? 55 53 57 49 8D ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? 48 33 ?? 48 89 ?? ?? 48 83 B9 80 00 00 00"); +auto optionBaseSetupButtonItemPattern = xorstr("48 89 ?? ?? ?? 48 89 ?? ?? ?? 41 ?? 48 83 EC ?? 49 8B ?? 48 8B ?? 4C 8B ?? 48 85 ?? 0F 84"); +auto optionBaseSetupSliderItemPattern = xorstr("48 8B ?? 55 56 41 ?? 48 81 EC ?? ?? ?? ?? 0F 29 ?? ?? 0F 28 ?? 4D 8B"); +auto optionBaseSetupDropdownListItemPattern = xorstr("48 89 ?? ?? ?? 48 89 ?? ?? ?? 48 89 ?? ?? ?? 57 48 83 EC ?? 41 8B ?? 49 8B ?? 48 8B ?? 48 8B ?? 48 85 ?? 0F 84 ?? ?? ?? ?? 48 8D ?? ?? ?? 48 8B ?? E8 ?? ?? ?? ?? 48 8B ?? ?? ?? ?? ?? ?? 48 8D ?? ?? ?? 44 8B ?? 48 89 ?? ?? ?? 4C 8B ?? 48 8B ?? E8 ?? ?? ?? ?? 48 8B ?? 48 85 ?? 74 ?? 48 8B ?? 48 8B ?? E8 ?? ?? ?? ?? 48 83 7F 70 ?? 74 ?? 80 BB 17 01 00 00 ?? 74 ?? 48 8B ?? ?? 48 85 ?? 74 ?? E8 ?? ?? ?? ?? 84 C0 74 ?? B2 ?? EB ?? 32 D2 48 8D ?? ?? ?? ?? ?? 48 8B ?? FF 50 ?? 48 8B"); +auto optionBaseSetupControlsItemPattern = xorstr("48 8B ?? 57 41 ?? 41 ?? 41 ?? 48 83 EC ?? 4D 8B"); + +auto listItemSpinnerSetDefaultValuePattern = xorstr("48 8B ?? 89 91 ?? ?? ?? ?? C6 81 3C 03 00 00"); +auto listItemNumberGetCurrentValuePattern = xorstr("48 89 ?? ?? ?? 57 48 83 EC ?? 48 8B ?? 48 8B ?? 48 8B ?? ?? ?? ?? ?? 48 85 ?? 74 ?? E8 ?? ?? ?? ?? 48 85 ?? 74 ?? 48 8B ?? E8 ?? ?? ?? ?? F3 0F ?? ?? ?? ?? ?? ?? 65 48 ?? ?? ?? ?? ?? ?? ?? 8B 0D ?? ?? ?? ?? BA ?? ?? ?? ?? 48 8B ?? ?? 8B 04 ?? 39 05 ?? ?? ?? ?? 0F 8F ?? ?? ?? ?? F3 0F ?? ?? ?? ?? ?? ?? 48 8D ?? ?? ?? ?? ?? F3 0F"); +auto listItemNumberSetDefaultValuePattern = xorstr("40 ?? 48 83 EC ?? F3 0F ?? ?? ?? ?? ?? ?? 48 8B ?? C6 81 84 03 00 00"); + +auto listItemComboBoxSetDefaultValuePattern = xorstr("48 8B ?? ?? ?? ?? ?? 89 91 ?? ?? ?? ?? C6 81 2C 03 00 00"); +auto listItemComboBoxGetSelectedIndexPattern = xorstr("48 8B ?? ?? ?? ?? ?? 48 85 ?? 74 ?? 48 8B ?? ?? ?? ?? ?? 8B 80 ?? ?? ?? ?? C3"); +auto listItemSpinnerGetCurrentSelectionIndexPattern = xorstr("48 8B ?? ?? ?? ?? ?? 48 85 ?? 0F 85 ?? ?? ?? ?? B8 ?? ?? ?? ?? C3 CC CC CC CC CC CC CC CC CC CC 48 8B"); \ No newline at end of file diff --git a/include/Utility.hpp b/include/Utility.hpp new file mode 100644 index 0000000..61208a1 --- /dev/null +++ b/include/Utility.hpp @@ -0,0 +1,14 @@ +#pragma once + +std::string Replace(std::string str, const std::string& strToFind, const std::string& replaceWith) { + size_t index = 0; + std::string result = str; + while (true) { + index = result.find(strToFind, index); + if (index == std::string::npos) + break; + result.replace(index, strToFind.size(), replaceWith); + index += replaceWith.size(); + } + return result; +} \ No newline at end of file diff --git a/include/json.lua b/include/json.lua new file mode 100644 index 0000000..270dd4a --- /dev/null +++ b/include/json.lua @@ -0,0 +1,390 @@ +R"( +-- +-- json.lua +-- +-- Copyright (c) 2020 rxi +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy of +-- this software and associated documentation files (the "Software"), to deal in +-- the Software without restriction, including without limitation the rights to +-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +-- of the Software, and to permit persons to whom the Software is furnished to do +-- so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in all +-- copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +local json = { _version = "0.1.2" } + +------------------------------------------------------------------------------- +-- Encode +------------------------------------------------------------------------------- + +local encode + +local escape_char_map = { + [ "\\" ] = "\\", + [ "\"" ] = "\"", + [ "\b" ] = "b", + [ "\f" ] = "f", + [ "\n" ] = "n", + [ "\r" ] = "r", + [ "\t" ] = "t", +} + +local escape_char_map_inv = { [ "/" ] = "/" } +for k, v in pairs(escape_char_map) do + escape_char_map_inv[v] = k +end + + +local function escape_char(c) + return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte())) +end + + +local function encode_nil(val) + return "null" +end + + +local function encode_table(val, stack) + local res = {} + stack = stack or {} + + -- Circular reference? + if stack[val] then error("circular reference") end + + stack[val] = true + + if rawget(val, 1) ~= nil or next(val) == nil then + -- Treat as array -- check keys are valid and it is not sparse + local n = 0 + for k in pairs(val) do + if type(k) ~= "number" then + error("invalid table: mixed or invalid key types") + end + n = n + 1 + end + if n ~= #val then + error("invalid table: sparse array") + end + -- Encode + for i, v in ipairs(val) do + table.insert(res, encode(v, stack)) + end + stack[val] = nil + return "[" .. table.concat(res, ",") .. "]" + + else + -- Treat as an object + for k, v in pairs(val) do + if type(k) ~= "string" then + error("invalid table: mixed or invalid key types") + end + table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) + end + stack[val] = nil + return "{" .. table.concat(res, ",") .. "}" + end +end + + +local function encode_string(val) + return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' +end + + +local function encode_number(val) + -- Check for NaN, -inf and inf + if val ~= val or val <= -math.huge or val >= math.huge then + error("unexpected number value '" .. tostring(val) .. "'") + end + return string.format("%.14g", val) +end + + +local type_func_map = { + [ "nil" ] = encode_nil, + [ "table" ] = encode_table, + [ "string" ] = encode_string, + [ "number" ] = encode_number, + [ "boolean" ] = tostring, +} + + +encode = function(val, stack) + local t = type(val) + local f = type_func_map[t] + if f then + return f(val, stack) + end + error("unexpected type '" .. t .. "'") +end + + +function json.encode(val) + return ( encode(val) ) +end + + +------------------------------------------------------------------------------- +-- Decode +------------------------------------------------------------------------------- + +local parse + +local function create_set(...) + local res = {} + for i = 1, select("#", ...) do + res[ select(i, ...) ] = true + end + return res +end + +local space_chars = create_set(" ", "\t", "\r", "\n") +local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") +local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") +local literals = create_set("true", "false", "null") + +local literal_map = { + [ "true" ] = true, + [ "false" ] = false, + [ "null" ] = nil, +} + + +local function next_char(str, idx, set, negate) + for i = idx, #str do + if set[str:sub(i, i)] ~= negate then + return i + end + end + return #str + 1 +end + + +local function decode_error(str, idx, msg) + local line_count = 1 + local col_count = 1 + for i = 1, idx - 1 do + col_count = col_count + 1 + if str:sub(i, i) == "\n" then + line_count = line_count + 1 + col_count = 1 + end + end + error( string.format("%s at line %d col %d", msg, line_count, col_count) ) +end + + +local function codepoint_to_utf8(n) + -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa + local f = math.floor + if n <= 0x7f then + return string.char(n) + elseif n <= 0x7ff then + return string.char(f(n / 64) + 192, n % 64 + 128) + elseif n <= 0xffff then + return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) + elseif n <= 0x10ffff then + return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, + f(n % 4096 / 64) + 128, n % 64 + 128) + end + error( string.format("invalid unicode codepoint '%x'", n) ) +end + + +local function parse_unicode_escape(s) + local n1 = tonumber( s:sub(1, 4), 16 ) + local n2 = tonumber( s:sub(7, 10), 16 ) + -- Surrogate pair? + if n2 then + return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) + else + return codepoint_to_utf8(n1) + end +end + + +local function parse_string(str, i) + local res = "" + local j = i + 1 + local k = j + + while j <= #str do + local x = str:byte(j) + + if x < 32 then + decode_error(str, j, "control character in string") + + elseif x == 92 then -- `\`: Escape + res = res .. str:sub(k, j - 1) + j = j + 1 + local c = str:sub(j, j) + if c == "u" then + local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) + or str:match("^%x%x%x%x", j + 1) + or decode_error(str, j - 1, "invalid unicode escape in string") + res = res .. parse_unicode_escape(hex) + j = j + #hex + else + if not escape_chars[c] then + decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string") + end + res = res .. escape_char_map_inv[c] + end + k = j + 1 + + elseif x == 34 then -- `"`: End of string + res = res .. str:sub(k, j - 1) + return res, j + 1 + end + + j = j + 1 + end + + decode_error(str, i, "expected closing quote for string") +end + + +local function parse_number(str, i) + local x = next_char(str, i, delim_chars) + local s = str:sub(i, x - 1) + local n = tonumber(s) + if not n then + decode_error(str, i, "invalid number '" .. s .. "'") + end + return n, x +end + + +local function parse_literal(str, i) + local x = next_char(str, i, delim_chars) + local word = str:sub(i, x - 1) + if not literals[word] then + decode_error(str, i, "invalid literal '" .. word .. "'") + end + return literal_map[word], x +end + + +local function parse_array(str, i) + local res = {} + local n = 1 + i = i + 1 + while 1 do + local x + i = next_char(str, i, space_chars, true) + -- Empty / end of array? + if str:sub(i, i) == "]" then + i = i + 1 + break + end + -- Read token + x, i = parse(str, i) + res[n] = x + n = n + 1 + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "]" then break end + if chr ~= "," then decode_error(str, i, "expected ']' or ','") end + end + return res, i +end + + +local function parse_object(str, i) + local res = {} + i = i + 1 + while 1 do + local key, val + i = next_char(str, i, space_chars, true) + -- Empty / end of object? + if str:sub(i, i) == "}" then + i = i + 1 + break + end + -- Read key + if str:sub(i, i) ~= '"' then + decode_error(str, i, "expected string for key") + end + key, i = parse(str, i) + -- Read ':' delimiter + i = next_char(str, i, space_chars, true) + if str:sub(i, i) ~= ":" then + decode_error(str, i, "expected ':' after key") + end + i = next_char(str, i + 1, space_chars, true) + -- Read value + val, i = parse(str, i) + -- Set + res[key] = val + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "}" then break end + if chr ~= "," then decode_error(str, i, "expected '}' or ','") end + end + return res, i +end + + +local char_func_map = { + [ '"' ] = parse_string, + [ "0" ] = parse_number, + [ "1" ] = parse_number, + [ "2" ] = parse_number, + [ "3" ] = parse_number, + [ "4" ] = parse_number, + [ "5" ] = parse_number, + [ "6" ] = parse_number, + [ "7" ] = parse_number, + [ "8" ] = parse_number, + [ "9" ] = parse_number, + [ "-" ] = parse_number, + [ "t" ] = parse_literal, + [ "f" ] = parse_literal, + [ "n" ] = parse_literal, + [ "[" ] = parse_array, + [ "{" ] = parse_object, +} + + +parse = function(str, idx) + local chr = str:sub(idx, idx) + local f = char_func_map[chr] + if f then + return f(str, idx) + end + decode_error(str, idx, "unexpected character '" .. chr .. "'") +end + + +function json.decode(str) + if type(str) ~= "string" then + error("expected argument of type string, got " .. type(str)) + end + local res, idx = parse(str, next_char(str, 1, space_chars, true)) + idx = next_char(str, idx, space_chars, true) + if idx <= #str then + decode_error(str, idx, "trailing garbage") + end + return res +end + + +return json +)" \ No newline at end of file diff --git a/split.lua b/split.lua new file mode 100644 index 0000000..df16e86 --- /dev/null +++ b/split.lua @@ -0,0 +1,129 @@ +R"( +------------------------------------------------------------------ +-- +-- Author: Alexey Melnichuk +-- +-- Copyright (C) 2016 Alexey Melnichuk +-- +-- Licensed according to the included 'LICENSE' document +-- +-- This file is part of lua-split library. +-- +------------------------------------------------------------------ + +--- +-- @usage +-- split = require "split" +-- lines = split(str, '\r?\n') +-- key, val = split.first(str, '=', true) +-- a,b,c,d = split.unpack(str, ':', true) + +local unpack = unpack or table.unpack + +local function is_match_empty(pat, plain) + return not not string.find('', pat, nil, plain) +end + +local function split(str, sep, plain) + local b, res = 0, {} + sep = sep or '%s+' + + assert(type(sep) == 'string') + assert(type(str) == 'string') + + if #sep == 0 then + for i = 1, #str do + res[#res + 1] = string.sub(str, i, i) + end + return res + end + + assert(not is_match_empty(sep, plain), 'delimiter can not match empty string') + + while b <= #str do + local e, e2 = string.find(str, sep, b, plain) + if e then + res[#res + 1] = string.sub(str, b, e-1) + b = e2 + 1 + if b > #str then res[#res + 1] = "" end + else + res[#res + 1] = string.sub(str, b) + break + end + end + return res +end + +local function split_iter(str, sep, plain) + sep = sep or '%s+' + + assert(type(sep) == 'string') + assert(type(str) == 'string') + + if #sep == 0 then + local i = 0 + return function() + i = i + 1 + if i > #str then return end + return (string.sub(str, i, i)) + end + end + + assert(not is_match_empty(sep, plain), 'delimiter can not match empty string') + + local b, eol = 0 + return function() + if b > #str then + if eol then + eol = nil + return "" + end + return + end + + local e, e2 = string.find(str, sep, b, plain) + if e then + local s = string.sub(str, b, e-1) + b = e2 + 1 + if b > #str then eol = true end + return s + end + + local s = string.sub(str, b) + b = #str + 1 + return s + end +end + +local function usplit(...) return unpack(split(...)) end + +local function split_first(str, sep, plain) + sep = sep or '%s+' + + assert(type(sep) == 'string') + assert(type(str) == 'string') + + if #sep == 0 then + return string.sub(str, 1, 1), string.sub(str, 2) + end + + assert(not is_match_empty(sep, plain), 'delimiter can not match empty string') + + local e, e2 = string.find(str, sep, nil, plain) + if e then + return string.sub(str, 1, e - 1), string.sub(str, e2 + 1) + end + return str +end + +return setmetatable({ + split = split; + unpack = usplit; + first = split_first; + each = split_iter; +},{ + __call = function(_, ...) + return split(...) + end +}) +)" \ No newline at end of file