feat(invoker): add a native invoker (#11)

This commit is contained in:
maybegreat48
2023-08-12 13:06:49 +00:00
committed by GitHub
parent 1df9405203
commit c2f19fe029
14 changed files with 132051 additions and 3 deletions
+2 -1
View File
@@ -1 +1,2 @@
build/
build/
.vs
+2
View File
@@ -7,6 +7,7 @@ include(cmake/async-logger.cmake)
include(cmake/imgui.cmake)
include(cmake/json.cmake)
include(cmake/minhook.cmake)
include(cmake/gtav-classes.cmake)
# source
set(SRC_DIR "${PROJECT_SOURCE_DIR}/src")
@@ -25,6 +26,7 @@ target_include_directories(${PROJECT_NAME} PRIVATE
"${SRC_DIR}"
"${imgui_SOURCE_DIR}"
"${minhook_SOURCE_DIR}/include"
"${gtav_classes_SOURCE_DIR}"
)
message(STATUS "Setting up linked libraries")
+27
View File
@@ -0,0 +1,27 @@
{
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
},
{
"name": "x64-Release",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_x64_x64" ],
"variables": []
}
]
}
+14
View File
@@ -0,0 +1,14 @@
include(FetchContent)
FetchContent_Declare(
gtav_classes
GIT_REPOSITORY https://github.com/maybegreat48/GTAV-Classes.git
GIT_TAG fb7ec7fe0a8ac08823ab7a0a2e68c08b027fce2f
GIT_PROGRESS TRUE
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
)
message("GTAV-Classes")
if(NOT gtav_classes_POPULATED)
FetchContent_Populate(gtav_classes)
endif()
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+113
View File
@@ -0,0 +1,113 @@
import json
crossmap = {}
natives = {}
current_idx = 0
crossmap_hash_list = []
class Arg:
def __init__(self, name: str, type: str):
self.name = name
self.type = type.replace("BOOL", "bool").replace("Any*", "void*")
def __str__(self) -> str:
return str(self.type) + " " + str(self.name)
class NativeFunc:
def __init__(self, namespace: str, name: str, hash: int, args: list[dict], return_type: str):
self.namespace = namespace
self.name = name
self.hash = hash
self.args: list[Arg] = []
self.return_type = return_type.replace("BOOL", "bool").replace("Any*", "void*")
self.native_index = -1
for arg in args:
self.args.append(Arg(arg["name"], arg["type"]))
def get_native_def_str(self) -> str:
assert self.native_index != -1
param_decl = ""
param_pass = ""
if len(self.args) > 0:
for arg in self.args:
param_decl += str(arg) + ", "
param_pass += arg.name + ", "
param_decl = param_decl[:-2]
param_pass = param_pass[:-2]
return f"FORCEINLINE constexpr {self.return_type} {self.name}({param_decl}) {{ return YimMenu::NativeInvoker::Invoke<{self.native_index}, {self.return_type}>({param_pass}); }}"
class CrossmapEntry:
def __init__(self, translated_hash: int):
self.hash = translated_hash
self.native_index = -1
def load_crossmap_data():
global crossmap
data = open("crossmap.txt").readlines()
for item in data:
translation = item.split(",")
crossmap[int(translation[0], 16)] = CrossmapEntry(int(translation[1], 16))
def load_natives_data():
global natives
data = json.load(open("natives.json"))
for ns, natives_list in data.items():
natives[ns] = []
for hash_str, native_data in natives_list.items():
natives[ns].append(NativeFunc(ns, native_data["name"], int(hash_str, 16), native_data["params"], native_data["return_type"]))
def allocate_indices():
global current_idx, crossmap_hash_list
for _, n in natives.items():
for native in n:
hash = native.hash
if hash in crossmap:
crossmap[hash].native_index = current_idx
native.native_index = current_idx
crossmap_hash_list.append(crossmap[hash].hash)
current_idx += 1
def write_crossmap_header():
open("Crossmap.hpp", "w+").write(f"""#pragma once
#include <script/scrNativeHandler.hpp>
namespace YimMenu
{{
constexpr std::array<rage::scrNativeHash, {len(crossmap_hash_list)}> g_Crossmap = {{{",".join([f"0x{x:X}" for x in crossmap_hash_list])}}};
}}
""")
def write_natives_header():
natives_buf = ""
for ns, nvs in natives.items():
natives_buf += f"namespace {ns}\n{{\n"
for nat_data in nvs:
if nat_data.native_index == -1:
continue
natives_buf += f"\t{nat_data.get_native_def_str()}\n"
natives_buf += "}\n\n"
natives_buf = natives_buf[:-2]
open("../Natives.hpp", "w+").write(f"""#pragma once
#include "invoker/Invoker.hpp"
// clang-format off
{natives_buf}
// clang-format on
""")
if __name__ == "__main__":
load_crossmap_data()
load_natives_data()
allocate_indices()
write_crossmap_header()
write_natives_header()
+20
View File
@@ -0,0 +1,20 @@
#include "Invoker.hpp"
#include "Crossmap.hpp"
#include "game/pointers/Pointers.hpp"
namespace YimMenu
{
void NativeInvoker::CacheHandlers()
{
if (m_AreHandlersCached)
return;
for (int i = 0; i < g_Crossmap.size(); i++)
{
m_Handlers[i] = Pointers.GetNativeHandler(Pointers.NativeRegistrationTable, g_Crossmap[i]);
}
m_AreHandlersCached = true;
}
}
+81
View File
@@ -0,0 +1,81 @@
#pragma once
#include "Crossmap.hpp"
#include "game/pointers/Pointers.hpp"
#include <script/scrNativeHandler.hpp>
#include <script/types.hpp>
namespace YimMenu
{
class CustomCallContext : public rage::scrNativeCallContext
{
public:
constexpr CustomCallContext()
{
m_return_value = &m_return_stack[0];
m_args = &m_arg_stack[0];
}
private:
uint64_t m_return_stack[10];
uint64_t m_arg_stack[40];
};
class NativeInvoker
{
static inline rage::scrNativeHandler m_Handlers[g_Crossmap.size()];
static inline bool m_AreHandlersCached{false};
public:
constexpr NativeInvoker(){};
constexpr void BeginCall()
{
m_CallContext.reset();
}
template<int index, bool fix_vectors>
constexpr void EndCall()
{
// TODO: try to get rid of this
if (!m_AreHandlersCached)
CacheHandlers();
m_Handlers[index](&m_CallContext);
if constexpr (fix_vectors)
Pointers.FixVectors(&m_CallContext);
}
template<typename T>
constexpr void PushArg(T&& value)
{
m_CallContext.push_arg(std::forward<T>(value));
}
template<typename T>
constexpr T& GetReturnValue()
{
return *m_CallContext.get_return_value<T>();
}
public:
static void CacheHandlers();
template<int index, typename Ret, typename... Args>
static constexpr FORCEINLINE Ret Invoke(Args&&... args)
{
NativeInvoker invoker{};
invoker.BeginCall();
(invoker.PushArg(std::forward<Args>(args)), ...);
invoker.EndCall<index, std::is_same_v<Ret, Vector3>>();
if constexpr (!std::is_same_v<Ret, void>)
{
return invoker.GetReturnValue<Ret>();
}
}
CustomCallContext m_CallContext{};
};
}
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+12 -1
View File
@@ -31,11 +31,22 @@ namespace YimMenu
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();
BytePatch::Make(ptr.As<void*>(), std::to_array({0xC3, 0x90, 0x90, 0x90}))->Apply();
UnhookWindowsHookEx(*ptr.Add(45).Rip().As<HHOOK*>());
});
constexpr auto nativeHandlersPtrn = Pattern<"48 8D 0D ? ? ? ? 48 8B 14 FA E8 ? ? ? ? 48 85 C0 75 0A">("NativeHandlers");
scanner.Add(nativeHandlersPtrn, [this](PointerCalculator ptr) {
NativeRegistrationTable = ptr.Add(3).Rip().As<void*>();
GetNativeHandler = ptr.Add(12).Rip().As<Functions::GetNativeHandler>();
});
constexpr auto fixVectorsPtrn = Pattern<"83 79 18 00 48 8B D1 74 4A FF 4A 18 48 63 4A 18 48 8D 41 04 48 8B 4C CA">("FixVectors");
scanner.Add(fixVectorsPtrn, [this](PointerCalculator ptr) {
FixVectors = ptr.As<Functions::FixVectors>();
});
if (!scanner.Scan())
{
LOG(FATAL) << "Some patterns could not be found, unloading.";
+10
View File
@@ -1,15 +1,25 @@
#pragma once
#include <d3d11.h>
#include <script/scrNativeHandler.hpp>
#include <windows.h>
namespace YimMenu
{
namespace Functions
{
using GetNativeHandler = rage::scrNativeHandler (*)(void* registration_table, rage::scrNativeHash hash);
using FixVectors = void (*)(rage::scrNativeCallContext* call_ctx);
}
struct PointerData
{
IDXGISwapChain** SwapChain;
HWND Hwnd;
WNDPROC WndProc;
std::int64_t** ScriptGlobals;
void* NativeRegistrationTable;
Functions::GetNativeHandler GetNativeHandler;
Functions::FixVectors FixVectors;
};
struct Pointers : PointerData
+2 -1
View File
@@ -4,9 +4,9 @@
#include "core/memory/ModuleMgr.hpp"
#include "core/renderer/Renderer.hpp"
#include "game/frontend/GUI.hpp"
#include "game/gta/Natives.hpp"
#include "game/pointers/Pointers.hpp"
namespace YimMenu
{
DWORD Main(void*)
@@ -28,6 +28,7 @@ namespace YimMenu
while (g_Running)
{
std::this_thread::sleep_for(100ms);
LOG(INFO) << ENTITY::GET_ENTITY_HEALTH(PLAYER::PLAYER_PED_ID());
}
unload: