commit 892d38ad4f7743565020316741387a5c604cf16a Author: c0dycode Date: Sat Jul 31 19:26:32 2021 +0200 Initial push. Still data missing 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