@@ -0,0 +1,522 @@ | |||
#include "BL3Lua.h" | |||
#include "LuaUtility.h" | |||
#include "include/patternscan.hpp" | |||
int lua_exception_handler(lua_State* L, sol::optional<const std::exception&> 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<std::streamsize>(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<std::string> 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<decltype(&my_panic), &my_panic>); | |||
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<SDK::UEngine**>((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<int> toDelete; | |||
std::vector<std::tuple<sol::reference, lua_State*, std::string, std::string, std::string>> 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<std::string> hotkeys; | |||
sol::function func; | |||
for (auto v : va) { | |||
if (v.is<std::string_view>()) { | |||
std::string hk = v.as<std::string>(); | |||
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<sol::function>()) { | |||
func = v.as<sol::function>(); | |||
} | |||
} | |||
//std::vector<std::string> hotkeys = hotkeyTable.as<std::vector<std::string>>(); | |||
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<SDK::UObject>(name); | |||
return tmp; | |||
}); | |||
lua->set_function("StaticFindObject", [](SDK::UClass* pClass, const std::wstring_view name) | |||
{ | |||
return StaticFindObject(pClass, reinterpret_cast<SDK::UObject*>(-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; | |||
} | |||
} |
@@ -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 <iostream> | |||
#include <unordered_map> | |||
#include <vector> | |||
#include <tuple> | |||
//#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 <Windows.h> | |||
#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<sol::protected_function> 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<std::string, std::vector<std::tuple<sol::reference, lua_State*, bool>>> procEventHooks; | |||
std::unordered_map<std::string, std::tuple<sol::reference, lua_State*, size_t>> consoleCommands; | |||
std::vector<std::tuple<sol::reference, lua_State*, std::string, std::string, std::string>> registeredHotkeys; | |||
std::vector<LuaComponent*> loadedComponents; | |||
private: | |||
sol::table callbacks; | |||
sol::environment consoleEnvironment; | |||
}; | |||
@@ -0,0 +1,188 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<ItemGroup Label="ProjectConfigurations"> | |||
<ProjectConfiguration Include="Debug-JIT|x64"> | |||
<Configuration>Debug-JIT</Configuration> | |||
<Platform>x64</Platform> | |||
</ProjectConfiguration> | |||
<ProjectConfiguration Include="Debug|x64"> | |||
<Configuration>Debug</Configuration> | |||
<Platform>x64</Platform> | |||
</ProjectConfiguration> | |||
<ProjectConfiguration Include="Release|x64"> | |||
<Configuration>Release</Configuration> | |||
<Platform>x64</Platform> | |||
</ProjectConfiguration> | |||
</ItemGroup> | |||
<PropertyGroup Label="Globals"> | |||
<VCProjectVersion>16.0</VCProjectVersion> | |||
<Keyword>Win32Proj</Keyword> | |||
<ProjectGuid>{df0b7199-3ed8-49dc-9544-46b6315d4390}</ProjectGuid> | |||
<RootNamespace>BL3Lua</RootNamespace> | |||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion> | |||
<ProjectName>BLua</ProjectName> | |||
</PropertyGroup> | |||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> | |||
<ConfigurationType>DynamicLibrary</ConfigurationType> | |||
<UseDebugLibraries>true</UseDebugLibraries> | |||
<PlatformToolset>ClangCL</PlatformToolset> | |||
<CharacterSet>Unicode</CharacterSet> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-JIT|x64'" Label="Configuration"> | |||
<ConfigurationType>DynamicLibrary</ConfigurationType> | |||
<UseDebugLibraries>true</UseDebugLibraries> | |||
<PlatformToolset>ClangCL</PlatformToolset> | |||
<CharacterSet>Unicode</CharacterSet> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> | |||
<ConfigurationType>DynamicLibrary</ConfigurationType> | |||
<UseDebugLibraries>false</UseDebugLibraries> | |||
<PlatformToolset>ClangCL</PlatformToolset> | |||
<WholeProgramOptimization>true</WholeProgramOptimization> | |||
<CharacterSet>Unicode</CharacterSet> | |||
</PropertyGroup> | |||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> | |||
<ImportGroup Label="ExtensionSettings"> | |||
</ImportGroup> | |||
<ImportGroup Label="Shared"> | |||
</ImportGroup> | |||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | |||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | |||
</ImportGroup> | |||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug-JIT|x64'" Label="PropertySheets"> | |||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | |||
</ImportGroup> | |||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | |||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> | |||
</ImportGroup> | |||
<PropertyGroup Label="UserMacros" /> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | |||
<LinkIncremental>true</LinkIncremental> | |||
<IncludePath>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)</IncludePath> | |||
<LibraryPath>F:\Programmieren\C++\BL3\BL3Lua\lib;$(LibraryPath)</LibraryPath> | |||
<SourcePath>$(SourcePath)</SourcePath> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-JIT|x64'"> | |||
<LinkIncremental>true</LinkIncremental> | |||
<IncludePath>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)</IncludePath> | |||
<LibraryPath>F:\Programmieren\C++\BL3\BL3Lua\lib;$(LibraryPath)</LibraryPath> | |||
<SourcePath>$(SourcePath)</SourcePath> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | |||
<LinkIncremental>false</LinkIncremental> | |||
<IncludePath>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)</IncludePath> | |||
<LibraryPath>F:\Programmieren\C++\BL3\BL3Lua\lib;$(LibraryPath)</LibraryPath> | |||
<SourcePath>$(SourcePath)</SourcePath> | |||
</PropertyGroup> | |||
<PropertyGroup Label="Vcpkg"> | |||
<VcpkgAutoLink>false</VcpkgAutoLink> | |||
</PropertyGroup> | |||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | |||
<ClCompile> | |||
<WarningLevel>Level3</WarningLevel> | |||
<SDLCheck>true</SDLCheck> | |||
<PreprocessorDefinitions>_DEBUG;BL3LUA_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
<ConformanceMode>true</ConformanceMode> | |||
<PrecompiledHeader>NotUsing</PrecompiledHeader> | |||
<PrecompiledHeaderFile> | |||
</PrecompiledHeaderFile> | |||
<LanguageStandard>stdcpp17</LanguageStandard> | |||
<LanguageStandard_C>stdc17</LanguageStandard_C> | |||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions> | |||
<OmitFramePointers>true</OmitFramePointers> | |||
<BufferSecurityCheck>false</BufferSecurityCheck> | |||
<ExceptionHandling>Async</ExceptionHandling> | |||
<RuntimeTypeInfo>false</RuntimeTypeInfo> | |||
</ClCompile> | |||
<Link> | |||
<SubSystem>Windows</SubSystem> | |||
<GenerateDebugInformation>true</GenerateDebugInformation> | |||
<EnableUAC>false</EnableUAC> | |||
<AdditionalDependencies>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)</AdditionalDependencies> | |||
</Link> | |||
</ItemDefinitionGroup> | |||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug-JIT|x64'"> | |||
<ClCompile> | |||
<WarningLevel>Level3</WarningLevel> | |||
<SDLCheck>true</SDLCheck> | |||
<PreprocessorDefinitions>_DEBUG;BL3LUA_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
<ConformanceMode>true</ConformanceMode> | |||
<PrecompiledHeader>NotUsing</PrecompiledHeader> | |||
<PrecompiledHeaderFile> | |||
</PrecompiledHeaderFile> | |||
<LanguageStandard>stdcpp17</LanguageStandard> | |||
<LanguageStandard_C>stdc17</LanguageStandard_C> | |||
<AdditionalOptions>/bigobj %(AdditionalOptions)</AdditionalOptions> | |||
<OmitFramePointers>true</OmitFramePointers> | |||
<BufferSecurityCheck>false</BufferSecurityCheck> | |||
<ExceptionHandling>Async</ExceptionHandling> | |||
<RuntimeTypeInfo>false</RuntimeTypeInfo> | |||
</ClCompile> | |||
<Link> | |||
<SubSystem>Windows</SubSystem> | |||
<GenerateDebugInformation>true</GenerateDebugInformation> | |||
<EnableUAC>false</EnableUAC> | |||
<AdditionalDependencies>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)</AdditionalDependencies> | |||
</Link> | |||
</ItemDefinitionGroup> | |||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | |||
<ClCompile> | |||
<WarningLevel>Level3</WarningLevel> | |||
<FunctionLevelLinking>true</FunctionLevelLinking> | |||
<IntrinsicFunctions>true</IntrinsicFunctions> | |||
<SDLCheck>true</SDLCheck> | |||
<PreprocessorDefinitions>NDEBUG;BL3LUA_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
<ConformanceMode>true</ConformanceMode> | |||
<PrecompiledHeader>NotUsing</PrecompiledHeader> | |||
<PrecompiledHeaderFile> | |||
</PrecompiledHeaderFile> | |||
<LanguageStandard>stdcpp17</LanguageStandard> | |||
<LanguageStandard_C>stdc17</LanguageStandard_C> | |||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed> | |||
<OmitFramePointers>true</OmitFramePointers> | |||
<ExceptionHandling>Async</ExceptionHandling> | |||
<BufferSecurityCheck>false</BufferSecurityCheck> | |||
<AdditionalOptions>-m64 /GR- %(AdditionalOptions)</AdditionalOptions> | |||
<RuntimeTypeInfo>false</RuntimeTypeInfo> | |||
</ClCompile> | |||
<Link> | |||
<SubSystem>Windows</SubSystem> | |||
<EnableCOMDATFolding>true</EnableCOMDATFolding> | |||
<OptimizeReferences>true</OptimizeReferences> | |||
<GenerateDebugInformation>true</GenerateDebugInformation> | |||
<EnableUAC>false</EnableUAC> | |||
<AdditionalDependencies>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)</AdditionalDependencies> | |||
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration> | |||
</Link> | |||
</ItemDefinitionGroup> | |||
<ItemGroup> | |||
<ClCompile Include="BL3Lua.cpp" /> | |||
<ClCompile Include="BL3LuaDLL.cpp" /> | |||
<ClCompile Include="C:\SDK_GEN\BL3-2021-4-9\SDK\BL3_Basic.cpp" /> | |||
<ClCompile Include="C:\SDK_GEN\BL3-2021-4-9\SDK\BL3_CoreUObject_functions.cpp" /> | |||
<ClCompile Include="C:\SDK_GEN\BL3-2021-4-9\SDK\BL3_Engine_functions.cpp" /> | |||
<ClCompile Include="C:\SDK_GEN\BL3-2021-4-9\SDK\BL3_GbxUI_functions.cpp" /> | |||
<ClCompile Include="C:\SDK_GEN\BL3-2021-4-9\SDK\BL3_OakGame_functions.cpp" /> | |||
<ClCompile Include="C:\SDK_GEN\BL3-2021-4-9\SDK\BL3_ScaleformUI_functions.cpp" /> | |||
<ClCompile Include="C:\SDK_GEN\BL3-2021-4-9\SDK\BL3_UMG_functions.cpp" /> | |||
<ClCompile Include="LuaComponent.cpp" /> | |||
<ClCompile Include="LuaUtility.cpp" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ClInclude Include="BitFields.hpp" /> | |||
<ClInclude Include="BL3Lua.h" /> | |||
<ClInclude Include="Helper.hpp" /> | |||
<ClInclude Include="include\LuaComponent.h" /> | |||
<ClInclude Include="include\Patterns.h" /> | |||
<ClInclude Include="include\Utility.hpp" /> | |||
<ClInclude Include="LuaUtility.h" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Include="include\json.lua" /> | |||
<None Include="split.lua" /> | |||
</ItemGroup> | |||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> | |||
<ImportGroup Label="ExtensionTargets"> | |||
</ImportGroup> | |||
</Project> |
@@ -0,0 +1,86 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<ItemGroup> | |||
<Filter Include="Source Files"> | |||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> | |||
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions> | |||
</Filter> | |||
<Filter Include="Header Files"> | |||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> | |||
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions> | |||
</Filter> | |||
<Filter Include="Resource Files"> | |||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> | |||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> | |||
</Filter> | |||
<Filter Include="SDK-CPP"> | |||
<UniqueIdentifier>{f40848c6-786f-425a-aeb2-8f803968ded9}</UniqueIdentifier> | |||
</Filter> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ClCompile Include="BL3LuaDLL.cpp"> | |||
<Filter>Source Files</Filter> | |||
</ClCompile> | |||
<ClCompile Include="BL3Lua.cpp"> | |||
<Filter>Source Files</Filter> | |||
</ClCompile> | |||
<ClCompile Include="LuaUtility.cpp"> | |||
<Filter>Source Files</Filter> | |||
</ClCompile> | |||
<ClCompile Include="LuaComponent.cpp"> | |||
<Filter>Source Files</Filter> | |||
</ClCompile> | |||
<ClCompile Include="C:\SDK_GEN\BL3-2021-4-9\SDK\BL3_CoreUObject_functions.cpp"> | |||
<Filter>SDK-CPP</Filter> | |||
</ClCompile> | |||
<ClCompile Include="C:\SDK_GEN\BL3-2021-4-9\SDK\BL3_Basic.cpp"> | |||
<Filter>SDK-CPP</Filter> | |||
</ClCompile> | |||
<ClCompile Include="C:\SDK_GEN\BL3-2021-4-9\SDK\BL3_OakGame_functions.cpp"> | |||
<Filter>SDK-CPP</Filter> | |||
</ClCompile> | |||
<ClCompile Include="C:\SDK_GEN\BL3-2021-4-9\SDK\BL3_Engine_functions.cpp"> | |||
<Filter>SDK-CPP</Filter> | |||
</ClCompile> | |||
<ClCompile Include="C:\SDK_GEN\BL3-2021-4-9\SDK\BL3_GbxUI_functions.cpp"> | |||
<Filter>SDK-CPP</Filter> | |||
</ClCompile> | |||
<ClCompile Include="C:\SDK_GEN\BL3-2021-4-9\SDK\BL3_ScaleformUI_functions.cpp"> | |||
<Filter>SDK-CPP</Filter> | |||
</ClCompile> | |||
<ClCompile Include="C:\SDK_GEN\BL3-2021-4-9\SDK\BL3_UMG_functions.cpp"> | |||
<Filter>SDK-CPP</Filter> | |||
</ClCompile> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<ClInclude Include="BitFields.hpp"> | |||
<Filter>Header Files</Filter> | |||
</ClInclude> | |||
<ClInclude Include="BL3Lua.h"> | |||
<Filter>Header Files</Filter> | |||
</ClInclude> | |||
<ClInclude Include="Helper.hpp"> | |||
<Filter>Header Files</Filter> | |||
</ClInclude> | |||
<ClInclude Include="LuaUtility.h"> | |||
<Filter>Header Files</Filter> | |||
</ClInclude> | |||
<ClInclude Include="include\LuaComponent.h"> | |||
<Filter>Header Files</Filter> | |||
</ClInclude> | |||
<ClInclude Include="include\Patterns.h"> | |||
<Filter>Header Files</Filter> | |||
</ClInclude> | |||
<ClInclude Include="include\Utility.hpp"> | |||
<Filter>Header Files</Filter> | |||
</ClInclude> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Include="include\json.lua"> | |||
<Filter>Resource Files</Filter> | |||
</None> | |||
<None Include="split.lua"> | |||
<Filter>Resource Files</Filter> | |||
</None> | |||
</ItemGroup> | |||
</Project> |
@@ -0,0 +1,18 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> | |||
<LocalDebuggerCommand>E:\SteamLibrary\steamapps\common\Borderlands 3\OakGame\Binaries\Win64\Borderlands3.exe</LocalDebuggerCommand> | |||
<LocalDebuggerAttach>true</LocalDebuggerAttach> | |||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug-JIT|x64'"> | |||
<LocalDebuggerCommand>E:\SteamLibrary\steamapps\common\Borderlands 3\OakGame\Binaries\Win64\Borderlands3.exe</LocalDebuggerCommand> | |||
<LocalDebuggerAttach>true</LocalDebuggerAttach> | |||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor> | |||
</PropertyGroup> | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | |||
<LocalDebuggerCommand>E:\SteamLibrary\steamapps\common\Borderlands 3\OakGame\Binaries\Win64\Borderlands3.exe</LocalDebuggerCommand> | |||
<LocalDebuggerAttach>true</LocalDebuggerAttach> | |||
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor> | |||
</PropertyGroup> | |||
</Project> |
@@ -0,0 +1,88 @@ | |||
#pragma once | |||
#include <cstddef> | |||
#include <cstdint> | |||
#include <climits> | |||
#include <type_traits> | |||
// 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 <std::size_t sz, typename C = void> | |||
struct bit_type { | |||
typedef uint64_t type; | |||
}; | |||
template <std::size_t sz> | |||
struct bit_type<sz, std::enable_if_t<(sz <= 1)>> { | |||
typedef bool type; | |||
}; | |||
template <std::size_t sz> | |||
struct bit_type<sz, std::enable_if_t<(sz > 2 && sz <= 16)>> { | |||
typedef uint16_t type; | |||
}; | |||
template <std::size_t sz> | |||
struct bit_type<sz, std::enable_if_t<(sz > 16 && sz <= 32)>> { | |||
typedef uint32_t type; | |||
}; | |||
template <std::size_t sz> | |||
struct bit_type<sz, std::enable_if_t<(sz > 32 && sz <= 64)>> { | |||
typedef uint64_t type; | |||
}; | |||
template <std::size_t sz> | |||
using bit_type_t = typename bit_type<sz>::type; | |||
template <typename T, typename V> | |||
bool vcxx_warning_crap(std::true_type, V val) { | |||
return val != 0; | |||
} | |||
template <typename T, typename V> | |||
T vcxx_warning_crap(std::false_type, V val) { | |||
return static_cast<T>(val); | |||
} | |||
template <typename T, typename V> | |||
auto vcxx_warning_crap(V val) { | |||
return vcxx_warning_crap<T>(std::is_same<bool, T>(), val); | |||
} | |||
template <typename Base, std::size_t baseOffset = 0x0, std::size_t bit_target = 0x0, std::size_t size = 0x1> | |||
void write(Base& b, bit_type_t<size> bits) { | |||
uintptr_t baseAddr = reinterpret_cast<uintptr_t>(&b); | |||
uintptr_t offs = baseOffset; | |||
unsigned char* byt = reinterpret_cast<unsigned char*>(baseAddr + offs); | |||
char testb = *byt & (1 << bit_target); | |||
if (bits) | |||
BIT_SET(*byt, bit_target); | |||
else | |||
BIT_CLEAR(*byt, bit_target); | |||
} | |||
template <typename Base, std::size_t baseOffset = 0x0, std::size_t bit_target = 0x0, std::size_t size = 0x1> | |||
bit_type_t<size> read(Base& b) { | |||
uintptr_t baseAddr = reinterpret_cast<uintptr_t>(&b); | |||
uintptr_t offs = baseOffset; | |||
unsigned char* byt = reinterpret_cast<unsigned char*>(baseAddr + offs); | |||
char test = *byt & (1 << bit_target); | |||
if (test) | |||
return true; | |||
return false; | |||
} | |||
} |
@@ -0,0 +1,78 @@ | |||
#pragma once | |||
#include <windows.h> | |||
/* | |||
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; | |||
} |
@@ -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>(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(); | |||
} |
@@ -0,0 +1,151 @@ | |||
#include "LuaUtility.h" | |||
#include <windows.h> | |||
#include <WinUser.h> | |||
#include <string> | |||
static std::unordered_map<std::string, uint8_t> 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<uint8_t> 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<uint8_t> 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<std::string, uint8_t> LuaUtility::GetKeyMap() | |||
{ | |||
if (!keyMap.size()) { | |||
keyMap.insert(std::pair<std::string, uint8_t>("backspace", VK_BACK)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("tab", VK_TAB)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("clear", VK_CLEAR)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("return", VK_RETURN)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("enter", VK_RETURN)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("shift", VK_SHIFT)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("ctrl", VK_CONTROL)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("alt", VK_MENU)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("pause", VK_PAUSE)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("capslock", VK_CAPITAL)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("escape", VK_ESCAPE)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("space", VK_SPACE)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("pageup", VK_PRIOR)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("pagedown", VK_NEXT)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("end", VK_END)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("home", VK_HOME)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("left", VK_LEFT)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("up", VK_UP)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("right", VK_RIGHT)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("down", VK_DOWN)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("printscr", VK_SNAPSHOT)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("insert", VK_INSERT)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("delete", VK_DELETE)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("f1", VK_F1)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("f2", VK_F2)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("f3", VK_F3)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("f4", VK_F4)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("f5", VK_F5)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("f6", VK_F6)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("f7", VK_F7)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("f8", VK_F8)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("f9", VK_F9)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("f10", VK_F10)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("f11", VK_F11)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("f12", VK_F12)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("numpad0", VK_NUMPAD0)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("numpad1", VK_NUMPAD1)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("numpad2", VK_NUMPAD2)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("numpad3", VK_NUMPAD3)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("numpad4", VK_NUMPAD4)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("numpad5", VK_NUMPAD5)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("numpad6", VK_NUMPAD6)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("numpad7", VK_NUMPAD7)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("numpad8", VK_NUMPAD8)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("numpad9", VK_NUMPAD9)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("multiply", VK_MULTIPLY)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("add", VK_ADD)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("subtract", VK_SUBTRACT)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("decimal", VK_DECIMAL)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("divide", VK_DIVIDE)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("numlock", VK_NUMLOCK)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("0", 0x30)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("1", 0x31)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("2", 0x32)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("3", 0x33)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("4", 0x34)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("5", 0x35)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("6", 0x36)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("7", 0x37)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("8", 0x38)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("9", 0x39)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("scrollock", VK_SCROLL)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("lshift", VK_LSHIFT)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("rshift", VK_RSHIFT)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("lctrl", VK_LCONTROL)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("rctrl", VK_RCONTROL)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("lalt", VK_LMENU)); | |||
keyMap.insert(std::pair<std::string, uint8_t>("ralt", VK_RMENU)); | |||
} | |||
return keyMap; | |||
} |
@@ -0,0 +1,18 @@ | |||
#pragma once | |||
#include <algorithm> | |||
#include <vector> | |||
#include <unordered_map> | |||
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<std::string, uint8_t> GetKeyMap(); | |||
}; |
@@ -0,0 +1,21 @@ | |||
#pragma once | |||
#include "F:/Programmieren/Lua/sol2/single/single/include/sol/sol.hpp" | |||
#include <iostream> | |||
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; | |||
}; | |||
@@ -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"); |
@@ -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; | |||
} |
@@ -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 | |||
)" |
@@ -0,0 +1,129 @@ | |||
R"( | |||
------------------------------------------------------------------ | |||
-- | |||
-- Author: Alexey Melnichuk <alexeymelnichuck@gmail.com> | |||
-- | |||
-- Copyright (C) 2016 Alexey Melnichuk <alexeymelnichuck@gmail.com> | |||
-- | |||
-- 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 | |||
}) | |||
)" |