commit 84f912cb344b0008a01e98b95c3b2468c647e925 Author: Andreas Maerten <24669514+Yimura@users.noreply.github.com> Date: Fri Aug 11 22:51:23 2023 +0200 Initial commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..8c858c5 --- /dev/null +++ b/.clang-format @@ -0,0 +1,74 @@ +BasedOnStyle: Microsoft +AccessModifierOffset: '-4' +AlignAfterOpenBracket: DontAlign +AlignConsecutiveAssignments: 'true' +AlignEscapedNewlines: Left +AlignOperands: 'false' +AlignTrailingComments: 'true' +AllowAllArgumentsOnNextLine: 'false' +AllowAllConstructorInitializersOnNextLine: 'false' +AllowAllParametersOfDeclarationOnNextLine: 'false' +AllowShortBlocksOnASingleLine: 'false' +AllowShortCaseLabelsOnASingleLine: 'true' +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: 'false' +AlwaysBreakTemplateDeclarations: 'Yes' +BinPackArguments: 'false' +BinPackParameters: 'true' +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Allman +BreakBeforeTernaryOperators: 'false' +BreakConstructorInitializers: AfterColon +BreakInheritanceList: AfterColon +BreakStringLiterals: 'false' +CompactNamespaces: 'false' +ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' +ConstructorInitializerIndentWidth: '4' +Cpp11BracedListStyle: 'true' +DerivePointerAlignment: 'false' +FixNamespaceComments: 'false' +IncludeBlocks: Regroup +IndentCaseLabels: 'false' +IndentPPDirectives: BeforeHash +IndentWidth: '4' +TabWidth: 4 +IndentWrappedFunctionNames: 'true' +KeepEmptyLinesAtTheStartOfBlocks: 'false' +Language: Cpp +MacroBlockBegin: '0' +MaxEmptyLinesToKeep: '2' +NamespaceIndentation: All +PenaltyExcessCharacter: '0' +PenaltyReturnTypeOnItsOwnLine: '0' +PointerAlignment: Left +ReflowComments: 'false' +SortIncludes: 'true' +SortUsingDeclarations: 'true' +SpaceAfterCStyleCast: 'false' +SpaceAfterLogicalNot: 'false' +SpaceAfterTemplateKeyword: 'false' +SpaceBeforeAssignmentOperators: 'true' +SpaceBeforeCpp11BracedList: 'false' +SpaceBeforeCtorInitializerColon: 'true' +SpaceBeforeInheritanceColon: 'true' +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: 'true' +SpaceInEmptyParentheses: 'false' +SpacesBeforeTrailingComments: '1' +SpacesInAngles: 'false' +SpacesInCStyleCastParentheses: 'false' +SpacesInContainerLiterals: 'false' +SpacesInParentheses: 'false' +SpacesInSquareBrackets: 'false' +Standard: Auto +UseTab: ForIndentation + +BreakBeforeBraces: Custom +BraceWrapping: + BeforeLambdaBody: 'false' + AfterCaseLabel: 'true' + +PackConstructorInitializers: Never \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d163863 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..6db4aa9 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "GTA5.exe Attach", + "type": "cppvsdbg", + "request": "attach", + "processId": "${command:pickProcess}" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4a8962b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,81 @@ +{ + "files.associations": { + "*.c": "c", + "string_view": "cpp", + "algorithm": "cpp", + "atomic": "cpp", + "bit": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "exception": "cpp", + "format": "cpp", + "forward_list": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "locale": "cpp", + "memory": "cpp", + "mutex": "cpp", + "new": "cpp", + "optional": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "string": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "utility": "cpp", + "vector": "cpp", + "xfacet": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xstddef": "cpp", + "xstring": "cpp", + "xtr1common": "cpp", + "xutility": "cpp", + "array": "cpp", + "condition_variable": "cpp", + "deque": "cpp", + "fstream": "cpp", + "functional": "cpp", + "iostream": "cpp", + "list": "cpp", + "queue": "cpp", + "ranges": "cpp", + "source_location": "cpp", + "span": "cpp", + "unordered_map": "cpp", + "xhash": "cpp", + "filesystem": "cpp" + }, + "C_Cpp.default.configurationProvider": "maxmitti.cmake-tools-fork" +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1215b8b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.20.x) + +project(NewBase VERSION 0.0.1 DESCRIPTION "A new base using new C++ features optimised for speed and ease of use") + +# libs +include(cmake/async-logger.cmake) +include(cmake/imgui.cmake) +include(cmake/json.cmake) +include(cmake/minhook.cmake) + +# source +set(SRC_DIR "${PROJECT_SOURCE_DIR}/src") +file(GLOB_RECURSE SRC_FILES + "${SRC_DIR}/**.hpp" + "${SRC_DIR}/**.cpp" +) + +add_library(${PROJECT_NAME} MODULE ${SRC_FILES}) +set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 23) + +target_precompile_headers(${PROJECT_NAME} PUBLIC "${SRC_DIR}/common.hpp") + +message(STATUS "Setting up include directories.") +target_include_directories(${PROJECT_NAME} PRIVATE + "${SRC_DIR}" + "${imgui_SOURCE_DIR}" + "${minhook_SOURCE_DIR}/include" +) + +message(STATUS "Setting up linked libraries") +target_link_libraries(${PROJECT_NAME} PRIVATE AsyncLogger imgui minhook nlohmann_json::nlohmann_json) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2c74b72 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Template Hell Base + +This base is honestly a joke and a learning opporunity for myself to learn some more stuff about C++20. I went through the basics of what makes a mod menu base and tried to template them to hell and back. + +## Structure + +- `core/` : Essential general features for the base +- `game/` : Game specific implemented things +- `util/` : Loose functions that aren't game specific \ No newline at end of file diff --git a/cmake/async-logger.cmake b/cmake/async-logger.cmake new file mode 100644 index 0000000..b04224d --- /dev/null +++ b/cmake/async-logger.cmake @@ -0,0 +1,12 @@ +include(FetchContent) + +message(STATUS "Setting up AsyncLogger") +FetchContent_Declare( + AsyncLogger + GIT_REPOSITORY https://github.com/Yimura/AsyncLogger.git + GIT_TAG v0.0.6 + GIT_PROGRESS TRUE +) +FetchContent_MakeAvailable(AsyncLogger) + +set_property(TARGET AsyncLogger PROPERTY CXX_STANDARD 23) \ No newline at end of file diff --git a/cmake/imgui.cmake b/cmake/imgui.cmake new file mode 100644 index 0000000..0f86a30 --- /dev/null +++ b/cmake/imgui.cmake @@ -0,0 +1,33 @@ +include(FetchContent) + +set(LIB_NAME "imgui") +message(STATUS "Setting up ${LIB_NAME}") + +FetchContent_Declare( + ${LIB_NAME} + GIT_REPOSITORY https://github.com/Spyral-Org/imgui.git + GIT_TAG 44d98bfd6d12caee43bea49fda71c8313c3e57e7 + GIT_PROGRESS TRUE +) + +FetchContent_GetProperties(${LIB_NAME}) + +if(NOT imgui_POPULATED) + FetchContent_Populate(${LIB_NAME}) + + file(GLOB SRC_IMGUI + "${imgui_SOURCE_DIR}/*.cpp" + "${imgui_SOURCE_DIR}/*.h" + "${imgui_SOURCE_DIR}/backends/imgui_impl_win32.*" + "${imgui_SOURCE_DIR}/backends/imgui_impl_dx11.*" + "${imgui_SOURCE_DIR}/misc/cpp/imgui_stdlib.*" + ) + + add_library(imgui STATIC ${SRC_IMGUI}) + source_group(TREE ${imgui_SOURCE_DIR} PREFIX "imgui" FILES ${SRC_IMGUI}) + target_include_directories(imgui PRIVATE + "${imgui_SOURCE_DIR}" + "${imgui_SOURCE_DIR}/backends" + "${imgui_SOURCE_DIR}/misc/cpp" + ) +endif() \ No newline at end of file diff --git a/cmake/json.cmake b/cmake/json.cmake new file mode 100644 index 0000000..2e50b53 --- /dev/null +++ b/cmake/json.cmake @@ -0,0 +1,6 @@ +include(FetchContent) + +message(STATUS "Setting up nlohmann::json") +FetchContent_Declare(json GIT_REPOSITORY "https://github.com/nlohmann/json/" GIT_TAG v3.11.2 GIT_SHALLOW TRUE) +option(JSON_MultipleHeaders "Disable nlohmann JSON multi header default." OFF) +FetchContent_MakeAvailable(json) \ No newline at end of file diff --git a/cmake/minhook.cmake b/cmake/minhook.cmake new file mode 100644 index 0000000..905fb8d --- /dev/null +++ b/cmake/minhook.cmake @@ -0,0 +1,12 @@ +include(FetchContent) + +set(LIB_NAME "minhook") +message(STATUS "Setting up ${LIB_NAME}") + +FetchContent_Declare( + ${LIB_NAME} + GIT_REPOSITORY https://github.com/TsudaKageyu/minhook.git + GIT_TAG 98b74f1fc12d00313d91f10450e5b3e0036175e3 + GIT_PROGRESS TRUE +) +FetchContent_MakeAvailable(${LIB_NAME}) \ No newline at end of file diff --git a/src/common.cpp b/src/common.cpp new file mode 100644 index 0000000..5ababec --- /dev/null +++ b/src/common.cpp @@ -0,0 +1,9 @@ +#include "common.hpp" + +namespace NewBase +{ + std::atomic g_Running{true}; + HINSTANCE g_DllInstance{nullptr}; + HANDLE g_MainThread{INVALID_HANDLE_VALUE}; + DWORD g_MainThreadId = -1; +} \ No newline at end of file diff --git a/src/common.hpp b/src/common.hpp new file mode 100644 index 0000000..5730afd --- /dev/null +++ b/src/common.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace al; +#include "core/logger/LogHelper.hpp" + +namespace NewBase +{ + using namespace std::chrono_literals; + + extern std::atomic g_Running; + extern HINSTANCE g_DllInstance; + extern HANDLE g_MainThread; + extern DWORD g_MainThreadId; +} \ No newline at end of file diff --git a/src/core/filemgr/BaseObj.cpp b/src/core/filemgr/BaseObj.cpp new file mode 100644 index 0000000..03d8a1e --- /dev/null +++ b/src/core/filemgr/BaseObj.cpp @@ -0,0 +1,20 @@ +#include "BaseObj.hpp" + +namespace NewBase +{ + BaseObj::BaseObj(const std::filesystem::path& path) : + m_Path(path) + { + + } + + [[nodiscard]] bool BaseObj::Exists() const + { + return exists(m_Path); + } + + const std::filesystem::path& BaseObj::Path() const + { + return m_Path; + } +} \ No newline at end of file diff --git a/src/core/filemgr/BaseObj.hpp b/src/core/filemgr/BaseObj.hpp new file mode 100644 index 0000000..ae56076 --- /dev/null +++ b/src/core/filemgr/BaseObj.hpp @@ -0,0 +1,18 @@ +#pragma once +#include + +namespace NewBase +{ + class BaseObj + { + public: + BaseObj(const std::filesystem::path& path); + + [[nodiscard]] bool Exists() const; + const std::filesystem::path& Path() const; + + protected: + const std::filesystem::path m_Path; + + }; +} \ No newline at end of file diff --git a/src/core/filemgr/File.cpp b/src/core/filemgr/File.cpp new file mode 100644 index 0000000..4c59526 --- /dev/null +++ b/src/core/filemgr/File.cpp @@ -0,0 +1,24 @@ +#include "File.hpp" +#include "FileMgr.hpp" + +namespace NewBase +{ + File::File(const std::filesystem::path& path) : + BaseObj(path) + { + + } + + File File::Move(std::filesystem::path newPath) + { + if (newPath.is_relative()) + newPath = m_Path.parent_path() / newPath; + + FileMgr::EnsureFileCanBeCreated(newPath); + + if (std::filesystem::exists(m_Path)) + std::filesystem::rename(m_Path, newPath); + + return { newPath }; + } +} \ No newline at end of file diff --git a/src/core/filemgr/File.hpp b/src/core/filemgr/File.hpp new file mode 100644 index 0000000..fb988aa --- /dev/null +++ b/src/core/filemgr/File.hpp @@ -0,0 +1,14 @@ +#pragma once +#include "BaseObj.hpp" + +namespace NewBase +{ + class File final : public BaseObj + { + public: + File(const std::filesystem::path& path); + + File Move(std::filesystem::path newPath); + + }; +} \ No newline at end of file diff --git a/src/core/filemgr/FileMgr.cpp b/src/core/filemgr/FileMgr.cpp new file mode 100644 index 0000000..9694598 --- /dev/null +++ b/src/core/filemgr/FileMgr.cpp @@ -0,0 +1,57 @@ +#include "FileMgr.hpp" +#include "File.hpp" +#include "Folder.hpp" + +namespace NewBase +{ + void FileMgr::Init(const std::filesystem::path& rootFolder) + { + GetInstance().InitImpl(rootFolder); + } + + void FileMgr::InitImpl(const std::filesystem::path& rootFolder) + { + m_RootFolder = rootFolder; + + CreateFolderIfNotExists(m_RootFolder); + } + + const std::filesystem::path& FileMgr::CreateFolderIfNotExists(const std::filesystem::path &folder) + { + bool create_path = !exists(folder); + + if (!create_path && !is_directory(folder)) + { + std::filesystem::remove(folder); + create_path = true; + } + if (create_path) + create_directory(folder); + + return folder; + } + + const std::filesystem::path& FileMgr::EnsureFileCanBeCreated(const std::filesystem::path &file) + { + return FileMgr::CreateFolderIfNotExists(file.parent_path()); + } + + File FileMgr::GetProjectFileImpl(const std::filesystem::path& file) const + { + if (file.is_absolute()) + throw std::invalid_argument("FileMgr::GetProjectFile expects a relative path."); + + auto projFile = File(m_RootFolder / file); + EnsureFileCanBeCreated(file); + + return projFile; + } + + Folder FileMgr::GetProjectFolderImpl(const std::filesystem::path &folder) const + { + if (folder.is_absolute()) + throw std::invalid_argument("FileMgr::GetProjectFolder expects a relative path."); + + return { m_RootFolder / folder }; + } +} \ No newline at end of file diff --git a/src/core/filemgr/FileMgr.hpp b/src/core/filemgr/FileMgr.hpp new file mode 100644 index 0000000..8601d5d --- /dev/null +++ b/src/core/filemgr/FileMgr.hpp @@ -0,0 +1,48 @@ +#pragma once +#include +#include "File.hpp" +#include "Folder.hpp" + +namespace NewBase +{ + class FileMgr final + { + private: + FileMgr() = default; + + public: + virtual ~FileMgr() = default; + + FileMgr(const FileMgr&) = delete; + FileMgr(FileMgr&&) noexcept = delete; + FileMgr& operator=(const FileMgr&) = delete; + FileMgr& operator=(FileMgr&&) noexcept = delete; + + static void Init(const std::filesystem::path& rootFolder); + + [[nodiscard]] static File GetProjectFile(const std::filesystem::path& file) + { return GetInstance().GetProjectFileImpl(file); } + [[nodiscard]] static Folder GetProjectFolder(const std::filesystem::path& folder) + { return GetInstance().GetProjectFolderImpl(folder); } + + static const std::filesystem::path& CreateFolderIfNotExists(const std::filesystem::path& folder); + static const std::filesystem::path& EnsureFileCanBeCreated(const std::filesystem::path& file); + + private: + void InitImpl(const std::filesystem::path& rootFolder); + + File GetProjectFileImpl(const std::filesystem::path& file) const; + Folder GetProjectFolderImpl(const std::filesystem::path& folder) const; + + static FileMgr& GetInstance() + { + static FileMgr i {}; + + return i; + } + + private: + std::filesystem::path m_RootFolder; + + }; +} \ No newline at end of file diff --git a/src/core/filemgr/Folder.cpp b/src/core/filemgr/Folder.cpp new file mode 100644 index 0000000..fd14fcb --- /dev/null +++ b/src/core/filemgr/Folder.cpp @@ -0,0 +1,11 @@ +#include "Folder.hpp" +#include "FileMgr.hpp" + +namespace NewBase +{ + Folder::Folder(const std::filesystem::path& path) : + BaseObj(path) + { + + } +} \ No newline at end of file diff --git a/src/core/filemgr/Folder.hpp b/src/core/filemgr/Folder.hpp new file mode 100644 index 0000000..dff08bd --- /dev/null +++ b/src/core/filemgr/Folder.hpp @@ -0,0 +1,12 @@ +#pragma once +#include "BaseObj.hpp" + +namespace NewBase +{ + class Folder final : public BaseObj + { + public: + Folder(const std::filesystem::path& folder); + + }; +} \ No newline at end of file diff --git a/src/core/hooking/BaseHook.cpp b/src/core/hooking/BaseHook.cpp new file mode 100644 index 0000000..dbd70d1 --- /dev/null +++ b/src/core/hooking/BaseHook.cpp @@ -0,0 +1,34 @@ +#include "BaseHook.hpp" + +#include + +namespace NewBase +{ + BaseHook::BaseHook(const std::string_view name) : + m_Name(name), + m_Enabled(false) + { + m_Hooks.emplace_back(this); + } + + std::vector& BaseHook::Hooks() + { + return m_Hooks; + } + + void BaseHook::EnableAll() + { + for (auto hook : m_Hooks) + { + hook->Enable(); + } + } + + void BaseHook::DisableAll() + { + for (auto hook : m_Hooks) + { + hook->Disable(); + } + } +} \ No newline at end of file diff --git a/src/core/hooking/BaseHook.hpp b/src/core/hooking/BaseHook.hpp new file mode 100644 index 0000000..cd97e93 --- /dev/null +++ b/src/core/hooking/BaseHook.hpp @@ -0,0 +1,63 @@ +#pragma once +#include + +namespace NewBase +{ + class BaseHook + { + private: + const std::string_view m_Name; + + protected: + bool m_Enabled; + + public: + BaseHook(const std::string_view name); + virtual ~BaseHook() = default; + BaseHook(const BaseHook&) = delete; + BaseHook(BaseHook&&) noexcept = delete; + BaseHook& operator=(const BaseHook&) = delete; + BaseHook& operator=(BaseHook&&) noexcept = delete; + + const std::string_view Name() const + { return m_Name; } + inline bool IsEnabled() const + { return m_Enabled; } + + virtual bool Enable() = 0; + virtual bool Disable() = 0; + + public: + template + struct HookHelper + { + inline static BaseHook* m_Hook; + }; + + template + inline static void Add(BaseHook* hook); + template + inline static T* Get(); + + static std::vector& Hooks(); + + static void EnableAll(); + static void DisableAll(); + + private: + inline static std::vector m_Hooks; + + }; + + template + inline void BaseHook::Add(BaseHook* hook) + { + HookHelper::m_Hook = hook; + } + + template + inline T* BaseHook::Get() + { + return reinterpret_cast(HookHelper::m_Hook); + } +} diff --git a/src/core/hooking/DetourHook.hpp b/src/core/hooking/DetourHook.hpp new file mode 100644 index 0000000..46e2c51 --- /dev/null +++ b/src/core/hooking/DetourHook.hpp @@ -0,0 +1,131 @@ +#pragma once +#include "BaseHook.hpp" +#include "core/memory/PointerCalculator.hpp" + +#include +#include + +namespace NewBase +{ + template + class DetourHook : public BaseHook + { + private: + void* m_TargetFunc; + void* m_DetourFunc; + void* m_OriginalFunc; + + public: + DetourHook(const std::string_view name, void* target, T detour); + virtual ~DetourHook(); + + bool Enable(); + bool Disable(); + + bool EnableNow(); + bool DisableNow(); + + T Original() const; + + private: + void OptimizeHook(); + }; + + template + inline DetourHook::DetourHook(const std::string_view name, void* target, T detour) : + BaseHook(name), + m_TargetFunc(target), + m_DetourFunc(detour), + m_OriginalFunc(nullptr) + { + OptimizeHook(); + + if (const auto result = MH_CreateHook(m_TargetFunc, m_DetourFunc, &m_OriginalFunc); result != MH_OK) + { + throw std::runtime_error("Failed to create hook!"); + } + } + + template + inline DetourHook::~DetourHook() + { + DisableNow(); + } + + template + inline bool DetourHook::Enable() + { + if (m_Enabled) + return false; + + if (const auto result = MH_QueueEnableHook(m_TargetFunc); result != MH_OK) + { + throw std::runtime_error("Failed to queue hook to be enabled."); + + return false; + } + return true; + } + + template + inline bool DetourHook::Disable() + { + if (!m_Enabled) + return false; + + if (const auto result = MH_QueueDisableHook(m_TargetFunc); result != MH_OK) + { + throw std::runtime_error("Failed to queue hook to be disable."); + + return false; + } + return true; + } + + template + inline bool DetourHook::EnableNow() + { + if (m_Enabled) + return false; + + if (const auto result = MH_EnableHook(m_TargetFunc); result != MH_OK) + { + throw std::runtime_error("Failed to enable hook right now."); + + return false; + } + return true; + } + + template + inline bool DetourHook::DisableNow() + { + if (!m_Enabled) + return false; + + if (const auto result = MH_DisableHook(m_TargetFunc); result != MH_OK) + { + throw std::runtime_error("Failed to disable hook right now."); + + return false; + } + return true; + } + + template + inline T DetourHook::Original() const + { + return reinterpret_cast(m_OriginalFunc); + } + + template + inline void DetourHook::OptimizeHook() + { + auto ptr = PointerCalculator(m_TargetFunc); + while (ptr.As() == 0xE9) + { + ptr = ptr.Add(1).Rip(); + } + m_TargetFunc = ptr.As(); + } +} \ No newline at end of file diff --git a/src/core/hooking/Hooking.cpp b/src/core/hooking/Hooking.cpp new file mode 100644 index 0000000..1085d5b --- /dev/null +++ b/src/core/hooking/Hooking.cpp @@ -0,0 +1,55 @@ +#include "Hooking.hpp" + +#include "BaseHook.hpp" +#include "DetourHook.hpp" +#include "VMTHook.hpp" +#include "game/hooks/Hooks.hpp" +#include "game/pointers/Pointers.hpp" + +namespace NewBase +{ + Hooking::Hooking() + { + BaseHook::Add(new DetourHook("WndProc", Pointers.WndProc, Window::WndProc)); + + auto swapchain = new VMTHook("SwapChain", *Pointers.SwapChain); + swapchain->Hook(SwapChain::VMTPresentIdx, SwapChain::Present); + swapchain->Hook(SwapChain::VMTResizeBuffersIdx, SwapChain::ResizeBuffers); + BaseHook::Add(swapchain); + } + + Hooking::~Hooking() + { + DestroyImpl(); + } + + bool Hooking::Init() + { + return GetInstance().InitImpl(); + } + + void Hooking::Destroy() + { + GetInstance().DestroyImpl(); + } + + bool Hooking::InitImpl() + { + BaseHook::EnableAll(); + m_MinHook.ApplyQueued(); + + return true; + } + + void Hooking::DestroyImpl() + { + BaseHook::DisableAll(); + m_MinHook.ApplyQueued(); + + for (auto it : BaseHook::Hooks()) + { + delete it; + } + BaseHook::Hooks().clear(); + } +} \ No newline at end of file diff --git a/src/core/hooking/Hooking.hpp b/src/core/hooking/Hooking.hpp new file mode 100644 index 0000000..5c518bd --- /dev/null +++ b/src/core/hooking/Hooking.hpp @@ -0,0 +1,35 @@ +#pragma once +#include "MinHook.hpp" + +namespace NewBase +{ + class Hooking + { + private: + Hooking(); + + MinHook m_MinHook; + + public: + virtual ~Hooking(); + Hooking(const Hooking&) = delete; + Hooking(Hooking&&) noexcept = delete; + Hooking& operator=(const Hooking&) = delete; + Hooking& operator=(Hooking&&) noexcept = delete; + + static bool Init(); + static void Destroy(); + + private: + bool InitImpl(); + void DestroyImpl(); + + private: + static Hooking& GetInstance() + { + static Hooking i{}; + return i; + } + + }; +} \ No newline at end of file diff --git a/src/core/hooking/IATHook.hpp b/src/core/hooking/IATHook.hpp new file mode 100644 index 0000000..6692cf1 --- /dev/null +++ b/src/core/hooking/IATHook.hpp @@ -0,0 +1,60 @@ +#pragma once +#include "BaseHook.hpp" +#include "memory/Module.hpp" +#include "memory/PointerCalculator.hpp" + +namespace NewBase +{ + template + class IATHook : public BaseHook + { + private: + void** m_HookLocation; + void* m_OriginalFunc; + void* m_HookFunc; + + public: + IATHook(const std::string_view name, Module* module, const std::string_view library, const std::string_view import, T detour); + virtual ~IATHook(); + + bool Enable(); + bool Disable(); + + T Original() const; + }; + + template + inline IATHook::IATHook(const std::string_view name, Module* module, const std::string_view library, const std::string_view import, T detour) : + BaseHook(name) + { + m_HookLocation = module->GetImport(library, import); + m_OriginalFunc = *m_HookLocation; + m_HookFunc = (void*)(detour); + } + + template + inline IATHook::~IATHook() + { + Disable(); + } + + template + inline bool IATHook::Enable() + { + *m_HookLocation = m_HookFunc; + return true; + } + + template + inline bool IATHook::Disable() + { + *m_HookLocation = m_OriginalFunc; + return true; + } + + template + inline T IATHook::Original() const + { + return reinterpret_cast(m_OriginalFunc); + } +} diff --git a/src/core/hooking/MinHook.hpp b/src/core/hooking/MinHook.hpp new file mode 100644 index 0000000..5c09232 --- /dev/null +++ b/src/core/hooking/MinHook.hpp @@ -0,0 +1,24 @@ +#pragma once +#include + +namespace NewBase +{ + class MinHook + { + public: + MinHook() + { + MH_Initialize(); + } + + ~MinHook() + { + MH_Uninitialize(); + } + + MH_STATUS ApplyQueued() + { + return MH_ApplyQueued(); + } + }; +} \ No newline at end of file diff --git a/src/core/hooking/VMTHook.hpp b/src/core/hooking/VMTHook.hpp new file mode 100644 index 0000000..873bcfb --- /dev/null +++ b/src/core/hooking/VMTHook.hpp @@ -0,0 +1,102 @@ +#pragma once +#include "BaseHook.hpp" + +#include +#include +#include + +namespace NewBase +{ + template + class VMTHook : public BaseHook + { + private: + std::string_view m_Name; + bool m_Enabled; + + std::array m_NewVMT = {nullptr}; + void** m_OriginalVMT; + void*** m_VMTAddress; + + public: + template + VMTHook(const std::string_view name, T* vmtAddress); + ~VMTHook(); + + virtual bool Enable() override; + virtual bool Disable() override; + + template + inline T Original(const std::uint32_t idx) const; + + template + void Hook(const std::uint32_t idx, T& detour); + void UnHook(const std::uint32_t idx); + + inline constexpr std::size_t VMTSize() const + { + return N; + } + }; + + template + template + inline VMTHook::VMTHook(const std::string_view name, T* vmtAddress) : + BaseHook(name), + m_VMTAddress(reinterpret_cast(vmtAddress)) + { + m_OriginalVMT = *m_VMTAddress; + std::copy_n(m_OriginalVMT, N, m_NewVMT.begin()); + } + + template + inline VMTHook::~VMTHook() + { + Disable(); + } + + template + inline bool VMTHook::Enable() + { + if (m_Enabled) + return false; + + m_Enabled = true; + *m_VMTAddress = m_NewVMT.data(); + return true; + } + + template + inline bool VMTHook::Disable() + { + if (!m_Enabled) + return false; + + *m_VMTAddress = m_OriginalVMT; + m_Enabled = false; + return true; + } + + template + template + inline void VMTHook::Hook(const std::uint32_t idx, T& detour) + { + if (std::is_pointer()) + m_NewVMT[idx] = reinterpret_cast(detour); + else + m_NewVMT[idx] = reinterpret_cast(&detour); + } + + template + inline void VMTHook::UnHook(const std::uint32_t idx) + { + m_NewVMT[idx] = m_OriginalVMT[idx]; + } + + template + template + inline T VMTHook::Original(const std::uint32_t idx) const + { + return reinterpret_cast(m_OriginalVMT[idx]); + } +} \ No newline at end of file diff --git a/src/core/logger/LogColor.hpp b/src/core/logger/LogColor.hpp new file mode 100644 index 0000000..d6ac139 --- /dev/null +++ b/src/core/logger/LogColor.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace NewBase +{ + enum class LogColor + { + RESET, + WHITE = 97, + CYAN = 36, + MAGENTA = 35, + BLUE = 34, + GREEN = 32, + YELLOW = 33, + RED = 31, + BLACK = 30 + }; +} \ No newline at end of file diff --git a/src/core/logger/LogHelper.cpp b/src/core/logger/LogHelper.cpp new file mode 100644 index 0000000..2717158 --- /dev/null +++ b/src/core/logger/LogHelper.cpp @@ -0,0 +1,111 @@ +#include "LogHelper.hpp" + +#include "LogSink.hpp" + +namespace NewBase +{ + void LogHelper::Destroy() + { + GetInstance().DestroyImpl(); + } + + bool LogHelper::Init(const std::string_view consoleName, const std::filesystem::path& file, const bool attachConsole) + { + return GetInstance().InitImpl(consoleName, file, attachConsole); + } + + void LogHelper::ToggleConsole(bool toggle) + { + GetInstance().ToggleConsoleImpl(toggle); + } + + void LogHelper::DestroyImpl() + { + Logger::Destroy(); + CloseOutputStreams(); + + if (m_DidConsoleExist) + SetConsoleMode(m_ConsoleHandle, m_OriginalConsoleMode); + + if (!m_DidConsoleExist && m_AttachConsole) + FreeConsole(); + } + + bool LogHelper::InitImpl(const std::string_view consoleName, const std::filesystem::path& file, const bool attachConsole) + { + m_ConsoleTitle = consoleName; + m_File = file; + m_AttachConsole = attachConsole; + + if (m_AttachConsole) + { + if (m_DidConsoleExist = ::AttachConsole(GetCurrentProcessId()); !m_DidConsoleExist) + AllocConsole(); + + if (m_ConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); m_ConsoleHandle != nullptr) + { + SetConsoleTitleA(m_ConsoleTitle.data()); + SetConsoleOutputCP(CP_UTF8); + + DWORD consoleMode; + GetConsoleMode(m_ConsoleHandle, &consoleMode); + m_OriginalConsoleMode = consoleMode; + + // terminal like behaviour enable full color support + consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; + consoleMode &= ~(ENABLE_QUICK_EDIT_MODE); + + SetConsoleMode(m_ConsoleHandle, consoleMode); + } + } + + OpenOutputStreams(); + + Logger::Init(); + Logger::AddSink([this](LogMessagePtr msg) { + m_ConsoleOut << LogSink::FormatConsole(msg); + + m_ConsoleOut.flush(); + }); + Logger::AddSink([this](LogMessagePtr msg) { + m_FileOut << LogSink::FormatFile(msg); + + m_FileOut.flush(); + }); + + return true; + } + + void LogHelper::ToggleConsoleImpl(bool toggle) + { + if (m_DidConsoleExist) + return; + + if (m_AttachConsole) + { + m_ConsoleOut.close(); + FreeConsole(); + } + else + { + m_ConsoleOut.open("CONOUT$", std::ios_base::out | std::ios_base::app); + AllocConsole(); + } + + m_AttachConsole = !m_AttachConsole; + } + + void LogHelper::CloseOutputStreams() + { + if (m_AttachConsole) + m_ConsoleOut.close(); + m_FileOut.close(); + } + + void LogHelper::OpenOutputStreams() + { + if (m_AttachConsole) + m_ConsoleOut.open("CONOUT$", std::ios_base::out | std::ios_base::app); + m_FileOut.open(m_File, std::ios::out | std::ios::trunc); + } +} \ No newline at end of file diff --git a/src/core/logger/LogHelper.hpp b/src/core/logger/LogHelper.hpp new file mode 100644 index 0000000..939d814 --- /dev/null +++ b/src/core/logger/LogHelper.hpp @@ -0,0 +1,55 @@ +#pragma once +#include +#include +#include + +namespace NewBase +{ +#define ADD_COLOR_TO_STREAM(color) "\x1b[" << int(color) << "m" +#define RESET_STREAM_COLOR "\x1b[0m" +#define HEX(value) "0x" << std::hex << std::uppercase << DWORD64(value) << std::dec << std::nouppercase + + class LogHelper final + { + public: + ~LogHelper() = default; + LogHelper(const LogHelper&) = delete; + LogHelper(LogHelper&&) = delete; + LogHelper& operator=(const LogHelper&) = delete; + LogHelper& operator=(LogHelper&&) = delete; + + static void Destroy(); + static bool Init(const std::string_view consoleName, const std::filesystem::path& file, const bool attachConsole = true); + + static void ToggleConsole(bool toggle); + + private: + LogHelper(){}; + + static LogHelper& GetInstance() + { + static LogHelper i{}; + return i; + } + + void DestroyImpl(); + bool InitImpl(const std::string_view consoleName, const std::filesystem::path& file, const bool attachConsole); + + void ToggleConsoleImpl(bool toggle); + + void CloseOutputStreams(); + void OpenOutputStreams(); + + private: + bool m_AttachConsole; + bool m_DidConsoleExist; + + std::string_view m_ConsoleTitle; + DWORD m_OriginalConsoleMode; + HANDLE m_ConsoleHandle; + + std::ofstream m_ConsoleOut; + std::filesystem::path m_File; + std::ofstream m_FileOut; + }; +} \ No newline at end of file diff --git a/src/core/logger/LogSink.cpp b/src/core/logger/LogSink.cpp new file mode 100644 index 0000000..46c3976 --- /dev/null +++ b/src/core/logger/LogSink.cpp @@ -0,0 +1,57 @@ +#include "LogSink.hpp" + +#include "LogColor.hpp" + +namespace NewBase +{ + LogColor LogSink::GetColor(const eLogLevel level) + { + switch (level) + { + case eLogLevel::VERBOSE: return LogColor::BLUE; + case eLogLevel::INFO: return LogColor::GREEN; + case eLogLevel::WARNING: return LogColor::YELLOW; + case eLogLevel::FATAL: return LogColor::RED; + } + return LogColor::WHITE; + } + + const char* LogSink::GetLevelStr(const eLogLevel level) + { + constexpr std::array levelStrings = {{{"DEBUG"}, {"INFO"}, {"WARN"}, {"ERROR"}}}; + + return levelStrings[level]; + } + + std::string LogSink::FormatConsole(const LogMessagePtr msg) + { + std::stringstream out; + + const auto timestamp = std::format("{0:%H:%M:%S}", msg->Timestamp()); + const auto& location = msg->Location(); + const auto level = msg->Level(); + const auto color = LogSink::GetColor(level); + + const auto file = std::filesystem::path(location.file_name()).filename().string(); + + out << "[" << timestamp << "]" << ADD_COLOR_TO_STREAM(color) << "[" << GetLevelStr(level) << "/" << file << ":" + << location.line() << "] " << RESET_STREAM_COLOR << msg->Message(); + + return out.str(); + } + + std::string LogSink::FormatFile(const LogMessagePtr msg) + { + std::stringstream out; + + const auto timestamp = std::format("{0:%H:%M:%S}", msg->Timestamp()); + const auto& location = msg->Location(); + const auto level = msg->Level(); + const auto file = std::filesystem::path(location.file_name()).filename().string(); + + out << "[" << timestamp << "]" + << "[" << GetLevelStr(level) << "/" << file << ":" << location.line() << "] " << msg->Message(); + + return out.str(); + } +} diff --git a/src/core/logger/LogSink.hpp b/src/core/logger/LogSink.hpp new file mode 100644 index 0000000..b1728b3 --- /dev/null +++ b/src/core/logger/LogSink.hpp @@ -0,0 +1,22 @@ +#pragma once +#include +#include +#include + + +namespace NewBase +{ + using namespace al; + enum class LogColor; + + class LogSink + { + private: + static LogColor GetColor(const eLogLevel level); + static const char* GetLevelStr(const eLogLevel level); + + public: + static std::string FormatConsole(const LogMessagePtr msg); + static std::string FormatFile(const LogMessagePtr msg); + }; +} \ No newline at end of file diff --git a/src/core/memory/BytePatch.cpp b/src/core/memory/BytePatch.cpp new file mode 100644 index 0000000..c672b82 --- /dev/null +++ b/src/core/memory/BytePatch.cpp @@ -0,0 +1,37 @@ +#include "BytePatch.hpp" + +namespace NewBase +{ + BytePatch::~BytePatch() + { + Restore(); + } + + void BytePatch::Apply() const + { + std::copy_n(m_Patch.get(), m_Size, reinterpret_cast(m_Address)); + } + + void BytePatch::Restore() const + { + std::copy_n(m_Original.get(), m_Size, reinterpret_cast(m_Address)); + } + + void BytePatch::Remove() const + { + if (const auto it = std::find(m_Patches.begin(), m_Patches.end(), this); it != m_Patches.end()) + { + m_Patches.erase(it); + } + } + + void BytePatch::RestoreAll() + { + m_Patches.clear(); + } + + bool operator==(const std::unique_ptr& a, const BytePatch* b) + { + return a->m_Address == b->m_Address; + } +} \ No newline at end of file diff --git a/src/core/memory/BytePatch.hpp b/src/core/memory/BytePatch.hpp new file mode 100644 index 0000000..d6b443f --- /dev/null +++ b/src/core/memory/BytePatch.hpp @@ -0,0 +1,90 @@ +#pragma once +#include +#include +#include + +namespace NewBase +{ + template + concept SpanCompatibleType = requires(T a) + { + std::span{a}; + }; + + class BytePatch + { + private: + byte* m_Address; + std::unique_ptr m_Patch; + std::unique_ptr m_Original; + std::size_t m_Size; + + protected: + static inline std::vector> m_Patches; + + public: + virtual ~BytePatch(); + + void Apply() const; + void Restore() const; + void Remove() const; + + static void RestoreAll(); + + template + static const std::unique_ptr& Make(TAddr addr, std::remove_pointer_t> value); + + template + requires SpanCompatibleType + static const std::unique_ptr& Make(TAddr addr, T spanCompatible); + + private: + template + BytePatch(TAddr addr, std::remove_pointer_t> value); + + template + BytePatch(TAddr addr, std::span span); + + friend bool operator==(const std::unique_ptr& a, const BytePatch* b); + + }; + + template + inline const std::unique_ptr& BytePatch::Make(TAddr addr, std::remove_pointer_t> value) + { + return m_Patches.emplace_back(std::unique_ptr(new BytePatch(addr, value))); + } + + template + requires SpanCompatibleType + inline const std::unique_ptr& BytePatch::Make(TAddr addr, T spanCompatible) + { + return m_Patches.emplace_back(std::unique_ptr(new BytePatch(addr, std::span{spanCompatible}))); + } + + template + inline BytePatch::BytePatch(TAddr addr, std::remove_pointer_t> value) : + m_Address(addr) + { + m_Size = sizeof(std::remove_pointer_t>); + + m_Original = std::make_unique(m_Size); + std::copy_n(m_Address, m_Size, m_Original.get()); + + m_Patch = std::make_unique(m_Size); + std::copy_n(&value, m_Size, m_Patch.get()); + } + + template + inline BytePatch::BytePatch(TAddr addr, std::span span) : + m_Address((byte*)addr) + { + m_Size = span.size(); + + m_Original = std::make_unique(m_Size); + std::copy_n(m_Address, m_Size, m_Original.get()); + + m_Patch = std::make_unique(m_Size); + std::copy(span.begin(), span.end(), m_Patch.get()); + } +} \ No newline at end of file diff --git a/src/core/memory/Module.cpp b/src/core/memory/Module.cpp new file mode 100644 index 0000000..2130475 --- /dev/null +++ b/src/core/memory/Module.cpp @@ -0,0 +1,63 @@ +#include "Module.hpp" + +namespace NewBase +{ + Module::Module(LDR_DATA_TABLE_ENTRY* dllEntry) : + m_Path(dllEntry->FullDllName.Buffer), + m_Name(m_Path.filename().string()), + m_Base(dllEntry->DllBase), + m_Size(0) + { + const auto ntHeader = GetNtHeader(); + if (ntHeader) + { + m_Size = ntHeader->OptionalHeader.SizeOfCode; + } + } + + const std::string_view Module::Name() const + { + return m_Name; + } + + void** Module::GetImport(const std::string_view moduleName, const std::string_view symbolName) const + { + const auto ntHeader = GetNtHeader(); + if (!ntHeader) + return nullptr; + + const auto imageDataDirectory = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; + + auto importDescriptor = m_Base.Add(imageDataDirectory.VirtualAddress).As(); + for (; importDescriptor->Name; importDescriptor++) + { + if (const auto modName = m_Base.Add(importDescriptor->Name).As(); strcmp(modName, moduleName.data())) + continue; + + auto firstThunk = m_Base.Add(importDescriptor->FirstThunk).As(); + auto origThunk = m_Base.Add(importDescriptor->OriginalFirstThunk).As(); + for (; origThunk->u1.AddressOfData != 0; firstThunk++, origThunk++) + { + if (const auto importData = m_Base.Add(origThunk->u1.AddressOfData).As(); + strcmp(importData->Name, symbolName.data())) + continue; + return reinterpret_cast(&firstThunk->u1.Function); + } + } + return nullptr; + } + + bool Module::Valid() const + { + return m_Size; + } + + IMAGE_NT_HEADERS* Module::GetNtHeader() const + { + if (!m_Base) + return nullptr; + + const auto offset = m_Base.As()->e_lfanew; + return m_Base.Add(offset).As(); + } +} \ No newline at end of file diff --git a/src/core/memory/Module.hpp b/src/core/memory/Module.hpp new file mode 100644 index 0000000..59c8ddf --- /dev/null +++ b/src/core/memory/Module.hpp @@ -0,0 +1,88 @@ +#pragma once +#include "PointerCalculator.hpp" +#include "common.hpp" + +#include + +namespace NewBase +{ + class Module + { + public: + Module(LDR_DATA_TABLE_ENTRY* dllEntry); + ~Module() = default; + + const std::string_view Name() const; + inline const std::uintptr_t Base() const; + inline const std::uintptr_t Size() const; + inline const std::uintptr_t End() const; + + /** + * @brief Parses the EAT of the module for the requested symbolname + * + * @param symbolName + * @return void* Function address of the exported function + */ + template + T GetExport(const std::string_view symbolName) const; + /** + * @brief Gets the address of the import function + * + * @param moduleName The module to get the import from + * @param symbolName The function name + * @return void** + */ + void** GetImport(const std::string_view moduleName, const std::string_view symbolName) const; + + bool Valid() const; + + private: + IMAGE_NT_HEADERS* GetNtHeader() const; + + private: + const std::filesystem::path m_Path; + const std::string m_Name; + PointerCalculator m_Base; + std::uintptr_t m_Size; + }; + + inline const std::uintptr_t Module::Base() const + { + return m_Base.As(); + } + + inline const std::uintptr_t Module::Size() const + { + return m_Size; + } + + inline const std::uintptr_t Module::End() const + { + return Base() + m_Size; + } + + template + inline T Module::GetExport(const std::string_view symbolName) const + { + const auto ntHeader = GetNtHeader(); + if (!ntHeader) + return nullptr; + + const auto imageDataDirectory = ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; + const auto exportDirectory = m_Base.Add(imageDataDirectory.VirtualAddress).As(); + const auto namesOffsets = m_Base.Add(exportDirectory->AddressOfNames).As(); + const auto funcOffsets = m_Base.Add(exportDirectory->AddressOfFunctions).As(); + + for (std::size_t i = 0; i < exportDirectory->NumberOfFunctions; i++) + { + const auto functionName = m_Base.Add(namesOffsets[i]).As(); + if (strcmp(functionName, symbolName.data())) + continue; + + return m_Base.Add(funcOffsets[i]).As(); + } + + LOG(FATAL) << "Cannot find export: " << symbolName; + return nullptr; + } +} \ No newline at end of file diff --git a/src/core/memory/ModuleMgr.cpp b/src/core/memory/ModuleMgr.cpp new file mode 100644 index 0000000..0596554 --- /dev/null +++ b/src/core/memory/ModuleMgr.cpp @@ -0,0 +1,49 @@ +#include "ModuleMgr.hpp" + +#include "util/Joaat.hpp" + +namespace NewBase +{ + Module* ModuleMgr::Get(const std::string_view name) + { + return Get(Joaat(name)); + } + + Module* ModuleMgr::Get(joaat_t hash) + { + if (const auto& it = m_CachedModules.find(hash); it != m_CachedModules.end()) + { + return it->second.get(); + } + return nullptr; + } + + bool ModuleMgr::LoadModules() + { + const auto peb = reinterpret_cast(NtCurrentTeb()->ProcessEnvironmentBlock); + if (!peb) + return false; + + const auto ldrData = peb->Ldr; + if (!ldrData) + return false; + + const auto moduleList = &ldrData->InMemoryOrderModuleList; + auto moduleEntry = moduleList->Flink; + for (; moduleList != moduleEntry; moduleEntry = moduleEntry->Flink) + { + const auto tableEntry = CONTAINING_RECORD(moduleEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); + if (!tableEntry) + continue; + + if (tableEntry->FullDllName.Buffer) + { + auto module = std::make_unique(tableEntry); + + m_CachedModules.insert({Joaat(module->Name()), std::move(module)}); + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/core/memory/ModuleMgr.hpp b/src/core/memory/ModuleMgr.hpp new file mode 100644 index 0000000..94a99d3 --- /dev/null +++ b/src/core/memory/ModuleMgr.hpp @@ -0,0 +1,35 @@ +#pragma once +#include "Module.hpp" +#include "common.hpp" + +namespace NewBase +{ + using joaat_t = std::uint32_t; + + class ModuleMgr + { + public: + ModuleMgr() = default; + virtual ~ModuleMgr() = default; + ModuleMgr(const ModuleMgr&) = delete; + ModuleMgr(ModuleMgr&&) noexcept = delete; + ModuleMgr& operator=(const ModuleMgr&) = delete; + ModuleMgr& operator=(ModuleMgr&&) noexcept = delete; + + Module* Get(const std::string_view name); + Module* Get(joaat_t hash); + + /** + * @brief Loads the modules from PEB and caches them. + * + * @return true If the peb is found and modules have been cached. + * @return false If the peb or peb->Ldr pointer were invalid. + */ + bool LoadModules(); + + private: + std::unordered_map> m_CachedModules; + }; + + inline NewBase::ModuleMgr ModuleMgr; +} \ No newline at end of file diff --git a/src/core/memory/Pattern.hpp b/src/core/memory/Pattern.hpp new file mode 100644 index 0000000..0c1ec13 --- /dev/null +++ b/src/core/memory/Pattern.hpp @@ -0,0 +1,131 @@ +#pragma once +#include "util/StrToHex.hpp" + +#include + +namespace NewBase +{ + template + struct Signature + { + char m_Signature[N]{}; + std::size_t m_SignatureByteLength; + + constexpr Signature(char const (&signature)[N]) : + m_SignatureByteLength(0) + { + std::ranges::copy(signature, m_Signature); + + for (std::size_t i = 0; i < N; i++) + { + if (m_Signature[i] == ' ' || m_Signature[i] == '\0') + m_SignatureByteLength++; + } + } + + /** + * @brief Signature string + * + * @return constexpr const decltype(m_Signature)& + */ + inline constexpr const decltype(m_Signature)& Get() const + { + return m_Signature; + } + /** + * @brief Returns the Length of the original signature string + * + * @return constexpr std::size_t + */ + constexpr std::size_t Length() const + { + return N; + } + /** + * @brief Returns the actual length in bytes for the signature + * + * @return constexpr std::size_t + */ + constexpr std::size_t ByteLength() const + { + return m_SignatureByteLength; + } + }; + + class IPattern + { + public: + constexpr IPattern() = default; + virtual ~IPattern() = default; + + virtual const std::string_view Name() const = 0; + virtual constexpr std::span> Signature() const = 0; + }; + + template + class Pattern final : public IPattern + { + private: + const std::string_view m_Name; + std::array, S.ByteLength()> m_Signature; + + public: + constexpr Pattern(const std::string_view name); + + inline virtual const std::string_view Name() const override + { + return m_Name; + } + inline virtual constexpr std::span> Signature() const override + { + return m_Signature; + } + + friend std::ostream& operator<< <>(std::ostream& os, const Pattern& signature); + }; + + template + inline constexpr Pattern::Pattern(const std::string_view name) : + IPattern(), + m_Name(name) + { + for (size_t i = 0, pos = 0; i < S.Length(); i++) + { + const auto c = S.Get()[i]; + if (c == ' ') + continue; + if (c == '\0') + break; + + if (S.Get()[i] == '?') + { + if (S.Get()[i + 1] == '?') + i++; + + m_Signature[pos++] = std::nullopt; + + continue; + } + + + m_Signature[pos++] = static_cast(StrToHex(S.Get()[i]) * 0x10 + StrToHex(S.Get()[++i])); + } + } + + template + inline std::ostream& operator<<(std::ostream& os, const Pattern& pattern) + { + os << pattern.Name() << ": { "; + for (const auto byte : pattern.Signature()) + { + if (byte == std::nullopt) + { + os << "?? "; + continue; + } + os << std::hex << std::uppercase << DWORD64(byte.value()) << std::nouppercase << std::dec << ' '; + } + os << "}"; + return os; + } +} \ No newline at end of file diff --git a/src/core/memory/PatternScanner.cpp b/src/core/memory/PatternScanner.cpp new file mode 100644 index 0000000..38e4157 --- /dev/null +++ b/src/core/memory/PatternScanner.cpp @@ -0,0 +1,73 @@ +#include "PatternScanner.hpp" + +#include "Module.hpp" + +#include + +namespace NewBase +{ + PatternScanner::PatternScanner(const Module* module) : + m_Module(module), + m_Patterns() + { + } + + bool PatternScanner::Scan() + { + if (!m_Module || !m_Module->Valid()) + return false; + + std::vector> jobs; + for (const auto& [pattern, func] : m_Patterns) + { + jobs.emplace_back(std::async(&PatternScanner::ScanInternal, this, pattern, func)); + } + + bool scanSuccess = true; + for (auto& job : jobs) + { + job.wait(); + + if (scanSuccess) + scanSuccess = job.get(); + } + if (!scanSuccess) + { + LOG(FATAL) << "Some patterns have not been found, continuing would be foolish."; + } + return scanSuccess; + } + + bool PatternScanner::ScanInternal(const IPattern* pattern, PatternFunc func) const + { + const auto signature = pattern->Signature(); + + for (auto i = m_Module->Base(); i < m_Module->End(); ++i) + { + if (signature.size() + i > m_Module->End()) + break; + + const auto instruction = reinterpret_cast(i); + bool found = true; + for (std::size_t instructionIdx = 0; instructionIdx < signature.size(); ++instructionIdx) + { + if (signature[instructionIdx] && signature[instructionIdx].value() != instruction[instructionIdx]) + { + found = false; + } + } + + if (found) + { + LOG(INFO) << "Found pattern [" << pattern->Name() << "] : [" << HEX(i) << "]"; + + std::invoke(func, i); + + return true; + } + } + + LOG(WARNING) << "Failed to find pattern [" << pattern->Name() << "]"; + return false; + } +} \ No newline at end of file diff --git a/src/core/memory/PatternScanner.hpp b/src/core/memory/PatternScanner.hpp new file mode 100644 index 0000000..0cc3bd9 --- /dev/null +++ b/src/core/memory/PatternScanner.hpp @@ -0,0 +1,35 @@ +#pragma once +#include "Pattern.hpp" +#include "PointerCalculator.hpp" + +#include +#include + +namespace NewBase +{ + class Module; + using PatternFunc = std::function; + + class PatternScanner + { + private: + const Module* m_Module; + std::vector> m_Patterns; + + public: + PatternScanner(const Module* module); + + template + void Add(const Pattern& pattern, const PatternFunc& func); + bool Scan(); + + private: + bool ScanInternal(const IPattern* pattern, PatternFunc func) const; + }; + + template + inline void PatternScanner::Add(const Pattern& pattern, const PatternFunc& func) + { + m_Patterns.push_back(std::move(std::make_pair(&pattern, func))); + } +} diff --git a/src/core/memory/PointerCalculator.hpp b/src/core/memory/PointerCalculator.hpp new file mode 100644 index 0000000..97716c5 --- /dev/null +++ b/src/core/memory/PointerCalculator.hpp @@ -0,0 +1,106 @@ +#pragma once +#include +#include + +namespace NewBase +{ + class PointerCalculator final + { + private: + std::uintptr_t m_InternalPtr; + + public: + inline PointerCalculator(void* ptr = nullptr); + inline PointerCalculator(std::uintptr_t ptr); + template + inline PointerCalculator(T* ptr); + virtual ~PointerCalculator() = default; + + template + std::enable_if_t, T> As() const; + + template + std::enable_if_t, T> As() const; + + template + std::enable_if_t, T> As() const; + + template + PointerCalculator Add(T offset) const; + + template + PointerCalculator Sub(T offset) const; + + PointerCalculator Rip() const; + + explicit operator bool() const; + friend bool operator==(PointerCalculator& a, PointerCalculator& b); + friend bool operator!=(PointerCalculator& a, PointerCalculator& b); + }; + + inline PointerCalculator::PointerCalculator(void* ptr) : + m_InternalPtr(reinterpret_cast(ptr)) + { + } + + inline PointerCalculator::PointerCalculator(std::uintptr_t ptr) : + m_InternalPtr(ptr) + { + } + + template + inline PointerCalculator::PointerCalculator(T* ptr) : + m_InternalPtr(reinterpret_cast(ptr)) + { + } + + template + inline std::enable_if_t, T> PointerCalculator::As() const + { + return reinterpret_cast(m_InternalPtr); + } + + template + inline std::enable_if_t, T> PointerCalculator::As() const + { + return *reinterpret_cast>>(m_InternalPtr); + } + + template + inline std::enable_if_t, T> PointerCalculator::As() const + { + return m_InternalPtr; + } + + template + inline PointerCalculator PointerCalculator::Add(T offset) const + { + return {m_InternalPtr + offset}; + } + + template + inline PointerCalculator PointerCalculator::Sub(T offset) const + { + return {m_InternalPtr - offset}; + } + + inline PointerCalculator PointerCalculator::Rip() const + { + return Add(As()).Add(4); + } + + inline PointerCalculator::operator bool() const + { + return m_InternalPtr != 0; + } + + inline bool operator==(PointerCalculator& a, PointerCalculator& b) + { + return a.m_InternalPtr == b.m_InternalPtr; + } + + inline bool operator!=(PointerCalculator& a, PointerCalculator& b) + { + return a.m_InternalPtr != b.m_InternalPtr; + } +} \ No newline at end of file diff --git a/src/core/renderer/Renderer.cpp b/src/core/renderer/Renderer.cpp new file mode 100644 index 0000000..fd1a406 --- /dev/null +++ b/src/core/renderer/Renderer.cpp @@ -0,0 +1,112 @@ +#include "Renderer.hpp" + +#include "game/pointers/Pointers.hpp" + +#include +#include +#include + +namespace NewBase +{ + Renderer::Renderer() + { + } + + Renderer::~Renderer() + { + } + + void Renderer::DestroyImpl() + { + if (m_SwapChain && m_Device && m_DeviceContext) + { + ImGui_ImplDX11_Shutdown(); + ImGui_ImplWin32_Shutdown(); + ImGui::DestroyContext(); + } + } + + bool Renderer::InitImpl() + { + if (!Pointers.SwapChain) + { + LOG(WARNING) << "SwapChain pointer is invalid!"; + + return false; + } + + if (m_SwapChain = *Pointers.SwapChain; !m_SwapChain) + { + LOG(WARNING) << "Dereferenced SwapChain pointer is invalid!"; + + return false; + } + + if (const auto result = m_SwapChain->GetDevice(__uuidof(ID3D11Device), reinterpret_cast(&m_Device)); result < 0) + { + LOG(WARNING) << "Failed to get D3D Device with result: [" << result << "]"; + + return false; + } + m_Device->GetImmediateContext(&m_DeviceContext); + + ImGui::CreateContext(); + ImGui::StyleColorsDark(); + + // never returns false, useless to check return + ImGui_ImplDX11_Init(m_Device, m_DeviceContext); + ImGui_ImplWin32_Init(Pointers.Hwnd); + + LOG(INFO) << "Renderer has finished initializing."; + return true; + } + + bool Renderer::AddDXCallbackImpl(DXCallback&& callback, std::uint32_t priority) + { + return m_DXCallbacks.insert({priority, callback}).second; + } + + void Renderer::AddWindowProcedureCallbackImpl(WindowProcedureCallback&& callback) + { + return m_WindowProcedureCallbacks.push_back(callback); + } + + void Renderer::OnPresentImpl() + { + Renderer::NewFrame(); + for (const auto& callback : m_DXCallbacks | std::views::values) + callback(); + Renderer::EndFrame(); + } + + LRESULT Renderer::WndProcImpl(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) + { + for (const auto& callback : m_WindowProcedureCallbacks) + callback(hwnd, msg, wparam, lparam); + + return ImGui_ImplWin32_WndProcHandler(hwnd, msg, wparam, lparam); + } + + void Renderer::PreResize() + { + ImGui_ImplDX11_InvalidateDeviceObjects(); + } + + void Renderer::PostResize() + { + ImGui_ImplDX11_CreateDeviceObjects(); + } + + void Renderer::NewFrame() + { + ImGui_ImplDX11_NewFrame(); + ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); + } + + void Renderer::EndFrame() + { + ImGui::Render(); + ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + } +} \ No newline at end of file diff --git a/src/core/renderer/Renderer.hpp b/src/core/renderer/Renderer.hpp new file mode 100644 index 0000000..1ed2dc7 --- /dev/null +++ b/src/core/renderer/Renderer.hpp @@ -0,0 +1,102 @@ +#pragma once +#include "util/Joaat.hpp" + +#include +#include +#include +#include + +namespace NewBase +{ + using DXCallback = std::function; + using WindowProcedureCallback = std::function; + + class Renderer final + { + private: + Renderer(); + + public: + ~Renderer(); + + Renderer(const Renderer&) = delete; + Renderer(Renderer&&) noexcept = delete; + Renderer& operator=(const Renderer&) = delete; + Renderer& operator=(Renderer&&) noexcept = delete; + + static void Destroy() + { + GetInstance().DestroyImpl(); + } + static bool Init() + { + return GetInstance().InitImpl(); + } + + /** + * @brief Add a callback function to draw using ImGui + * + * @param callback Callback function + * @param priority Low values will be drawn before higher values. + * @return true Successfully added callback. + * @return false Duplicate render priority was given. + */ + static bool AddDXCallback(DXCallback&& callback, std::uint32_t priority) + { + return GetInstance().AddDXCallbackImpl(std::move(callback), priority); + } + /** + * @brief Add a callback function to handle Windows WindowProcedure + * + * @param callback Callback function + */ + static void AddWindowProcedureCallback(WindowProcedureCallback&& callback) + { + GetInstance().AddWindowProcedureCallbackImpl(std::move(callback)); + } + + static void OnPresent() + { + GetInstance().OnPresentImpl(); + } + static LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) + { + return GetInstance().WndProcImpl(hwnd, msg, wparam, lparam); + } + + static void PreResize(); + static void PostResize(); + + private: + static void NewFrame(); + static void EndFrame(); + + private: + void DestroyImpl(); + bool InitImpl(); + + bool AddDXCallbackImpl(DXCallback&& callback, std::uint32_t priority); + void AddWindowProcedureCallbackImpl(WindowProcedureCallback&& callback); + + void OnPresentImpl(); + LRESULT WndProcImpl(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + + static Renderer& GetInstance() + { + static Renderer i{}; + + return i; + } + + private: + ID3D11Device* m_Device; + ID3D11DeviceContext* m_DeviceContext; + IDXGISwapChain* m_SwapChain; + + std::map m_DXCallbacks; + std::vector m_WindowProcedureCallbacks; + }; +} + +// Make our linker aware of the ImGui WndProcHandler +extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); \ No newline at end of file diff --git a/src/game/frontend/GUI.cpp b/src/game/frontend/GUI.cpp new file mode 100644 index 0000000..aceebe9 --- /dev/null +++ b/src/game/frontend/GUI.cpp @@ -0,0 +1,44 @@ +#include "GUI.hpp" + +#include "menu/Menu.hpp" +#include "core/renderer/Renderer.hpp" + +namespace NewBase +{ + GUI::GUI() : + m_IsOpen(false) + { + Renderer::AddDXCallback(Menu::Main, -1); + Renderer::AddWindowProcedureCallback([this](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + GUI::WndProc(hwnd, msg, wparam, lparam); + }); + } + + GUI::~GUI() + { + } + + void GUI::ToggleMouse() + { + auto& io = ImGui::GetIO(); + if (GUI::IsOpen()) + { + io.MouseDrawCursor = true; + io.ConfigFlags &= ~ImGuiConfigFlags_NoMouse; + } + else + { + io.MouseDrawCursor = false; + io.ConfigFlags |= ImGuiConfigFlags_NoMouse; + } + } + + void GUI::WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) + { + if (msg == WM_KEYUP && wparam == VK_INSERT) + { + m_IsOpen = !m_IsOpen; + ToggleMouse(); + } + } +} \ No newline at end of file diff --git a/src/game/frontend/GUI.hpp b/src/game/frontend/GUI.hpp new file mode 100644 index 0000000..763128b --- /dev/null +++ b/src/game/frontend/GUI.hpp @@ -0,0 +1,43 @@ +#pragma once +#include + +namespace NewBase +{ + class GUI final + { + private: + GUI(); + + public: + ~GUI(); + + GUI(const GUI&) = delete; + GUI(GUI&&) noexcept = delete; + GUI& operator=(const GUI&) = delete; + GUI& operator=(GUI&&) noexcept = delete; + + static void Init() + { + GetInstance(); + } + + static bool IsOpen() + { + return GetInstance().m_IsOpen; + } + + private: + void WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + + static void ToggleMouse(); + static GUI& GetInstance() + { + static GUI i{}; + + return i; + } + + private: + bool m_IsOpen; + }; +} \ No newline at end of file diff --git a/src/game/frontend/menu/Menu.cpp b/src/game/frontend/menu/Menu.cpp new file mode 100644 index 0000000..57e7a2a --- /dev/null +++ b/src/game/frontend/menu/Menu.cpp @@ -0,0 +1,17 @@ +#include "Menu.hpp" + +namespace NewBase +{ + void Menu::Main() + { + if (!GUI::IsOpen()) + return; + + if (ImGui::Begin("Test")) + { + if (ImGui::Button("Unload")) + g_Running = false; + } + ImGui::End(); + } +} \ No newline at end of file diff --git a/src/game/frontend/menu/Menu.hpp b/src/game/frontend/menu/Menu.hpp new file mode 100644 index 0000000..23f986a --- /dev/null +++ b/src/game/frontend/menu/Menu.hpp @@ -0,0 +1,7 @@ +#pragma once +#include "game/frontend/GUI.hpp" + +namespace NewBase::Menu +{ + extern void Main(); +} \ No newline at end of file diff --git a/src/game/hooks/GUI/SwapChain.cpp b/src/game/hooks/GUI/SwapChain.cpp new file mode 100644 index 0000000..f77e401 --- /dev/null +++ b/src/game/hooks/GUI/SwapChain.cpp @@ -0,0 +1,27 @@ +#include "core/hooking/VMTHook.hpp" +#include "core/renderer/Renderer.hpp" +#include "game/hooks/Hooks.hpp" + +namespace NewBase +{ + HRESULT SwapChain::Present(IDXGISwapChain* that, UINT syncInterval, UINT flags) + { + if (g_Running) + { + Renderer::OnPresent(); + } + return BaseHook::Get>()->Original(SwapChain::VMTPresentIdx)(that, syncInterval, flags); + } + + HRESULT SwapChain::ResizeBuffers(IDXGISwapChain* that, UINT bufferCount, UINT width, UINT height, DXGI_FORMAT newFormat, UINT swapChainFlags) + { + if (g_Running) + { + Renderer::PreResize(); + const auto result = BaseHook::Get>()->Original(SwapChain::VMTResizeBuffersIdx)(that, bufferCount, width, height, newFormat, swapChainFlags); + Renderer::PostResize(); + return result; + } + return BaseHook::Get>()->Original(SwapChain::VMTResizeBuffersIdx)(that, bufferCount, width, height, newFormat, swapChainFlags); + } +} \ No newline at end of file diff --git a/src/game/hooks/GUI/Window.cpp b/src/game/hooks/GUI/Window.cpp new file mode 100644 index 0000000..11346f9 --- /dev/null +++ b/src/game/hooks/GUI/Window.cpp @@ -0,0 +1,18 @@ +#include "core/hooking/DetourHook.hpp" +#include "core/renderer/Renderer.hpp" +#include "game/hooks/Hooks.hpp" + + +namespace NewBase +{ + LRESULT Window::WndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) + { + if (g_Running) + Renderer::WndProc(hwnd, umsg, wparam, lparam); + + if (umsg == WM_KEYUP && wparam == VK_DELETE) // fallback to unload + g_Running = false; + + return BaseHook::Get>()->Original()(hwnd, umsg, wparam, lparam); + } +} \ No newline at end of file diff --git a/src/game/hooks/Hooks.hpp b/src/game/hooks/Hooks.hpp new file mode 100644 index 0000000..280f352 --- /dev/null +++ b/src/game/hooks/Hooks.hpp @@ -0,0 +1,19 @@ +#pragma once +#include + +namespace NewBase +{ + namespace SwapChain + { + constexpr auto VMTPresentIdx = 8; + constexpr auto VMTResizeBuffersIdx = 13; + constexpr auto VMTSize = 19; + extern HRESULT Present(IDXGISwapChain* that, UINT syncInterval, UINT flags); + extern HRESULT ResizeBuffers(IDXGISwapChain* that, UINT bufferCount, UINT width, UINT height, DXGI_FORMAT newFormat, UINT swapChainFlags); + } + + namespace Window + { + extern LRESULT WndProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam); + } +} \ No newline at end of file diff --git a/src/game/pointers/Pointers.cpp b/src/game/pointers/Pointers.cpp new file mode 100644 index 0000000..eb234a0 --- /dev/null +++ b/src/game/pointers/Pointers.cpp @@ -0,0 +1,55 @@ +#include "Pointers.hpp" + +#include "core/memory/BytePatch.hpp" +#include "core/memory/ModuleMgr.hpp" +#include "core/memory/PatternScanner.hpp" +#include "util/Joaat.hpp" + +namespace NewBase +{ + bool Pointers::Init() + { + const auto gta5 = ModuleMgr.Get("GTA5.exe"_J); + if (!gta5) + { + LOG(FATAL) << "Could not find " << gta5->Name() << ", is this GTA5?"; + + return false; + } + + auto scanner = PatternScanner(gta5); + + constexpr auto swapchainPtrn = Pattern<"48 8B 0D ? ? ? ? 48 8B 01 44 8D 43 01 33 D2 FF 50 40 8B C8">("IDXGISwapChain"); + scanner.Add(swapchainPtrn, [this](PointerCalculator ptr) { + SwapChain = ptr.Add(3).Rip().As(); + }); + + constexpr auto wndProcPtrn = Pattern<"48 8B C4 48 89 58 08 4C 89 48 20 55 56 57 41 54 41 55 41 56 41 57 48 8D 68 A1 48 81 EC F0">("WNDPROC"); + scanner.Add(wndProcPtrn, [this](PointerCalculator ptr) { + WndProc = ptr.As(); + }); + + constexpr auto debugTrap = Pattern<"48 83 EC 28 33 C9 FF 15 ? ? ? ? 45 33 C9">("DebugTrap"); + scanner.Add(debugTrap, [this](PointerCalculator ptr) { + BytePatch::Make(ptr.As(), std::to_array({ 0xC3, 0x90, 0x90, 0x90 }))->Apply(); + + UnhookWindowsHookEx(*ptr.Add(45).Rip().As()); + }); + + if (!scanner.Scan()) + { + LOG(FATAL) << "Some patterns could not be found, unloading."; + + return false; + } + + if (Hwnd = FindWindow("grcWindow", nullptr); !Hwnd) + { + LOG(FATAL) << "Failed to grab game window, unloading."; + + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/src/game/pointers/Pointers.hpp b/src/game/pointers/Pointers.hpp new file mode 100644 index 0000000..9195ffa --- /dev/null +++ b/src/game/pointers/Pointers.hpp @@ -0,0 +1,20 @@ +#pragma once +#include +#include + +namespace NewBase +{ + struct PointerData + { + IDXGISwapChain** SwapChain; + HWND Hwnd; + WNDPROC WndProc; + }; + + struct Pointers : PointerData + { + bool Init(); + }; + + inline NewBase::Pointers Pointers; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..f005635 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,58 @@ +#include "common.hpp" +#include "core/filemgr/FileMgr.hpp" +#include "core/hooking/Hooking.hpp" +#include "core/memory/ModuleMgr.hpp" +#include "core/renderer/Renderer.hpp" +#include "game/frontend/GUI.hpp" +#include "game/pointers/Pointers.hpp" + + +namespace NewBase +{ + DWORD Main(void*) + { + const auto documents = std::filesystem::path(std::getenv("USERPROFILE")) / "Documents"; + FileMgr::Init(documents / "HellBase"); + + LogHelper::Init("henlo", FileMgr::GetProjectFile("./cout.log").Path()); + + if (!ModuleMgr.LoadModules()) + goto unload; + if (!Pointers.Init()) + goto unload; + if (!Renderer::Init()) + goto unload; + GUI::Init(); + Hooking::Init(); + + while (g_Running) + { + std::this_thread::sleep_for(100ms); + } + + unload: + Hooking::Destroy(); + Renderer::Destroy(); + LogHelper::Destroy(); + + CloseHandle(g_MainThread); + FreeLibraryAndExitThread(g_DllInstance, EXIT_SUCCESS); + + return EXIT_SUCCESS; + } +} + +BOOL WINAPI DllMain(HINSTANCE dllInstance, DWORD reason, void*) +{ + using namespace NewBase; + + DisableThreadLibraryCalls(dllInstance); + + if (reason == DLL_PROCESS_ATTACH) + { + g_DllInstance = dllInstance; + + g_MainThread = CreateThread(nullptr, 0, Main, nullptr, 0, &g_MainThreadId); + } + return true; +} \ No newline at end of file diff --git a/src/util/Joaat.cpp b/src/util/Joaat.cpp new file mode 100644 index 0000000..c0f682a --- /dev/null +++ b/src/util/Joaat.cpp @@ -0,0 +1,19 @@ +#include "Joaat.hpp" + +namespace NewBase +{ + constexpr joaat_t Joaat(const std::string_view str) + { + joaat_t hash = 0; + for (auto c : str) + { + hash += ToLower(c); + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; + } +} \ No newline at end of file diff --git a/src/util/Joaat.hpp b/src/util/Joaat.hpp new file mode 100644 index 0000000..54ee3cf --- /dev/null +++ b/src/util/Joaat.hpp @@ -0,0 +1,32 @@ +#pragma once +#include +#include + +namespace NewBase +{ + using joaat_t = std::uint32_t; + + inline constexpr char ToLower(char c) + { + return c >= 'A' && c <= 'Z' ? c | 1 << 5 : c; + } + + extern constexpr joaat_t Joaat(const std::string_view str); + inline consteval joaat_t operator""_J(const char* s, std::size_t n) + { + joaat_t result = 0; + + for (std::size_t i = 0; i < n; i++) + { + result += ToLower(s[i]); + result += (result << 10); + result ^= (result >> 6); + } + + result += (result << 3); + result ^= (result >> 11); + result += (result << 15); + + return result; + } +} \ No newline at end of file diff --git a/src/util/StrToHex.hpp b/src/util/StrToHex.hpp new file mode 100644 index 0000000..370cea6 --- /dev/null +++ b/src/util/StrToHex.hpp @@ -0,0 +1,16 @@ +#pragma once +#include + +namespace NewBase +{ + inline constexpr std::uint8_t StrToHex(const char& ch) noexcept + { + if (ch >= '0' && ch <= '9') + return ch - '0'; + + if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + + return ch - 'a' + 10; + } +} \ No newline at end of file