mirror of
https://github.com/YimMenu/YimMenuV2.git
synced 2026-10-10 03:43:15 +08:00
Initial commit
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
build/
|
||||
Vendored
+14
@@ -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}"
|
||||
}
|
||||
]
|
||||
}
|
||||
Vendored
+81
@@ -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"
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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})
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "common.hpp"
|
||||
|
||||
namespace NewBase
|
||||
{
|
||||
std::atomic<bool> g_Running{true};
|
||||
HINSTANCE g_DllInstance{nullptr};
|
||||
HANDLE g_MainThread{INVALID_HANDLE_VALUE};
|
||||
DWORD g_MainThreadId = -1;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <AsyncLogger/Logger.hpp>
|
||||
#include <MinHook.h>
|
||||
#include <Windows.h>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <imgui.h>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using namespace al;
|
||||
#include "core/logger/LogHelper.hpp"
|
||||
|
||||
namespace NewBase
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
extern std::atomic<bool> g_Running;
|
||||
extern HINSTANCE g_DllInstance;
|
||||
extern HANDLE g_MainThread;
|
||||
extern DWORD g_MainThreadId;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include <filesystem>
|
||||
|
||||
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;
|
||||
|
||||
};
|
||||
}
|
||||
@@ -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 };
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
};
|
||||
}
|
||||
@@ -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 };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include <filesystem>
|
||||
#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;
|
||||
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "Folder.hpp"
|
||||
#include "FileMgr.hpp"
|
||||
|
||||
namespace NewBase
|
||||
{
|
||||
Folder::Folder(const std::filesystem::path& path) :
|
||||
BaseObj(path)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "BaseObj.hpp"
|
||||
|
||||
namespace NewBase
|
||||
{
|
||||
class Folder final : public BaseObj
|
||||
{
|
||||
public:
|
||||
Folder(const std::filesystem::path& folder);
|
||||
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#include "BaseHook.hpp"
|
||||
|
||||
#include <MinHook.h>
|
||||
|
||||
namespace NewBase
|
||||
{
|
||||
BaseHook::BaseHook(const std::string_view name) :
|
||||
m_Name(name),
|
||||
m_Enabled(false)
|
||||
{
|
||||
m_Hooks.emplace_back(this);
|
||||
}
|
||||
|
||||
std::vector<BaseHook*>& 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
#include <string_view>
|
||||
|
||||
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<auto HookFunc>
|
||||
struct HookHelper
|
||||
{
|
||||
inline static BaseHook* m_Hook;
|
||||
};
|
||||
|
||||
template<auto HookFunc>
|
||||
inline static void Add(BaseHook* hook);
|
||||
template<auto HookFunc, typename T>
|
||||
inline static T* Get();
|
||||
|
||||
static std::vector<BaseHook*>& Hooks();
|
||||
|
||||
static void EnableAll();
|
||||
static void DisableAll();
|
||||
|
||||
private:
|
||||
inline static std::vector<BaseHook*> m_Hooks;
|
||||
|
||||
};
|
||||
|
||||
template<auto HookFunc>
|
||||
inline void BaseHook::Add(BaseHook* hook)
|
||||
{
|
||||
HookHelper<HookFunc>::m_Hook = hook;
|
||||
}
|
||||
|
||||
template<auto HookFunc, typename T>
|
||||
inline T* BaseHook::Get()
|
||||
{
|
||||
return reinterpret_cast<T*>(HookHelper<HookFunc>::m_Hook);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
#include "BaseHook.hpp"
|
||||
#include "core/memory/PointerCalculator.hpp"
|
||||
|
||||
#include <MinHook.h>
|
||||
#include <string_view>
|
||||
|
||||
namespace NewBase
|
||||
{
|
||||
template<typename T = int*>
|
||||
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<typename T>
|
||||
inline DetourHook<T>::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<typename T>
|
||||
inline DetourHook<T>::~DetourHook()
|
||||
{
|
||||
DisableNow();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool DetourHook<T>::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<typename T>
|
||||
inline bool DetourHook<T>::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<typename T>
|
||||
inline bool DetourHook<T>::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<typename T>
|
||||
inline bool DetourHook<T>::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<typename T>
|
||||
inline T DetourHook<T>::Original() const
|
||||
{
|
||||
return reinterpret_cast<T>(m_OriginalFunc);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void DetourHook<T>::OptimizeHook()
|
||||
{
|
||||
auto ptr = PointerCalculator(m_TargetFunc);
|
||||
while (ptr.As<std::uint8_t&>() == 0xE9)
|
||||
{
|
||||
ptr = ptr.Add(1).Rip();
|
||||
}
|
||||
m_TargetFunc = ptr.As<void*>();
|
||||
}
|
||||
}
|
||||
@@ -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<Window::WndProc>(new DetourHook("WndProc", Pointers.WndProc, Window::WndProc));
|
||||
|
||||
auto swapchain = new VMTHook<SwapChain::VMTSize>("SwapChain", *Pointers.SwapChain);
|
||||
swapchain->Hook(SwapChain::VMTPresentIdx, SwapChain::Present);
|
||||
swapchain->Hook(SwapChain::VMTResizeBuffersIdx, SwapChain::ResizeBuffers);
|
||||
BaseHook::Add<SwapChain::Present>(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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
#include "BaseHook.hpp"
|
||||
#include "memory/Module.hpp"
|
||||
#include "memory/PointerCalculator.hpp"
|
||||
|
||||
namespace NewBase
|
||||
{
|
||||
template<typename T = int*>
|
||||
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<typename T>
|
||||
inline IATHook<T>::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<typename T>
|
||||
inline IATHook<T>::~IATHook()
|
||||
{
|
||||
Disable();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool IATHook<T>::Enable()
|
||||
{
|
||||
*m_HookLocation = m_HookFunc;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool IATHook<T>::Disable()
|
||||
{
|
||||
*m_HookLocation = m_OriginalFunc;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T IATHook<T>::Original() const
|
||||
{
|
||||
return reinterpret_cast<T>(m_OriginalFunc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <MinHook.h>
|
||||
|
||||
namespace NewBase
|
||||
{
|
||||
class MinHook
|
||||
{
|
||||
public:
|
||||
MinHook()
|
||||
{
|
||||
MH_Initialize();
|
||||
}
|
||||
|
||||
~MinHook()
|
||||
{
|
||||
MH_Uninitialize();
|
||||
}
|
||||
|
||||
MH_STATUS ApplyQueued()
|
||||
{
|
||||
return MH_ApplyQueued();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
#include "BaseHook.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
|
||||
namespace NewBase
|
||||
{
|
||||
template<std::size_t N>
|
||||
class VMTHook : public BaseHook
|
||||
{
|
||||
private:
|
||||
std::string_view m_Name;
|
||||
bool m_Enabled;
|
||||
|
||||
std::array<void*, N> m_NewVMT = {nullptr};
|
||||
void** m_OriginalVMT;
|
||||
void*** m_VMTAddress;
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
VMTHook(const std::string_view name, T* vmtAddress);
|
||||
~VMTHook();
|
||||
|
||||
virtual bool Enable() override;
|
||||
virtual bool Disable() override;
|
||||
|
||||
template<typename T>
|
||||
inline T Original(const std::uint32_t idx) const;
|
||||
|
||||
template<typename T>
|
||||
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<std::size_t N>
|
||||
template<typename T>
|
||||
inline VMTHook<N>::VMTHook(const std::string_view name, T* vmtAddress) :
|
||||
BaseHook(name),
|
||||
m_VMTAddress(reinterpret_cast<void***>(vmtAddress))
|
||||
{
|
||||
m_OriginalVMT = *m_VMTAddress;
|
||||
std::copy_n(m_OriginalVMT, N, m_NewVMT.begin());
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
inline VMTHook<N>::~VMTHook()
|
||||
{
|
||||
Disable();
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
inline bool VMTHook<N>::Enable()
|
||||
{
|
||||
if (m_Enabled)
|
||||
return false;
|
||||
|
||||
m_Enabled = true;
|
||||
*m_VMTAddress = m_NewVMT.data();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
inline bool VMTHook<N>::Disable()
|
||||
{
|
||||
if (!m_Enabled)
|
||||
return false;
|
||||
|
||||
*m_VMTAddress = m_OriginalVMT;
|
||||
m_Enabled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
template<typename T>
|
||||
inline void VMTHook<N>::Hook(const std::uint32_t idx, T& detour)
|
||||
{
|
||||
if (std::is_pointer<T>())
|
||||
m_NewVMT[idx] = reinterpret_cast<void*>(detour);
|
||||
else
|
||||
m_NewVMT[idx] = reinterpret_cast<void*>(&detour);
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
inline void VMTHook<N>::UnHook(const std::uint32_t idx)
|
||||
{
|
||||
m_NewVMT[idx] = m_OriginalVMT[idx];
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
template<typename T>
|
||||
inline T VMTHook<N>::Original(const std::uint32_t idx) const
|
||||
{
|
||||
return reinterpret_cast<T>(m_OriginalVMT[idx]);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
@@ -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<const char*, 4> 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include <AsyncLogger/Logger.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
@@ -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<byte*>(m_Address));
|
||||
}
|
||||
|
||||
void BytePatch::Restore() const
|
||||
{
|
||||
std::copy_n(m_Original.get(), m_Size, reinterpret_cast<byte*>(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<BytePatch>& a, const BytePatch* b)
|
||||
{
|
||||
return a->m_Address == b->m_Address;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace NewBase
|
||||
{
|
||||
template<typename T>
|
||||
concept SpanCompatibleType = requires(T a)
|
||||
{
|
||||
std::span{a};
|
||||
};
|
||||
|
||||
class BytePatch
|
||||
{
|
||||
private:
|
||||
byte* m_Address;
|
||||
std::unique_ptr<byte[]> m_Patch;
|
||||
std::unique_ptr<byte[]> m_Original;
|
||||
std::size_t m_Size;
|
||||
|
||||
protected:
|
||||
static inline std::vector<std::unique_ptr<BytePatch>> m_Patches;
|
||||
|
||||
public:
|
||||
virtual ~BytePatch();
|
||||
|
||||
void Apply() const;
|
||||
void Restore() const;
|
||||
void Remove() const;
|
||||
|
||||
static void RestoreAll();
|
||||
|
||||
template<typename TAddr>
|
||||
static const std::unique_ptr<BytePatch>& Make(TAddr addr, std::remove_pointer_t<std::remove_reference_t<TAddr>> value);
|
||||
|
||||
template<typename TAddr, typename T>
|
||||
requires SpanCompatibleType<T>
|
||||
static const std::unique_ptr<BytePatch>& Make(TAddr addr, T spanCompatible);
|
||||
|
||||
private:
|
||||
template<typename TAddr>
|
||||
BytePatch(TAddr addr, std::remove_pointer_t<std::remove_reference_t<TAddr>> value);
|
||||
|
||||
template<typename TAddr, typename T, std::size_t N>
|
||||
BytePatch(TAddr addr, std::span<T, N> span);
|
||||
|
||||
friend bool operator==(const std::unique_ptr<BytePatch>& a, const BytePatch* b);
|
||||
|
||||
};
|
||||
|
||||
template<typename TAddr>
|
||||
inline const std::unique_ptr<BytePatch>& BytePatch::Make(TAddr addr, std::remove_pointer_t<std::remove_reference_t<TAddr>> value)
|
||||
{
|
||||
return m_Patches.emplace_back(std::unique_ptr<BytePatch>(new BytePatch(addr, value)));
|
||||
}
|
||||
|
||||
template<typename TAddr, typename T>
|
||||
requires SpanCompatibleType<T>
|
||||
inline const std::unique_ptr<BytePatch>& BytePatch::Make(TAddr addr, T spanCompatible)
|
||||
{
|
||||
return m_Patches.emplace_back(std::unique_ptr<BytePatch>(new BytePatch(addr, std::span{spanCompatible})));
|
||||
}
|
||||
|
||||
template<typename TAddr>
|
||||
inline BytePatch::BytePatch(TAddr addr, std::remove_pointer_t<std::remove_reference_t<TAddr>> value) :
|
||||
m_Address(addr)
|
||||
{
|
||||
m_Size = sizeof(std::remove_pointer_t<std::remove_reference_t<TAddr>>);
|
||||
|
||||
m_Original = std::make_unique<byte[]>(m_Size);
|
||||
std::copy_n(m_Address, m_Size, m_Original.get());
|
||||
|
||||
m_Patch = std::make_unique<byte[]>(m_Size);
|
||||
std::copy_n(&value, m_Size, m_Patch.get());
|
||||
}
|
||||
|
||||
template<typename TAddr, typename T, std::size_t N>
|
||||
inline BytePatch::BytePatch(TAddr addr, std::span<T, N> span) :
|
||||
m_Address((byte*)addr)
|
||||
{
|
||||
m_Size = span.size();
|
||||
|
||||
m_Original = std::make_unique<byte[]>(m_Size);
|
||||
std::copy_n(m_Address, m_Size, m_Original.get());
|
||||
|
||||
m_Patch = std::make_unique<byte[]>(m_Size);
|
||||
std::copy(span.begin(), span.end(), m_Patch.get());
|
||||
}
|
||||
}
|
||||
@@ -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<IMAGE_IMPORT_DESCRIPTOR*>();
|
||||
for (; importDescriptor->Name; importDescriptor++)
|
||||
{
|
||||
if (const auto modName = m_Base.Add(importDescriptor->Name).As<const char*>(); strcmp(modName, moduleName.data()))
|
||||
continue;
|
||||
|
||||
auto firstThunk = m_Base.Add(importDescriptor->FirstThunk).As<IMAGE_THUNK_DATA*>();
|
||||
auto origThunk = m_Base.Add(importDescriptor->OriginalFirstThunk).As<IMAGE_THUNK_DATA*>();
|
||||
for (; origThunk->u1.AddressOfData != 0; firstThunk++, origThunk++)
|
||||
{
|
||||
if (const auto importData = m_Base.Add(origThunk->u1.AddressOfData).As<IMAGE_IMPORT_BY_NAME*>();
|
||||
strcmp(importData->Name, symbolName.data()))
|
||||
continue;
|
||||
return reinterpret_cast<void**>(&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<IMAGE_DOS_HEADER*>()->e_lfanew;
|
||||
return m_Base.Add(offset).As<IMAGE_NT_HEADERS*>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
#include "PointerCalculator.hpp"
|
||||
#include "common.hpp"
|
||||
|
||||
#include <winternl.h>
|
||||
|
||||
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<typename T = void*>
|
||||
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<std::uintptr_t>();
|
||||
}
|
||||
|
||||
inline const std::uintptr_t Module::Size() const
|
||||
{
|
||||
return m_Size;
|
||||
}
|
||||
|
||||
inline const std::uintptr_t Module::End() const
|
||||
{
|
||||
return Base() + m_Size;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
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<IMAGE_EXPORT_DIRECTORY*>();
|
||||
const auto namesOffsets = m_Base.Add(exportDirectory->AddressOfNames).As<DWORD*>();
|
||||
const auto funcOffsets = m_Base.Add(exportDirectory->AddressOfFunctions).As<DWORD*>();
|
||||
|
||||
for (std::size_t i = 0; i < exportDirectory->NumberOfFunctions; i++)
|
||||
{
|
||||
const auto functionName = m_Base.Add(namesOffsets[i]).As<const char*>();
|
||||
if (strcmp(functionName, symbolName.data()))
|
||||
continue;
|
||||
|
||||
return m_Base.Add(funcOffsets[i]).As<T>();
|
||||
}
|
||||
|
||||
LOG(FATAL) << "Cannot find export: " << symbolName;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -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<PPEB>(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<Module>(tableEntry);
|
||||
|
||||
m_CachedModules.insert({Joaat(module->Name()), std::move(module)});
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -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<std::uint32_t, std::unique_ptr<Module>> m_CachedModules;
|
||||
};
|
||||
|
||||
inline NewBase::ModuleMgr ModuleMgr;
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
#pragma once
|
||||
#include "util/StrToHex.hpp"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace NewBase
|
||||
{
|
||||
template<std::size_t N>
|
||||
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<const std::optional<std::uint8_t>> Signature() const = 0;
|
||||
};
|
||||
|
||||
template<Signature S>
|
||||
class Pattern final : public IPattern
|
||||
{
|
||||
private:
|
||||
const std::string_view m_Name;
|
||||
std::array<std::optional<std::uint8_t>, 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<const std::optional<std::uint8_t>> Signature() const override
|
||||
{
|
||||
return m_Signature;
|
||||
}
|
||||
|
||||
friend std::ostream& operator<< <>(std::ostream& os, const Pattern<S>& signature);
|
||||
};
|
||||
|
||||
template<Signature S>
|
||||
inline constexpr Pattern<S>::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<std::uint8_t>(StrToHex(S.Get()[i]) * 0x10 + StrToHex(S.Get()[++i]));
|
||||
}
|
||||
}
|
||||
|
||||
template<Signature S>
|
||||
inline std::ostream& operator<<(std::ostream& os, const Pattern<S>& 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#include "PatternScanner.hpp"
|
||||
|
||||
#include "Module.hpp"
|
||||
|
||||
#include <future>
|
||||
|
||||
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<std::future<bool>> 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<std::uint8_t*>(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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#include "Pattern.hpp"
|
||||
#include "PointerCalculator.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace NewBase
|
||||
{
|
||||
class Module;
|
||||
using PatternFunc = std::function<void(PointerCalculator)>;
|
||||
|
||||
class PatternScanner
|
||||
{
|
||||
private:
|
||||
const Module* m_Module;
|
||||
std::vector<std::pair<const IPattern*, PatternFunc>> m_Patterns;
|
||||
|
||||
public:
|
||||
PatternScanner(const Module* module);
|
||||
|
||||
template<Signature S>
|
||||
void Add(const Pattern<S>& pattern, const PatternFunc& func);
|
||||
bool Scan();
|
||||
|
||||
private:
|
||||
bool ScanInternal(const IPattern* pattern, PatternFunc func) const;
|
||||
};
|
||||
|
||||
template<Signature S>
|
||||
inline void PatternScanner::Add(const Pattern<S>& pattern, const PatternFunc& func)
|
||||
{
|
||||
m_Patterns.push_back(std::move(std::make_pair(&pattern, func)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace NewBase
|
||||
{
|
||||
class PointerCalculator final
|
||||
{
|
||||
private:
|
||||
std::uintptr_t m_InternalPtr;
|
||||
|
||||
public:
|
||||
inline PointerCalculator(void* ptr = nullptr);
|
||||
inline PointerCalculator(std::uintptr_t ptr);
|
||||
template<typename T>
|
||||
inline PointerCalculator(T* ptr);
|
||||
virtual ~PointerCalculator() = default;
|
||||
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_pointer_v<T>, T> As() const;
|
||||
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_lvalue_reference_v<T>, T> As() const;
|
||||
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_same_v<T, std::uintptr_t>, T> As() const;
|
||||
|
||||
template<typename T>
|
||||
PointerCalculator Add(T offset) const;
|
||||
|
||||
template<typename T>
|
||||
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<std::uintptr_t>(ptr))
|
||||
{
|
||||
}
|
||||
|
||||
inline PointerCalculator::PointerCalculator(std::uintptr_t ptr) :
|
||||
m_InternalPtr(ptr)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline PointerCalculator::PointerCalculator(T* ptr) :
|
||||
m_InternalPtr(reinterpret_cast<std::uintptr_t>(ptr))
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::enable_if_t<std::is_pointer_v<T>, T> PointerCalculator::As() const
|
||||
{
|
||||
return reinterpret_cast<T>(m_InternalPtr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::enable_if_t<std::is_lvalue_reference_v<T>, T> PointerCalculator::As() const
|
||||
{
|
||||
return *reinterpret_cast<std::add_pointer_t<std::remove_reference_t<T>>>(m_InternalPtr);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::enable_if_t<std::is_same_v<T, std::uintptr_t>, T> PointerCalculator::As() const
|
||||
{
|
||||
return m_InternalPtr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline PointerCalculator PointerCalculator::Add(T offset) const
|
||||
{
|
||||
return {m_InternalPtr + offset};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline PointerCalculator PointerCalculator::Sub(T offset) const
|
||||
{
|
||||
return {m_InternalPtr - offset};
|
||||
}
|
||||
|
||||
inline PointerCalculator PointerCalculator::Rip() const
|
||||
{
|
||||
return Add(As<std::int32_t&>()).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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#include "Renderer.hpp"
|
||||
|
||||
#include "game/pointers/Pointers.hpp"
|
||||
|
||||
#include <backends/imgui_impl_dx11.h>
|
||||
#include <backends/imgui_impl_win32.h>
|
||||
#include <imgui.h>
|
||||
|
||||
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<void**>(&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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
#include "util/Joaat.hpp"
|
||||
|
||||
#include <d3d11.h>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <windows.h>
|
||||
|
||||
namespace NewBase
|
||||
{
|
||||
using DXCallback = std::function<void()>;
|
||||
using WindowProcedureCallback = std::function<void(HWND, UINT, WPARAM, LPARAM)>;
|
||||
|
||||
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<joaat_t, DXCallback> m_DXCallbacks;
|
||||
std::vector<WindowProcedureCallback> m_WindowProcedureCallbacks;
|
||||
};
|
||||
}
|
||||
|
||||
// Make our linker aware of the ImGui WndProcHandler
|
||||
extern LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include "game/frontend/GUI.hpp"
|
||||
|
||||
namespace NewBase::Menu
|
||||
{
|
||||
extern void Main();
|
||||
}
|
||||
@@ -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<SwapChain::Present, VMTHook<SwapChain::VMTSize>>()->Original<decltype(&Present)>(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<SwapChain::Present, VMTHook<SwapChain::VMTSize>>()->Original<decltype(&ResizeBuffers)>(SwapChain::VMTResizeBuffersIdx)(that, bufferCount, width, height, newFormat, swapChainFlags);
|
||||
Renderer::PostResize();
|
||||
return result;
|
||||
}
|
||||
return BaseHook::Get<SwapChain::Present, VMTHook<SwapChain::VMTSize>>()->Original<decltype(&ResizeBuffers)>(SwapChain::VMTResizeBuffersIdx)(that, bufferCount, width, height, newFormat, swapChainFlags);
|
||||
}
|
||||
}
|
||||
@@ -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<Window::WndProc, DetourHook<WNDPROC>>()->Original()(hwnd, umsg, wparam, lparam);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include <d3d11.h>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<IDXGISwapChain**>();
|
||||
});
|
||||
|
||||
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<WNDPROC>();
|
||||
});
|
||||
|
||||
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<void*>(), std::to_array({ 0xC3, 0x90, 0x90, 0x90 }))->Apply();
|
||||
|
||||
UnhookWindowsHookEx(*ptr.Add(45).Rip().As<HHOOK*>());
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include <d3d11.h>
|
||||
#include <windows.h>
|
||||
|
||||
namespace NewBase
|
||||
{
|
||||
struct PointerData
|
||||
{
|
||||
IDXGISwapChain** SwapChain;
|
||||
HWND Hwnd;
|
||||
WNDPROC WndProc;
|
||||
};
|
||||
|
||||
struct Pointers : PointerData
|
||||
{
|
||||
bool Init();
|
||||
};
|
||||
|
||||
inline NewBase::Pointers Pointers;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user