Skip to content

Instantly share code, notes, and snippets.

@user-grinch
Last active December 29, 2024 10:03
Show Gist options
  • Save user-grinch/3164c2055fd93aee5c20576dea226a24 to your computer and use it in GitHub Desktop.
Save user-grinch/3164c2055fd93aee5c20576dea226a24 to your computer and use it in GitHub Desktop.
GTA III VC SA DirectX Hooking Class
#include "pch.h"
#include "d3dhook.h"
#include "kiero/kiero.h"
#include "kiero/minhook/MinHook.h"
#include "imgui/imgui_impl_dx9.h"
#include "imgui/imgui_impl_dx11.h"
#include "imgui/imgui_impl_win32.h"
#include "../include/fonts/fonts.h"
#include "../trainer.h"
#include "../pages/menu.h"
extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
bool D3dHook::GetMouseState() {
return mouseShown;
}
void D3dHook::SetMouseState(bool state) {
mouseShown = state;
}
LRESULT D3dHook::hkWndProc(const HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
ImGui_ImplWin32_WndProcHandler(hWnd, uMsg, wParam, lParam);
if (ImGui::GetIO().WantTextInput) {
#ifdef GTASA
Call<0x53F1E0>(); // CPad::ClearKeyboardHistory
#endif
return 1;
}
return CallWindowProc(oWndProc, hWnd, uMsg, wParam, lParam);
}
HRESULT D3dHook::hkReset(IDirect3DDevice9* pDevice, D3DPRESENT_PARAMETERS* pPresentationParameters) {
ImGui_ImplDX9_InvalidateDeviceObjects();
return oReset(pDevice, pPresentationParameters);
}
BOOL CALLBACK D3dHook::hkSetCursorPos(int x, int y) {
if (ImGui::GetIO().MouseDrawCursor) {
return true;
}
return oSetCursorPos(x, y);
}
BOOL CALLBACK D3dHook::hkShowCursor(bool flag) {
if (ImGui::GetIO().MouseDrawCursor) {
return oShowCursor(TRUE);
}
return oShowCursor(flag);
}
void D3dHook::ProcessFrame(void* ptr) {
ImGuiIO& io = ImGui::GetIO();
static bool init;
if (init) {
ProcessMouse();
ImGui_ImplWin32_NewFrame();
if (gRenderer == eRenderer::Dx9) {
ImGui_ImplDX9_NewFrame();
} else {
ImGui_ImplDX11_NewFrame();
}
// Scale the menu if game resolution changed
static ImVec2 fScreenSize = ImVec2(-1, -1);
ImVec2 screen(screen::GetScreenWidth(), screen::GetScreenHeight());
if (fScreenSize.x != screen.x && fScreenSize.y != screen.y) {
FontMgr::SetFontReloadRequired(true);
if (fScreenSize.x != -1 && fScreenSize.y != -1) {
Trainer.ResetParams();
}
ImGuiStyle* style = &ImGui::GetStyle();
float scaleX = screen.x / 1920.0f;
float scaleY = screen.y / 1080.0f;
style->FramePadding = ImVec2(5.0f * scaleX, 5.0f * scaleY);
style->ItemSpacing = ImVec2(5 * scaleX, 3 * scaleY);
style->ScrollbarSize = 15 * scaleX;
style->IndentSpacing = 8 * scaleX;
style->ItemInnerSpacing = ImVec2(2 * scaleX, 2 * scaleY);
fScreenSize = screen;
}
ImGui::NewFrame();
if (pCallbackFunc != nullptr) {
pCallbackFunc();
}
if (gRenderer == eRenderer::Dx9 && mouseShown) {
auto drawList = ImGui::GetForegroundDrawList();
ImVec2 pos = ImGui::GetCurrentContext()->MouseLastValidPos;
float scaleX = screen::GetScreenWidth() / 1366.0f;
float scaleY = screen::GetScreenHeight() / 768.0f;
drawList->AddImage(gTextureList.FindTextureByName("cursor"), pos, pos + ImVec2(20.0f * scaleX, 20.0f * scaleY), {0, 0}, {1, 1});
} else {
io.MouseDrawCursor = mouseShown;
}
ImGui::EndFrame();
ImGui::Render();
if (gRenderer == eRenderer::Dx9) {
ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData());
} else {
ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData());
}
if (FontMgr::IsFontReloadRequired())
{
if (gRenderer == eRenderer::Dx9)
{
ImGui_ImplDX9_InvalidateDeviceObjects();
}
else
{
ImGui_ImplDX11_InvalidateDeviceObjects();
}
FontMgr::ReloadAll();
}
} else {
init = true;
ImGui_ImplWin32_Init(RsGlobal.ps->window);
#ifdef GTASA
patch::Nop(0x00531155, 5); // shift trigger fix
#endif
if (gRenderer == eRenderer::Dx9) {
gD3dDevice = ptr;
ImGui_ImplDX9_Init(reinterpret_cast<IDirect3DDevice9*>(ptr));
} else {
// for dx11 device ptr is swapchain
reinterpret_cast<IDXGISwapChain*>(ptr)->GetDevice(__uuidof(ID3D11Device), &ptr);
gD3dDevice = ptr;
ID3D11DeviceContext* context;
reinterpret_cast<ID3D11Device*>(ptr)->GetImmediateContext(&context);
ImGui_ImplDX11_Init(reinterpret_cast<ID3D11Device*>(ptr), context);
}
ImGui_ImplWin32_EnableDpiAwareness();
io.DisplaySize = {screen::GetScreenWidth(), screen::GetScreenHeight()};
// Loading fonts
io.FontDefault = FontMgr::LoadFont("text", textFont, 16.5f);
FontMgr::LoadFont("title", titleFont, 21.0f);
FontMgr::LoadFont("icon", iconFont, 26.0f, true);
io.IniFilename = nullptr;
io.LogFilename = nullptr;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
oWndProc = (WNDPROC)SetWindowLongPtr(RsGlobal.ps->window, GWL_WNDPROC, (LRESULT)hkWndProc);
}
}
HRESULT D3dHook::hkEndScene(IDirect3DDevice9* pDevice) {
ProcessFrame(pDevice);
return oEndScene(pDevice);
}
HRESULT D3dHook::hkPresent(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags) {
ProcessFrame(pSwapChain);
return oPresent(pSwapChain, SyncInterval, Flags);
}
void D3dHook::ProcessMouse() {
// Disable player controls for controllers
bool bMouseDisabled = false;
bool isController = patch::Get<BYTE>(BY_GAME(0xBA6818, 0x86968B, 0x5F03D8));
#ifdef GTA3
isController = !isController;
#endif
if ((isController || menuPage.m_bLockGameInputs) && (mouseShown || bMouseDisabled)) {
#ifdef GTASA
CPlayerPed *player = FindPlayerPed();
CPad *pad = player ? player->GetPadFromPlayer() : NULL;
#else
CPad *pad = CPad::GetPad(0);
#endif
if (pad) {
if (mouseShown) {
bMouseDisabled = true;
#ifdef GTA3
pad->DisablePlayerControls = true;
#else
pad->DisablePlayerControls = true;
#endif
} else {
bMouseDisabled = false;
#ifdef GTA3
pad->DisablePlayerControls = false;
#else
pad->DisablePlayerControls = false;
#endif
}
}
}
static bool mouseState = false;
if (mouseState != mouseShown) {
if (mouseShown) {
patch::SetUChar(BY_GAME(0x6194A0, 0x6020A0, 0x580D20), 0xC3); // psSetMousePos
patch::Nop(BY_GAME(0x541DD7, 0x4AB6CA, 0x49272F), 5); // don't call CPad::UpdateMouse()
// patch::SetUChar(BY_GAME(0x541DD0, 0x4AB6C0, 0x492720), 0xC3);
#ifdef GTASA
// Fix bug with radio switching
patch::SetUChar(0x4EB731, 0xEB); // jz -> jmp, skip mouse checks
patch::SetUChar(0x4EB75A, 0xEB); // jz -> jmp, skip mouse checks
#endif
} else {
patch::SetUChar(BY_GAME(0x6194A0, 0x6020A0, 0x580D20), BY_GAME(0xE9, 0x53, 0x53));
// patch::SetUChar(BY_GAME(0x541DD0, 0x4AB6C0, 0x492720), BY_GAME(0x56, 0xB9, 0x53));
#ifdef GTASA
patch::SetRaw(0x541DD7, (char*)"\xE8\xE4\xD5\xFF\xFF", 5);
patch::SetUChar(0x4EB731, 0x74); // jz
patch::SetUChar(0x4EB75A, 0x74); // jz
#elif GTAVC
patch::SetRaw(0x4AB6CA, (char*)"\xE8\x51\x21\x00\x00", 5);
#else
patch::SetRaw(0x49272F, (char*)"\xE8\x6C\xF5\xFF\xFF", 5);
#endif
}
// Need to update pads before resting values
CPad::UpdatePads();
CPad::NewMouseControllerState.x = 0;
CPad::NewMouseControllerState.y = 0;
#ifdef GTA3
CPad::GetPad(0)->ClearMouseHistory();
#else
CPad::ClearMouseHistory();
CPad::GetPad(0)->NewState.DPadUp = 0;
CPad::GetPad(0)->OldState.DPadUp = 0;
CPad::GetPad(0)->NewState.DPadDown = 0;
CPad::GetPad(0)->OldState.DPadDown = 0;
#endif
mouseState = mouseShown;
}
}
bool D3dHook::Init(std::function<void()> pCallback) {
static bool hookInjected;
if (hookInjected) {
return false;
}
MH_Initialize();
PVOID pSetCursorPos = GetProcAddress(GetModuleHandle("user32.dll"), "SetCursorPos");
PVOID pShowCursor = GetProcAddress(GetModuleHandle("user32.dll"), "ShowCursor");
MH_CreateHook(pSetCursorPos, hkSetCursorPos, reinterpret_cast<LPVOID*>(&oSetCursorPos));
// MH_CreateHook(pShowCursor, hkShowCursor, reinterpret_cast<LPVOID*>(&oShowCursor));
MH_EnableHook(pSetCursorPos);
// MH_EnableHook(pShowCursor);
/*
Must check for d3d9 first!
Seems to crash with nvidia geforce experience overlay
if anything else is checked before d3d9
*/
if (init(kiero::RenderType::D3D9) == kiero::Status::Success) {
gRenderer = eRenderer::Dx9;
// gD3dDevice = *reinterpret_cast<void**>(kiero::getMethodsTable());
kiero::bind(16, (void**)&oReset, hkReset);
kiero::bind(42, (void**)&oEndScene, hkEndScene);
pCallbackFunc = pCallback;
hookInjected = true;
} else {
if (init(kiero::RenderType::D3D11) == kiero::Status::Success) {
gRenderer = eRenderer::Dx11;
kiero::bind(8, (void**)&oPresent, hkPresent);
pCallbackFunc = pCallback;
hookInjected = true;
}
}
return hookInjected;
}
void D3dHook::Shutdown() {
pCallbackFunc = nullptr;
SetWindowLongPtr(RsGlobal.ps->window, GWL_WNDPROC, (LRESULT)oWndProc);
if (gRenderer == eRenderer::Dx9) {
ImGui_ImplDX9_Shutdown();
} else {
ImGui_ImplDX11_Shutdown();
}
ImGui_ImplWin32_Shutdown();
ImGui::DestroyContext();
kiero::shutdown();
}
#pragma once
#include "pch.h"
/*
* DirectX Hooking Class
* Supports DX9 & DX11
*/
class D3dHook {
private:
using f_EndScene = HRESULT(CALLBACK*)(IDirect3DDevice9*);
using f_Present = HRESULT(CALLBACK*)(IDXGISwapChain*, UINT, UINT);
using f_Reset = HRESULT(CALLBACK*)(IDirect3DDevice9*, D3DPRESENT_PARAMETERS*);
using f_SetCursorPos = BOOL(CALLBACK*)(int, int);
using f_ShowCursor = BOOL(CALLBACK*)(bool);
static inline WNDPROC oWndProc;
static inline f_Present oPresent;
static inline f_EndScene oEndScene;
static inline f_SetCursorPos oSetCursorPos;
static inline f_ShowCursor oShowCursor;
static inline f_Reset oReset;
static inline bool mouseShown;
static inline std::function<void()> pCallbackFunc = nullptr;
static void CALLBACK ProcessFrame(void* ptr);
static LRESULT CALLBACK hkWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static void ProcessMouse();
static BOOL CALLBACK hkSetCursorPos(int, int);
static BOOL CALLBACK hkShowCursor(bool);
// DirectX9
static HRESULT CALLBACK hkEndScene(IDirect3DDevice9* pDevice);
static HRESULT CALLBACK hkReset(IDirect3DDevice9* pDevice, D3DPRESENT_PARAMETERS* pPresentationParameters);
// DirectX11, Renderhook
static HRESULT CALLBACK hkPresent(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags);
public:
D3dHook() = delete;
D3dHook(D3dHook const&) = delete;
void operator=(D3dHook const&) = delete;
// Returns the current mouse visibility state
static bool GetMouseState();
// Injects game hooks & stuff
static bool Init(std::function<void()> pCallback);
// Sets the current mouse visibility state
static void SetMouseState(bool state);
// Cleans up hooks
static void Shutdown();
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment