diff --git a/LICENSE b/LICENSE index aae4ee8..d05522f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 HardCoded +Copyright (c) 2021 HardCoded and TechnologicNick Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/PluginDevFolder/SMLuaHook/SMLuaHook.vcxproj b/PluginDevFolder/SMLuaHook/SMLuaHook.vcxproj new file mode 100644 index 0000000..08df75f --- /dev/null +++ b/PluginDevFolder/SMLuaHook/SMLuaHook.vcxproj @@ -0,0 +1,124 @@ + + + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + 16.0 + Win32Proj + {93661f1d-21dc-4b5b-88ec-6bea337df56b} + SMLuaHook + 10.0 + + + + DynamicLibrary + true + v142 + Unicode + + + DynamicLibrary + false + v142 + true + Unicode + + + + + + + + + + + + + + + true + $(SolutionDir)..\SMLibrary\include;$(IncludePath) + $(OutDir);$(LibraryPath) + + + false + $(SolutionDir)..\SMLibrary\include;$(IncludePath) + $(OutDir);$(LibraryPath) + + + + Level3 + true + _DEBUG;SMLUAHOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + stdcpp17 + + + Windows + true + false + smlibrary.lib + + + if not exist "$(OutputPath)plugins\" mkdir "$(OutputPath)plugins\" +copy /B "$(LocalDebuggerCommand)" "$(OutputPath)plugins\$(TargetFileName)" + + + + + Level3 + true + true + true + NDEBUG;SMLUAHOOK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + stdcpp17 + + + Windows + true + true + true + false + smlibrary.lib + + + if not exist "$(OutputPath)plugins\" mkdir "$(OutputPath)plugins\" +copy /B "$(LocalDebuggerCommand)" "$(OutputPath)plugins\$(TargetFileName)" + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/PluginDevFolder/SMLuaHook/SMLuaHook.vcxproj.filters b/PluginDevFolder/SMLuaHook/SMLuaHook.vcxproj.filters new file mode 100644 index 0000000..127eef9 --- /dev/null +++ b/PluginDevFolder/SMLuaHook/SMLuaHook.vcxproj.filters @@ -0,0 +1,33 @@ + + + + + {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 + + + + + Source Files + + + + + Header Files + + + Header Files + + + + + + \ No newline at end of file diff --git a/PluginDevFolder/SMLuaHook/hooks.h b/PluginDevFolder/SMLuaHook/hooks.h new file mode 100644 index 0000000..c01b763 --- /dev/null +++ b/PluginDevFolder/SMLuaHook/hooks.h @@ -0,0 +1,86 @@ +#pragma once +#include +#include +#include +#include + +#include +using Console::Color; + +#include "lua_hook_config.h" + + +// LUAL_REGISTER +typedef void (*pluaL_register)(lua_State*, const char*, const luaL_Reg*); +GameHook* hck_luaL_register; + +// LUAL_LOADSTRING +typedef int (*pluaL_loadstring)(lua_State*, const char*); +GameHook* hck_luaL_loadstring; + +// LUA_NEWSTATE +typedef lua_State* (*plua_newstate)(lua_Alloc, void*); +GameHook* hck_lua_newstate; + +// LUAL_LOADBUFFER +typedef int (*pluaL_loadbuffer)(lua_State*, const char*, size_t, const char*); +GameHook* hck_luaL_loadbuffer; + +// ============= + +namespace LuaHook::Hooks { + void hook_luaL_register(lua_State* L, const char* libname, const luaL_Reg* l) { + Console::log(Color::Aqua, "hook_luaL_register: libname=[%s]", libname); + + const luaL_Reg* ptr = l; + + int i = 0; + while (ptr->name != NULL) { + Console::log(Color::Aqua, "hook_luaL_register: luaL_Reg[%d] name=[%s] func=[%p]", i++, ptr->name, (void*)ptr->func); + + ptr++; + } + + return ((pluaL_register)*hck_luaL_register)(L, libname, l); + } + + int hook_luaL_loadstring(lua_State* L, const char* s) { + Console::log(Color::Aqua, "hook_luaL_loadstring: s=[ ... ]"); + + std::map fields = { + {"s", &s} + }; + + std::string input(s); + + if (LuaHook::runLuaHook("luaL_loadstring", &input, fields)) { + Console::log(Color::Green, "Set contents to:\n%s", input.c_str()); + return ((pluaL_loadstring)*hck_luaL_loadstring)(L, input.c_str()); + } + + return ((pluaL_loadstring)*hck_luaL_loadstring)(L, s); + } + + lua_State* hook_lua_newstate(lua_Alloc f, void* ud) { + Console::log(Color::Aqua, "hck_lua_newstate: ud=[%p]", ud); + return ((plua_newstate)*hck_lua_newstate)(f, ud); + } + + int hook_luaL_loadbuffer(lua_State* L, const char* buff, size_t sz, const char* name) { + Console::log(Color::Aqua, "hck_luaL_loadbuffer: buff=[ ... ], sz=[%zu], name=[%s]", sz, name); + + std::map fields = { + {"buff", &buff}, + {"name", &name} + }; + + std::string input(buff, sz); + + if (size_t executeCount = LuaHook::runLuaHook("luaL_loadbuffer", &input, fields)) { + Console::log(Color::Green, "Set contents to:\n%s", input.c_str()); + return ((pluaL_loadbuffer)*hck_luaL_loadbuffer)(L, input.c_str(), input.size(), name); + } + + return ((pluaL_loadbuffer)*hck_luaL_loadbuffer)(L, buff, sz, name); + } +} diff --git a/PluginDevFolder/SMLuaHook/lua_hook_config.h b/PluginDevFolder/SMLuaHook/lua_hook_config.h new file mode 100644 index 0000000..0b78206 --- /dev/null +++ b/PluginDevFolder/SMLuaHook/lua_hook_config.h @@ -0,0 +1,330 @@ +#pragma once +#pragma warning(push) +#pragma warning(disable : 26819 28020) +#define JSON_DIAGNOSTICS 1 +#include +#pragma warning(pop) +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +using json = nlohmann::json; + +namespace LuaHook { + + struct selector; + struct executor; + struct hookItem; + + typedef std::function& fields, hookItem& hookItem, selector& selector)> selectorFunction; + typedef std::function& fields, hookItem& hookItem, executor& executor)> executorFunction; + + struct selector { + selectorFunction* func; + + json j_selector; + }; + + struct executor { + executorFunction* func; + + json j_executor; + }; + + struct hookItem { + float priority = 1; + std::vector selector; + std::vector execute; + + json j_hook; + + bool operator<(const hookItem& other) const { + return this->priority < other.priority; + } + }; + + namespace SelectorHelper { + + std::any getInput(std::map& fields, hookItem& hookItem, selector& selector) { + if (selector.j_selector.contains("field")) { + std::string fieldName = selector.j_selector.at("field"); + + if (fields.find(fieldName) == fields.end()) { + throw new std::invalid_argument("Field not found"); + } + + return fields[fieldName]; + } + else { + if (fields.size() == 1) { + return fields.begin()->second; + } + else { + // Throw an exception + selector.j_selector.at("field"); + return NULL; + } + } + } + + std::string getInputConstCharPtr(std::map& fields, hookItem& hookItem, selector& selector) { + std::any input = SelectorHelper::getInput(fields, hookItem, selector); + + if (const char*** str = std::any_cast(&input)) { + return std::string(**str); + } + else { + throw new std::invalid_argument(std::string("Field type not castable to const char**: ") + input.type().name()); + } + } + + } + + std::map selectorFunctions = { + { + "CONTAINS", [](std::map& fields, hookItem& hookItem, selector& selector) { + std::string input = SelectorHelper::getInputConstCharPtr(fields, hookItem, selector); + + std::string value = selector.j_selector.at("value"); + + return input.find(value) != std::string::npos; + } + }, + { + "EQUALS", [](std::map& fields, hookItem& hookItem, selector& selector) { + std::string input = SelectorHelper::getInputConstCharPtr(fields, hookItem, selector); + + std::string value = selector.j_selector.at("value"); + + return input.compare(value) == 0; + } + } + }; + + + + namespace ExecutorHelper { + + std::string readFile(fs::path path) { + std::ifstream in(path); + + if (in.fail()) { +#pragma warning(suppress : 4996) + throw std::exception((std::string() + "Failed to open file \"" + path.string() + "\": " + strerror(errno)).c_str()); + } + + std::ostringstream sstr; + sstr << in.rdbuf(); + return sstr.str(); + } + + } + + std::map executorFunctions = { + { + "REPLACE_CONTENT_WITH_FILE", [](std::string* input, std::map& fields, hookItem& hookItem, executor& executor) { + return input->assign(LuaHook::ExecutorHelper::readFile(PathHelper::resolvePath(executor.j_executor.at("file")))); + } + }, + { + "PREPEND_FILE", [](std::string* input, std::map& fields, hookItem& hookItem, executor& executor) { + return input->insert(0, LuaHook::ExecutorHelper::readFile(PathHelper::resolvePath(executor.j_executor.at("file"))).append("\n")); + } + }, + { + "APPEND_FILE", [](std::string* input, std::map& fields, hookItem& hookItem, executor& executor) { + return input->append("\n").append(LuaHook::ExecutorHelper::readFile(PathHelper::resolvePath(executor.j_executor.at("file")))); + } + }, + { + "PREPEND_STRING", [](std::string* input, std::map& fields, hookItem& hookItem, executor& executor) { + return input->insert(0, executor.j_executor.at("string").get().append("\n")); + } + }, + { + "APPEND_STRING", [](std::string* input, std::map& fields, hookItem& hookItem, executor& executor) { + return input->append("\n").append(executor.j_executor.at("string")); + } + } + }; + + + + void from_json(const json& j, selector& s) { + std::string op = j.at("operator").get(); + + if (selectorFunctions.find(op) == selectorFunctions.end()) { + std::vector opList; + for (auto const& opFunc : selectorFunctions) { + opList.push_back(opFunc.first); + } + + throw std::exception((std::string() + "Unknown operator \"" + op + "\". Possible operators: " + json(opList).dump()).c_str()); + } + + s.func = &selectorFunctions.at(op); + + j.get_to(s.j_selector); + } + + void from_json(const json& j, executor& e) { + std::string command = j.at("command").get(); + + if (executorFunctions.find(command) == executorFunctions.end()) { + std::vector commandList; + for (auto const& cmdFunc : executorFunctions) { + commandList.push_back(cmdFunc.first); + } + + throw std::exception((std::string() + "Unknown command \"" + command + "\". Possible commands: " + json(commandList).dump()).c_str()); + } + + e.func = &executorFunctions.at(command); + + j.get_to(e.j_executor); + } + + void from_json(const json& j, hookItem& hi) { + j.at("priority").get_to(hi.priority); + j.at("selector").get_to(hi.selector); + j.at("execute").get_to(hi.execute); + + j.get_to(hi.j_hook); + } + + + + class HookConfig { + public: + inline static std::vector enabledConfigs; + + private: + json root; + std::map> hooks; + bool enabled = false; + + public: + HookConfig(json root) { + this->root = root; + + for (auto& [key, value] : this->root.at("hooks").items()) { + try { + this->hooks[key] = value.get>(); + } + catch (json::exception e) { + Console::log(Color::LightRed, "Failed parsing %s hooks: %s", key.c_str(), e.what()); + } + catch (std::exception e) { + Console::log(Color::LightRed, "Failed processing %s hooks: %s", key.c_str(), e.what()); + } + } + } + + ~HookConfig() { + if (this->enabled) { + Console::log(Color::LightYellow, "Warning: Deconstructing HookConfig while enabled!"); + } + } + + void setEnabled(bool enabled) { + if (enabled == this->enabled) { + return; + } + + if (enabled) { + // Add this + HookConfig::enabledConfigs.push_back(this); + } + else { + // Remove this + HookConfig::enabledConfigs.erase(std::remove(HookConfig::enabledConfigs.begin(), HookConfig::enabledConfigs.end(), this), HookConfig::enabledConfigs.end()); + } + } + + static std::vector getHookItems(std::string name) { + size_t totalSize = 0; + + // Calculate the total size + for (auto& hookConfig : HookConfig::enabledConfigs) { + if (hookConfig->hooks.find(name) == hookConfig->hooks.end()) + continue; + + totalSize += hookConfig->hooks[name].size(); + } + + std::vector items; + items.reserve(totalSize); + + // Insert all hookItems into items + for (auto& hookConfig : HookConfig::enabledConfigs) { + if (hookConfig->hooks.find(name) == hookConfig->hooks.end()) + continue; + + items.insert(items.end(), hookConfig->hooks[name].begin(), hookConfig->hooks[name].end()); + } + + std::sort(items.begin(), items.end()); + + return items; + } + + static std::set getHookNames() { + std::set names; + + // Insert all hookItems into items + for (auto& hookConfig : HookConfig::enabledConfigs) { + for (auto& [hookName, hookItems] : hookConfig->hooks) { + names.insert(hookName); + } + } + + return names; + } + + }; + + + + size_t runLuaHook(std::string name, std::string* input, std::map& fields) { + size_t executeCount = 0; + + for (hookItem& hookItem : HookConfig::getHookItems(name)) { + bool selected = true; + + for (selector& selector : hookItem.selector) { + selected &= (*selector.func)(fields, hookItem, selector); + + Console::log(selected ? Color::LightPurple : Color::Purple, selected ? "selected" : "not selected"); + + if (!selected) { + break; + } + } + + if (selected) { + + for (executor& executor : hookItem.execute) { + try { + (*executor.func)(input, fields, hookItem, executor); + + executeCount++; + } + catch (std::exception& e) { + Console::log(Color::LightRed, "Failed executing executor %s: %s", executor.j_executor.dump(4).c_str(), e.what()); + } + } + + } + } + + return executeCount; + } + +} \ No newline at end of file diff --git a/PluginDevFolder/SMLuaHook/main.cpp b/PluginDevFolder/SMLuaHook/main.cpp new file mode 100644 index 0000000..d855217 --- /dev/null +++ b/PluginDevFolder/SMLuaHook/main.cpp @@ -0,0 +1,145 @@ +#define _SM_LIBRARY_BUILD_PLUGIN +#define _SM_PLUGIN_NAME SMLuaHook + +#include +#include +#include +#include + +using Console::Color; + +#include "hooks.h" +#include "lua_hook_config.h" +bool InjectLua(); + +LIB_RESULT PluginLoad() { + Console::log(Color::Aqua, "Starting plugin ..."); + + if (!InjectLua()) { + Console::log(Color::Red, "Failed to inject lua functions"); + return PLUGIN_ERROR; + } + + return PLUGIN_SUCCESSFULL; +} + +const char* defaultConfig = R"(// Configuration file for hooking the Lua C API +{ + "hooks": { + + // This function is used for setting up the Lua environment + "luaL_loadstring": [ + /* + { + // Replace the environment table (the functions accessible inside + // the lua sandbox) with an environment table loaded from a file + + // Hooks are ran from high to low priority + "priority": 1, + + // All selectors must result in true for commands to be ran + "selector": [ + { + // The "name" field can be omitted if the hooked function has + // at most one string field (check the (Lua/Luajit) source code) + + // "name": "s", + "operator": "CONTAINS", + "value": "unsafe_env" + } + ], + + "execute": [ + { + "command": "REPLACE_CONTENT_WITH_FILE", + "file": "$PLUGIN_CONFIG/override/unsafe_env.lua" + } + ] + } + */ + ], + + // All files the game tries to load pass through here + "luaL_loadbuffer": [ + /* + { + // Enable survival dev mode + + "priority": 1, + "selector": [ + { + "field": "name", + "operator": "EQUALS", + "value": "Survival/Scripts/game/SurvivalGame.lua" + } + ], + "execute": [ + { + "command": "PREPEND_STRING", + "string": "g_survivalDev = true" + } + ] + } + */ + ] + + } +} +)"; + +bool InjectLua() { + Console::log(Color::Aqua, "Installing lua hooks"); + + hck_luaL_register = GameHooks::InjectFromName("lua51.dll", "luaL_register", &LuaHook::Hooks::hook_luaL_register, 15); + hck_luaL_loadstring = GameHooks::InjectFromName("lua51.dll", "luaL_loadstring", &LuaHook::Hooks::hook_luaL_loadstring, 16); + hck_lua_newstate = GameHooks::InjectFromName("lua51.dll", "lua_newstate", &LuaHook::Hooks::hook_lua_newstate, 6); + hck_luaL_loadbuffer = GameHooks::InjectFromName("lua51.dll", "luaL_loadbuffer", &LuaHook::Hooks::hook_luaL_loadbuffer, 13); + + if (!hck_luaL_register) { + Console::log(Color::Red, "Failed to inject 'luaL_register'"); + return false; + } + + if (!hck_luaL_loadstring) { + Console::log(Color::Red, "Failed to inject 'luaL_loadstring'"); + return false; + } + + if (!hck_lua_newstate) { + Console::log(Color::Red, "Failed to inject 'lua_newstate'"); + return false; + } + + if (!hck_luaL_loadbuffer) { + Console::log(Color::Red, "Failed to inject 'luaL_loadbuffer'"); + return false; + } + + PluginConfig config(_LIB_PLUGIN_NAME_STR, "lua_hooks.json"); + config.setDefaultContent(defaultConfig); + config.createIfNotExists(); + config.load(); + + try { + Console::log(Color::Aqua, "Loading Lua C API hook config files..."); + LuaHook::HookConfig *hookConfig = new LuaHook::HookConfig(config.root); + hookConfig->setEnabled(true); + } + catch (const json::exception& e) { + Console::log(Color::LightRed, "Failed initialising HookConfig: %s", e.what()); + } + + Console::log(Color::Aqua, "Loaded %llu Lua C API hook config files", LuaHook::HookConfig::enabledConfigs.size()); + for (auto& hookName : LuaHook::HookConfig::getHookNames()) { + Console::log(Color::Aqua, " %s (%llu)", hookName.c_str(), LuaHook::HookConfig::getHookItems(hookName).size()); + } + + + return true; +} + +LIB_RESULT PluginUnload() { + Console::log(Color::Aqua, "Unloading this plugin!"); + //util->Unload(); + return PLUGIN_SUCCESSFULL; +} diff --git a/PluginDevFolder/SMLuaHook/packages.config b/PluginDevFolder/SMLuaHook/packages.config new file mode 100644 index 0000000..a51867b --- /dev/null +++ b/PluginDevFolder/SMLuaHook/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/PluginDevFolder/SMLuaReaderPlugin/SMLuaReaderPlugin.vcxproj b/PluginDevFolder/SMLuaReaderPlugin/SMLuaReaderPlugin.vcxproj index c86cf1c..ef294b4 100644 --- a/PluginDevFolder/SMLuaReaderPlugin/SMLuaReaderPlugin.vcxproj +++ b/PluginDevFolder/SMLuaReaderPlugin/SMLuaReaderPlugin.vcxproj @@ -15,7 +15,9 @@ - + + + 16.0 @@ -69,6 +71,7 @@ NotUsing + stdcpp17 Windows @@ -96,6 +99,7 @@ copy /B "$(LocalDebuggerCommand)" "$(OutputPath)plugins\$(TargetFileName)"NotUsing + stdcpp17 Windows @@ -116,5 +120,12 @@ copy /B "$(LocalDebuggerCommand)" "$(OutputPath)plugins\$(TargetFileName)" + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + \ No newline at end of file diff --git a/PluginDevFolder/SMLuaReaderPlugin/SMLuaReaderPlugin.vcxproj.filters b/PluginDevFolder/SMLuaReaderPlugin/SMLuaReaderPlugin.vcxproj.filters index abc8bae..f362751 100644 --- a/PluginDevFolder/SMLuaReaderPlugin/SMLuaReaderPlugin.vcxproj.filters +++ b/PluginDevFolder/SMLuaReaderPlugin/SMLuaReaderPlugin.vcxproj.filters @@ -17,8 +17,8 @@ Header Files - - Header Files - + + + \ No newline at end of file diff --git a/PluginDevFolder/SMLuaReaderPlugin/packages.config b/PluginDevFolder/SMLuaReaderPlugin/packages.config new file mode 100644 index 0000000..8226e66 --- /dev/null +++ b/PluginDevFolder/SMLuaReaderPlugin/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/PluginDevFolder/SMLuaReaderPlugin/src/hooks.h b/PluginDevFolder/SMLuaReaderPlugin/src/hooks.h index 6681f9e..916561b 100644 --- a/PluginDevFolder/SMLuaReaderPlugin/src/hooks.h +++ b/PluginDevFolder/SMLuaReaderPlugin/src/hooks.h @@ -1,6 +1,7 @@ +#pragma once #include #include -#include "lua.h" +#include #include using Console::Color; @@ -12,35 +13,42 @@ GameHook* hck_luaL_register; // LUAL_LOADSTRING typedef int (*pluaL_loadstring)(lua_State*, const char*); -GameHook *hck_luaL_loadstring; +GameHook* hck_luaL_loadstring; // LUA_NEWSTATE -typedef lua_State *(*plua_newstate)(lua_Alloc, void*); -GameHook *hck_lua_newstate; +typedef lua_State* (*plua_newstate)(lua_Alloc, void*); +GameHook* hck_lua_newstate; // LUAL_LOADBUFFER typedef int (*pluaL_loadbuffer)(lua_State*, const char*, size_t, const char*); -GameHook *hck_luaL_loadbuffer; +GameHook* hck_luaL_loadbuffer; // ============= namespace Hooks { - void hook_luaL_register(lua_State *L, const char *libname, const luaL_Reg *l) { + void hook_luaL_register(lua_State* L, const char* libname, const luaL_Reg* l) { Console::log(Color::Aqua, "hook_luaL_register: libname=[%s]", libname); + + const luaL_Reg* ptr = l; + + for (int i = 0; ptr->name != NULL; i++, ptr++) { + Console::log(Color::Aqua, "hook_luaL_register: luaL_Reg[%d] name=[%s] func=[%p]", i, ptr->name, (void*)ptr->func); + } + return ((pluaL_register)*hck_luaL_register)(L, libname, l); } - int hook_luaL_loadstring(lua_State *L, const char *s) { + int hook_luaL_loadstring(lua_State* L, const char* s) { Console::log(Color::Aqua, "hook_luaL_loadstring: s=[ ... ]"); return ((pluaL_loadstring)*hck_luaL_loadstring)(L, s); } - lua_State *hook_lua_newstate(lua_Alloc f, void* ud) { + lua_State* hook_lua_newstate(lua_Alloc f, void* ud) { Console::log(Color::Aqua, "hck_lua_newstate: ud=[%p]", ud); return ((plua_newstate)*hck_lua_newstate)(f, ud); } - int hook_luaL_loadbuffer(lua_State *L, const char *buff, size_t sz, const char *name) { + int hook_luaL_loadbuffer(lua_State* L, const char* buff, size_t sz, const char* name) { Console::log(Color::Aqua, "hck_luaL_loadbuffer: buff=[ ... ], sz=[%zu], name=[%s]", sz, name); return ((pluaL_loadbuffer)*hck_luaL_loadbuffer)(L, buff, sz, name); } diff --git a/PluginDevFolder/SMLuaReaderPlugin/src/lua.h b/PluginDevFolder/SMLuaReaderPlugin/src/lua.h deleted file mode 100644 index f0113d9..0000000 --- a/PluginDevFolder/SMLuaReaderPlugin/src/lua.h +++ /dev/null @@ -1,7 +0,0 @@ -typedef void *(*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); -typedef struct lua_State lua_State; -typedef int (*lua_CFunction) (lua_State *L); -typedef struct luaL_Reg { - const char *name; - lua_CFunction func; -} luaL_Reg; diff --git a/PluginDevFolder/SMPluginTemplate/SMPluginTemplate.vcxproj b/PluginDevFolder/SMPluginTemplate/SMPluginTemplate.vcxproj index 0003fc3..0fb0b04 100644 --- a/PluginDevFolder/SMPluginTemplate/SMPluginTemplate.vcxproj +++ b/PluginDevFolder/SMPluginTemplate/SMPluginTemplate.vcxproj @@ -1,14 +1,6 @@ - - Debug - Win32 - - - Release - Win32 - Debug x64 @@ -29,19 +21,6 @@ 10.0 - - DynamicLibrary - true - v142 - Unicode - - - DynamicLibrary - false - v142 - true - Unicode - DynamicLibrary true @@ -60,12 +39,6 @@ - - - - - - @@ -73,16 +46,6 @@ - - true - $(SolutionDir)..\SMLibrary\include;$(IncludePath) - $(OutDir);$(LibraryPath) - - - false - $(SolutionDir)..\SMLibrary\include;$(IncludePath) - $(OutDir);$(LibraryPath) - true $(SolutionDir)..\SMLibrary\include;$(IncludePath) @@ -93,60 +56,6 @@ $(SolutionDir)..\SMLibrary\include;$(IncludePath) $(OutDir);$(LibraryPath) - - - Level3 - true - WIN32;_DEBUG;SMPLUGINTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - NotUsing - - - - - Windows - true - false - smlibrary.lib;%(AdditionalDependencies) - - - if not exist "$(OutputPath)plugins\" mkdir "$(OutputPath)plugins\" -copy /B "$(LocalDebuggerCommand)" "$(OutputPath)plugins\$(TargetFileName)" - - - - - - - - - Level3 - true - true - true - WIN32;NDEBUG;SMPLUGINTEMPLATE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - NotUsing - - - - - Windows - true - true - true - false - smlibrary.lib;%(AdditionalDependencies) - - - if not exist "$(OutputPath)plugins\" mkdir "$(OutputPath)plugins\" -copy /B "$(LocalDebuggerCommand)" "$(OutputPath)plugins\$(TargetFileName)" - - - - - - Level3 @@ -156,6 +65,7 @@ copy /B "$(LocalDebuggerCommand)" "$(OutputPath)plugins\$(TargetFileName)"NotUsing + stdcpp17 Windows @@ -183,6 +93,7 @@ copy /B "$(LocalDebuggerCommand)" "$(OutputPath)plugins\$(TargetFileName)"NotUsing + stdcpp17 Windows diff --git a/PluginDevFolder/TileEditorUnlockPlugin/TileEditorUnlockPlugin.vcxproj b/PluginDevFolder/TileEditorUnlockPlugin/TileEditorUnlockPlugin.vcxproj index 0973a43..74b2dd6 100644 --- a/PluginDevFolder/TileEditorUnlockPlugin/TileEditorUnlockPlugin.vcxproj +++ b/PluginDevFolder/TileEditorUnlockPlugin/TileEditorUnlockPlugin.vcxproj @@ -1,14 +1,6 @@ - - Debug - Win32 - - - Release - Win32 - Debug x64 @@ -29,19 +21,6 @@ 10.0 - - DynamicLibrary - true - v142 - Unicode - - - DynamicLibrary - false - v142 - true - Unicode - DynamicLibrary true @@ -60,12 +39,6 @@ - - - - - - @@ -73,16 +46,6 @@ - - true - $(SolutionDir)..\SMLibrary\include;$(IncludePath) - $(OutDir);$(LibraryPath) - - - false - $(SolutionDir)..\SMLibrary\include;$(IncludePath) - $(OutDir);$(LibraryPath) - true $(SolutionDir)..\SMLibrary\include;$(IncludePath) @@ -93,52 +56,6 @@ $(SolutionDir)..\SMLibrary\include;$(IncludePath) $(OutDir);$(LibraryPath) - - - Level3 - true - WIN32;_DEBUG;TILEEDITORUNLOCKPLUGIN_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - NotUsing - - - - - Windows - true - false - smlibrary.lib;%(AdditionalDependencies) - - - if not exist "$(OutputPath)plugins\" mkdir "$(OutputPath)plugins\" -copy /B "$(LocalDebuggerCommand)" "$(OutputPath)plugins\$(TargetFileName)" - - - - - Level3 - true - true - true - WIN32;NDEBUG;TILEEDITORUNLOCKPLUGIN_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) - true - NotUsing - - - - - Windows - true - true - true - false - smlibrary.lib;%(AdditionalDependencies) - - - if not exist "$(OutputPath)plugins\" mkdir "$(OutputPath)plugins\" -copy /B "$(LocalDebuggerCommand)" "$(OutputPath)plugins\$(TargetFileName)" - - Level3 @@ -148,6 +65,7 @@ copy /B "$(LocalDebuggerCommand)" "$(OutputPath)plugins\$(TargetFileName)"NotUsing + stdcpp17 Windows @@ -171,6 +89,7 @@ copy /B "$(LocalDebuggerCommand)" "$(OutputPath)plugins\$(TargetFileName)"NotUsing + stdcpp17 Windows diff --git a/SMInjector/SMInjector.sln b/SMInjector/SMInjector.sln index a160e7b..6b362ff 100644 --- a/SMInjector/SMInjector.sln +++ b/SMInjector/SMInjector.sln @@ -24,58 +24,33 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SMLuaReaderPlugin", "..\Plu {81368E09-9FA2-4F28-9915-22FE758BFD48} = {81368E09-9FA2-4F28-9915-22FE758BFD48} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PlaygroundPlugin", "..\PluginDevFolder\PlaygroundPlugin\PlaygroundPlugin.vcxproj", "{53AE29AD-5DEB-4557-A65E-6E68D2E5336A}" - ProjectSection(ProjectDependencies) = postProject - {81368E09-9FA2-4F28-9915-22FE758BFD48} = {81368E09-9FA2-4F28-9915-22FE758BFD48} - EndProjectSection +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SMLuaHook", "..\PluginDevFolder\SMLuaHook\SMLuaHook.vcxproj", "{93661F1D-21DC-4B5B-88EC-6BEA337DF56B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 Release|x64 = Release|x64 - Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E94AFAD6-2262-40E4-81D3-CD0130B98C0B}.Debug|x64.ActiveCfg = Debug|x64 - {E94AFAD6-2262-40E4-81D3-CD0130B98C0B}.Debug|x86.ActiveCfg = Debug|x64 {E94AFAD6-2262-40E4-81D3-CD0130B98C0B}.Release|x64.ActiveCfg = Release|x64 {E94AFAD6-2262-40E4-81D3-CD0130B98C0B}.Release|x64.Build.0 = Release|x64 - {E94AFAD6-2262-40E4-81D3-CD0130B98C0B}.Release|x86.ActiveCfg = Release|x64 {81368E09-9FA2-4F28-9915-22FE758BFD48}.Debug|x64.ActiveCfg = Debug|x64 - {81368E09-9FA2-4F28-9915-22FE758BFD48}.Debug|x86.ActiveCfg = Debug|Win32 - {81368E09-9FA2-4F28-9915-22FE758BFD48}.Debug|x86.Build.0 = Debug|Win32 {81368E09-9FA2-4F28-9915-22FE758BFD48}.Release|x64.ActiveCfg = Release|x64 {81368E09-9FA2-4F28-9915-22FE758BFD48}.Release|x64.Build.0 = Release|x64 - {81368E09-9FA2-4F28-9915-22FE758BFD48}.Release|x86.ActiveCfg = Release|Win32 - {81368E09-9FA2-4F28-9915-22FE758BFD48}.Release|x86.Build.0 = Release|Win32 {F09BBCFB-A1E3-43E0-8406-FBC8B72A09CA}.Debug|x64.ActiveCfg = Debug|x64 - {F09BBCFB-A1E3-43E0-8406-FBC8B72A09CA}.Debug|x86.ActiveCfg = Debug|Win32 - {F09BBCFB-A1E3-43E0-8406-FBC8B72A09CA}.Debug|x86.Build.0 = Debug|Win32 {F09BBCFB-A1E3-43E0-8406-FBC8B72A09CA}.Release|x64.ActiveCfg = Release|x64 {F09BBCFB-A1E3-43E0-8406-FBC8B72A09CA}.Release|x64.Build.0 = Release|x64 - {F09BBCFB-A1E3-43E0-8406-FBC8B72A09CA}.Release|x86.ActiveCfg = Release|Win32 - {F09BBCFB-A1E3-43E0-8406-FBC8B72A09CA}.Release|x86.Build.0 = Release|Win32 {16BDCA26-D20C-4AC4-8DB3-9C108719B752}.Debug|x64.ActiveCfg = Debug|x64 - {16BDCA26-D20C-4AC4-8DB3-9C108719B752}.Debug|x86.ActiveCfg = Debug|Win32 - {16BDCA26-D20C-4AC4-8DB3-9C108719B752}.Debug|x86.Build.0 = Debug|Win32 {16BDCA26-D20C-4AC4-8DB3-9C108719B752}.Release|x64.ActiveCfg = Release|x64 {16BDCA26-D20C-4AC4-8DB3-9C108719B752}.Release|x64.Build.0 = Release|x64 - {16BDCA26-D20C-4AC4-8DB3-9C108719B752}.Release|x86.ActiveCfg = Release|Win32 - {16BDCA26-D20C-4AC4-8DB3-9C108719B752}.Release|x86.Build.0 = Release|Win32 {13E88C11-DEB6-47D5-9382-4E5D484A58C2}.Debug|x64.ActiveCfg = Debug|x64 - {13E88C11-DEB6-47D5-9382-4E5D484A58C2}.Debug|x86.ActiveCfg = Debug|x64 {13E88C11-DEB6-47D5-9382-4E5D484A58C2}.Release|x64.ActiveCfg = Release|x64 {13E88C11-DEB6-47D5-9382-4E5D484A58C2}.Release|x64.Build.0 = Release|x64 - {13E88C11-DEB6-47D5-9382-4E5D484A58C2}.Release|x86.ActiveCfg = Release|x64 - {53AE29AD-5DEB-4557-A65E-6E68D2E5336A}.Debug|x64.ActiveCfg = Debug|x64 - {53AE29AD-5DEB-4557-A65E-6E68D2E5336A}.Debug|x64.Build.0 = Debug|x64 - {53AE29AD-5DEB-4557-A65E-6E68D2E5336A}.Debug|x86.ActiveCfg = Debug|Win32 - {53AE29AD-5DEB-4557-A65E-6E68D2E5336A}.Debug|x86.Build.0 = Debug|Win32 - {53AE29AD-5DEB-4557-A65E-6E68D2E5336A}.Release|x64.ActiveCfg = Release|x64 - {53AE29AD-5DEB-4557-A65E-6E68D2E5336A}.Release|x64.Build.0 = Release|x64 - {53AE29AD-5DEB-4557-A65E-6E68D2E5336A}.Release|x86.ActiveCfg = Release|Win32 - {53AE29AD-5DEB-4557-A65E-6E68D2E5336A}.Release|x86.Build.0 = Release|Win32 + {93661F1D-21DC-4B5B-88EC-6BEA337DF56B}.Debug|x64.ActiveCfg = Debug|x64 + {93661F1D-21DC-4B5B-88EC-6BEA337DF56B}.Debug|x64.Build.0 = Debug|x64 + {93661F1D-21DC-4B5B-88EC-6BEA337DF56B}.Release|x64.ActiveCfg = Release|x64 + {93661F1D-21DC-4B5B-88EC-6BEA337DF56B}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -84,7 +59,7 @@ Global {F09BBCFB-A1E3-43E0-8406-FBC8B72A09CA} = {4EC6A5BC-FCE4-4FFE-AC55-7D83B6B7C8D7} {16BDCA26-D20C-4AC4-8DB3-9C108719B752} = {4EC6A5BC-FCE4-4FFE-AC55-7D83B6B7C8D7} {13E88C11-DEB6-47D5-9382-4E5D484A58C2} = {4EC6A5BC-FCE4-4FFE-AC55-7D83B6B7C8D7} - {53AE29AD-5DEB-4557-A65E-6E68D2E5336A} = {4EC6A5BC-FCE4-4FFE-AC55-7D83B6B7C8D7} + {93661F1D-21DC-4B5B-88EC-6BEA337DF56B} = {4EC6A5BC-FCE4-4FFE-AC55-7D83B6B7C8D7} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D7E2F6B1-96AC-4FB5-B8AA-FDB43C7F169E} diff --git a/SMInjector/src/main.cpp b/SMInjector/src/main.cpp index fa7393c..dae7a96 100644 --- a/SMInjector/src/main.cpp +++ b/SMInjector/src/main.cpp @@ -9,28 +9,23 @@ #include "find_steam.h" +namespace fs = std::filesystem; + bool Inject(HANDLE, const wchar_t*); +bool InjectSetDllDirectory(HANDLE, const wchar_t*); inline bool does_file_exist(const wchar_t* name) { struct _stat buffer; return (_wstat(name, &buffer) == 0); } -std::wstring get_dir_path() { - wchar_t buffer[MAX_PATH] = { 0 }; - GetModuleFileNameW(NULL, buffer, MAX_PATH); - std::wstring::size_type pos = std::wstring(buffer).find_last_of(L"\\/"); - return std::wstring(buffer).substr(0, pos); -} - -std::wstring get_dll_path(const wchar_t* dll_name) { - wchar_t buffer[MAX_PATH] = { 0 }; - GetModuleFileNameW(NULL, buffer, MAX_PATH); - std::wstring::size_type pos = std::wstring(buffer).find_last_of(L"\\/"); - return std::wstring(buffer).substr(0, pos).append(L"\\").append(dll_name); +fs::path get_dir_path() { + TCHAR dir[MAX_PATH] = { 0 }; + DWORD length = GetModuleFileName(NULL, dir, _countof(dir)); + return fs::path(dir).parent_path(); } -BOOL startup(std::wstring in_exe, std::wstring in_dir, std::wstring in_cmd, HANDLE &hProcess, HANDLE &hThread) { +BOOL startup(fs::path in_exe, fs::path in_dir, std::wstring in_cmd, HANDLE &hProcess, HANDLE &hThread) { STARTUPINFO si; PROCESS_INFORMATION pi; @@ -39,8 +34,8 @@ BOOL startup(std::wstring in_exe, std::wstring in_dir, std::wstring in_cmd, HAND wchar_t cmd[4097] = { 0 }; ZeroMemory(cmd, 4097); - memcpy(exe, in_exe.c_str(), min(in_exe.size() * 2, MAX_PATH + 1)); - memcpy(dir, in_dir.c_str(), min(in_dir.size() * 2, MAX_PATH + 1)); + memcpy(exe, in_exe.c_str(), min(in_exe.string().size() * 2, MAX_PATH + 1)); + memcpy(dir, in_dir.c_str(), min(in_dir.string().size() * 2, MAX_PATH + 1)); swprintf_s(cmd, 4097, L"\"%s\" %s", exe, in_cmd.c_str()); //wprintf(L"EXE: [%s]\n", exe); @@ -78,9 +73,10 @@ BOOL startup(std::wstring in_exe, std::wstring in_dir, std::wstring in_cmd, HAND int main(int argc, char** argv) { printf("SMInjector: startup\n"); - std::wstring game_path = SteamFinder::FindGame(L"Scrap Mechanic"); - std::wstring dll_path = get_dll_path(L"SMLibrary.dll"); - + fs::path game_path = SteamFinder::FindGame(L"Scrap Mechanic"); + fs::path dir_path = get_dir_path(); + fs::path dll_path = dir_path / "SMLibrary.dll"; + wprintf(L"GAME: [%s]\n", game_path.c_str()); wprintf(L"DLL : [%s]\n", dll_path.c_str()); @@ -91,18 +87,38 @@ int main(int argc, char** argv) { HANDLE hProcess; HANDLE hThread; - std::wstring exe_exe = std::wstring(game_path).append(L"\\Release\\ScrapMechanic.exe"); - std::wstring exe_dir = std::wstring(game_path).append(L"\\Release"); + fs::path exe_dir = game_path / "Release"; + fs::path exe_exe = game_path / "Release" / "ScrapMechanic.exe"; if(startup(exe_exe, exe_dir, L"-dev", hProcess, hThread)) { - if(!Inject(hProcess, dll_path.c_str())) { - printf("SMInjector: failed to inject dll file\n"); + + // Set the dll directory so SMLibrary.dll can load all it's dependencies + if (!InjectSetDllDirectory(hProcess, dir_path.c_str())) { + printf("SMInjector: Failed to inject dll directory\n"); + TerminateProcess(hProcess, 0); + CloseHandle(hProcess); + return 0; + } + + // Inject SMLibrary.dll + if (!Inject(hProcess, dll_path.c_str())) { + printf("SMInjector: Failed to inject SMLibrary.dll\n"); TerminateProcess(hProcess, 0); CloseHandle(hProcess); return 0; } - printf("\nSMInjector: Adding plugins.\n"); - for(const auto& file : std::filesystem::directory_iterator(get_dir_path().append(L"\\plugins"))) { + // Create plugin dependencies directory + if (!fs::exists(dir_path / "plugins" / "dependencies")) { + fs::create_directories(dir_path / "plugins" / "dependencies"); + } + + // Inject plugins + printf("\nSMInjector: Adding plugins...\n"); + for(const auto& file : fs::directory_iterator(get_dir_path() / "plugins")) { + if (file.path().extension() != ".dll") { + continue; + } + if(!Inject(hProcess, file.path().c_str())) { wprintf(L" Failed to inject '%s'\n", file.path().filename().c_str()); return 0; @@ -147,3 +163,34 @@ bool Inject(HANDLE hProc, const wchar_t* path) { CloseHandle(threadID); return true; } + +bool InjectSetDllDirectory(HANDLE hProc, const wchar_t* path) { + if (!does_file_exist(path)) { + return false; + } + + const size_t path_length = wcslen(path) * 2; + + LPVOID lpvoid = VirtualAllocEx(hProc, NULL, path_length, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + if (!lpvoid) { + return false; + } + + WriteProcessMemory(hProc, lpvoid, path, path_length, NULL); + HMODULE kernel32 = GetModuleHandleA("kernel32.dll"); + if (!kernel32) { + return false; + } + + LPVOID SetDllDirectoryAddr = (LPVOID)GetProcAddress(kernel32, "SetDllDirectoryW"); + + HANDLE threadID = CreateRemoteThread(hProc, NULL, NULL, (LPTHREAD_START_ROUTINE)SetDllDirectoryAddr, lpvoid, NULL, NULL); + if (!threadID) { + return false; + } + + WaitForSingleObject(threadID, INFINITE); + VirtualFreeEx(hProc, lpvoid, 0, MEM_RELEASE); + CloseHandle(threadID); + return true; +} diff --git a/SMLibrary/SMLibrary.vcxproj b/SMLibrary/SMLibrary.vcxproj index 41c4fb6..5503edf 100644 --- a/SMLibrary/SMLibrary.vcxproj +++ b/SMLibrary/SMLibrary.vcxproj @@ -1,14 +1,6 @@ - - Debug - Win32 - - - Release - Win32 - Debug x64 @@ -27,19 +19,6 @@ SMLibrary - - DynamicLibrary - true - v142 - Unicode - - - DynamicLibrary - false - v142 - true - Unicode - DynamicLibrary true @@ -58,12 +37,6 @@ - - - - - - @@ -71,14 +44,6 @@ - - true - $(SourcePath) - - - false - $(SourcePath) - true $(SourcePath) @@ -87,35 +52,6 @@ false $(SourcePath) - - - Level3 - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - MaxSpeed - - - Console - true - - - - - Level3 - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - - - Console - true - true - true - - Level3 @@ -123,6 +59,8 @@ _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true MaxSpeed + Default + stdcpp17 Console @@ -137,6 +75,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true + stdcpp17 Console @@ -153,11 +92,24 @@ + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + \ No newline at end of file diff --git a/SMLibrary/SMLibrary.vcxproj.filters b/SMLibrary/SMLibrary.vcxproj.filters index 3c136c5..556726b 100644 --- a/SMLibrary/SMLibrary.vcxproj.filters +++ b/SMLibrary/SMLibrary.vcxproj.filters @@ -41,5 +41,17 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + + \ No newline at end of file diff --git a/SMLibrary/include/console.h b/SMLibrary/include/console.h index 1c59933..a62fa9d 100644 --- a/SMLibrary/include/console.h +++ b/SMLibrary/include/console.h @@ -37,8 +37,8 @@ namespace Console { }; #ifdef _SM_LIBRARY_BUILD_PLUGIN - _LIB_IMPORT - void vlogf(Color color, const char *plugin_name, const char *format, va_list args); + _LIB_IMPORT void vlogf(Color color, const char* plugin_name, const char* format, va_list args); + _LIB_IMPORT void wvlogf(Color color, const char* plugin_name, const wchar_t* format, va_list args); #else FILE* console_handle = NULL; HANDLE hConsole; @@ -59,8 +59,7 @@ namespace Console { #endif } - _LIB_EXPORT - void vlogf(Color color, const char *plugin_name, const char *format, va_list args) { + _LIB_EXPORT void vlogf(Color color, const char *plugin_name, const char *format, va_list args) { if(!console_handle) return; if(!hConsole) hConsole = GetStdHandle(STD_OUTPUT_HANDLE); @@ -70,12 +69,29 @@ namespace Console { vprintf(format, args); printf("\n"); } + + _LIB_EXPORT void wvlogf(Color color, const char* plugin_name, const wchar_t* format, va_list args) { + if (!console_handle) return; + + if (!hConsole) hConsole = GetStdHandle(STD_OUTPUT_HANDLE); + if (hConsole) SetConsoleTextAttribute(hConsole, (WORD)color); + + printf("[%s]: ", plugin_name); + vwprintf(format, args); + printf("\n"); + } #endif void log(Color color, const char *format, ...) { va_list args; va_start(args, format); vlogf(color, _LIB_PLUGIN_NAME_STR, format, args); } + + void wlog(Color color, const wchar_t* format, ...) { + va_list args; + va_start(args, format); + wvlogf(color, _LIB_PLUGIN_NAME_STR, format, args); + } } #endif diff --git a/SMLibrary/include/path_helper.h b/SMLibrary/include/path_helper.h new file mode 100644 index 0000000..7a21e49 --- /dev/null +++ b/SMLibrary/include/path_helper.h @@ -0,0 +1,42 @@ +#pragma once +#include +#include + +#include "plugin_config.h" + +namespace fs = std::filesystem; + +namespace PathHelper { + + fs::path getInstallationPath() { + TCHAR dir[MAX_PATH] = { 0 }; + DWORD length = GetModuleFileName(NULL, dir, _countof(dir)); + return fs::path(dir).parent_path(); + } + + fs::path resolvePath(std::string path) { + +#ifdef _LIB_PLUGIN_NAME_STR + if (path.rfind("$PLUGIN_CONFIG", 0) == 0) { + return PluginConfig::getConfigDirectory() / fs::path(_LIB_PLUGIN_NAME_STR) / path.substr(path.find_first_of("/") + 1); + } +#endif + + if (path.rfind("$GAME_DATA", 0) == 0) { + return PathHelper::getInstallationPath() / fs::path("Data") / path.substr(path.find_first_of("/") + 1); + } + + if (path.rfind("$SURVIVAL_DATA", 0) == 0) { + return PathHelper::getInstallationPath() / fs::path("Survival") / path.substr(path.find_first_of("/") + 1); + } + + if (path.rfind("$CHALLENGE_DATA", 0) == 0) { + return PathHelper::getInstallationPath() / fs::path("ChallengeData") / path.substr(path.find_first_of("/") + 1); + } + + // TODO: Call the funtion in the game to do this + + return path; + } + +} diff --git a/SMLibrary/include/plugin_config.h b/SMLibrary/include/plugin_config.h new file mode 100644 index 0000000..7c25e03 --- /dev/null +++ b/SMLibrary/include/plugin_config.h @@ -0,0 +1,114 @@ +#pragma once +#include "stdafx.h" +#include +#include +#include +#pragma warning(push) +#pragma warning(disable : 26819 28020) +#define JSON_DIAGNOSTICS 1 +#include +#pragma warning(pop) +#include "console.h" + +namespace fs = std::filesystem; +using json = nlohmann::json; + +class PluginConfig { +private: + inline static fs::path configDirectory; + + const char* pluginName = NULL; + fs::path path; + fs::path fullPath; + + const char* defaultContent = "// Empty config file\n{}\n"; + +public: + json root; + + _LIB_FUNCTION static bool setConfigDirectory(const fs::path& configDirectory) { + PluginConfig::configDirectory = fs::absolute(configDirectory); + + if (!fs::is_directory(configDirectory) || !fs::exists(configDirectory)) { + if (!fs::create_directories(configDirectory)) { + Console::wlog(Console::Color::LightRed, L"Failed creating main config directory \"%s\"", PluginConfig::configDirectory.c_str()); + return false; + } + } + + Console::wlog(Console::Color::Aqua, L"Set main config directory to \"%s\"", PluginConfig::configDirectory.c_str()); + + return true; + } + + _LIB_FUNCTION static fs::path getConfigDirectory() { + return PluginConfig::configDirectory; + } + + PluginConfig(const char *pluginName, const fs::path& path) { + this->pluginName = pluginName; + this->path = path; + + this->fullPath = PluginConfig::getConfigDirectory() / fs::path(pluginName) / this->path; + } + +#ifdef _LIB_PLUGIN_NAME_STR + PluginConfig(const fs::path& path) : PluginConfig(_LIB_PLUGIN_NAME_STR, path) {} +#endif + + void setDefaultContent(const char* defaultContent) { + this->defaultContent = defaultContent; + } + + bool createIfNotExists() { + if (fs::exists(this->fullPath)) { + return false; + } + + Console::wlog(Console::Color::LightYellow, L"Warning: Unable to find config file \"%s\", creating with default content...", this->fullPath.c_str()); + + if (!fs::exists(this->fullPath.parent_path())) { + fs::create_directories(this->fullPath.parent_path()); + } + + std::ofstream ofs(this->fullPath); + + if (!ofs.is_open()) { +#pragma warning(suppress : 4996) + Console::wlog(Console::Color::LightRed, L"Failed creating config file \"%s\": %s", this->fullPath.c_str(), _wcserror(errno)); + return false; + } + + ofs << this->defaultContent; + ofs.close(); + + return true; + } + + bool load() { + std::ifstream ifs(this->fullPath); + + if (!ifs.is_open()) { +#pragma warning(suppress : 4996) + Console::wlog(Console::Color::LightRed, L"Failed reading config file \"%s\": %s", this->fullPath.c_str(), _wcserror(errno)); + return false; + } + + Console::wlog(Console::Color::Gray, L"Loaded config file \"%s\"", this->fullPath.c_str()); + + + try { + this->root = json::parse(ifs, nullptr, true, true); + std::cout << this->root.dump(4) << '\n'; + + } catch (json::exception& e) { + Console::wlog(Console::Color::LightRed, L"Failed parsing config file \"%s\":", this->fullPath.c_str()); + Console::log(Console::Color::LightRed, " %s", e.what()); + } + + ifs.close(); + + return true; + } + +}; \ No newline at end of file diff --git a/SMLibrary/include/sigscan.h b/SMLibrary/include/sigscan.h new file mode 100644 index 0000000..b22810a --- /dev/null +++ b/SMLibrary/include/sigscan.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include + +#include "console.h" + +#define ERROR_GETMODULE(reason, ...) Console::log(Color::LightRed, "[SignatureScanner] Failed getting handle of module %s. Reason: " reason, moduleName, __VA_ARGS__) + +using Console::Color; + +class SignatureScanner { +private: + LPCWSTR moduleName; + DWORD64 moduleBase; + DWORD moduleSize; + + MODULEINFO GetModuleInfo(LPCWSTR moduleName) { + MODULEINFO moduleInfo = { 0 }; + HMODULE hModule = GetModuleHandle(moduleName); + if (hModule == NULL) + return moduleInfo; + + GetModuleInformation(GetCurrentProcess(), hModule, &moduleInfo, sizeof(MODULEINFO)); + + return moduleInfo; + } + +public: + SignatureScanner(LPCWSTR moduleName) { + this->moduleName = moduleName; + + MODULEINFO moduleInfo = GetModuleInfo(moduleName); + + if (moduleInfo.SizeOfImage == 0) { + Console::log(Color::LightRed, "[SignatureScanner] Failed getting info of module %s", moduleName); + return; + } + + this->moduleBase = (DWORD64) moduleInfo.lpBaseOfDll; + this->moduleSize = moduleInfo.SizeOfImage; + + Console::wlog(Color::Aqua, L"[SignatureScanner] Found module %s with base=[0x%llX] size=[%ul]", moduleName, this->moduleBase, this->moduleSize); + } + +}; + +#undef ERROR_GETMODULE diff --git a/SMLibrary/packages.config b/SMLibrary/packages.config new file mode 100644 index 0000000..db3400a --- /dev/null +++ b/SMLibrary/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SMLibrary/src/sm_lib.cpp b/SMLibrary/src/sm_lib.cpp index 3a833c5..df36141 100644 --- a/SMLibrary/src/sm_lib.cpp +++ b/SMLibrary/src/sm_lib.cpp @@ -2,11 +2,16 @@ #include #include #include +#include +#include + +namespace fs = std::filesystem; #define _SM_PLUGIN_NAME SMLibrary #define _SM_OUTPUT_LOGS #include "../include/sm_lib.h" +#include "../include/plugin_config.h" #include "../include/console.h" using Console::Color; @@ -32,7 +37,15 @@ BOOL PostConsoleInjections(); #include "hooks.h" -BOOL Startup(HMODULE hModule) { +fs::path smlibrarydllPath; + +fs::path GetDllPath(HMODULE hModule) { + TCHAR dllPath[MAX_PATH] = { 0 }; + DWORD length = GetModuleFileName(hModule, dllPath, _countof(dllPath)); + return fs::path(dllPath); +} + +BOOL Startup() { HMODULE sm_handle = GetModuleHandleA("ScrapMechanic.exe"); if(!sm_handle) return false; @@ -43,6 +56,9 @@ BOOL Startup(HMODULE hModule) { BOOL PostConsoleInjections() { Console::log_open(); + + PluginConfig::setConfigDirectory(smlibrarydllPath.parent_path() / "config"); + Console::log(Color::Aqua, "Installing the library functions"); const size_t plugins_size = plugins.size(); @@ -60,11 +76,45 @@ BOOL PostConsoleInjections() { return true; } +void SetupDllDirectories() { + fs::path baseDir = smlibrarydllPath.parent_path(); + + fs::path directories[] = { + baseDir, + baseDir / "plugins", + baseDir / "plugins" / "dependencies" + }; + + for (const fs::path& dir : directories) { + if (!AddDllDirectory(dir.c_str())) { + std::string body; + body += "Failed adding dll directory \""; + body += dir.string(); + body += "\"\nSome plugins might not load\n\nReason:\n"; + body += std::system_category().message(GetLastError()); + + MessageBoxA(0, body.c_str(), "AddDllDirectory failed", MB_ICONERROR); + } + } + + if (!SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS)) { + std::string body; + body += "Some plugins might not load\n\nReason:\n"; + body += std::system_category().message(GetLastError()); + + MessageBoxA(0, body.c_str(), "SetDefaultDllDirectories failed", MB_ICONERROR); + } +} + BOOL WINAPI DllMain(HMODULE hModule, DWORD fdwReason, LPVOID lpReserved) { switch(fdwReason) { case DLL_PROCESS_ATTACH: - Startup(hModule); MessageBox(0, L"From DLL\n", L"Process Attach", MB_ICONINFORMATION); + + smlibrarydllPath = GetDllPath(hModule); + + SetupDllDirectories(); + Startup(); break; case DLL_PROCESS_DETACH: