goldberg_emulator/overlay_experimental/Hook_Manager.cpp
Nemirtingas dd530e80b1 Now passing to hooks device object.
Added Base_Hook::get_lib_name to track what renderer is hooked for overlay.
Objects used to detect renderer type are now also used to hook the rendering functions. So we don't have to build another device.
Updated VTables for DX12.
2019-08-26 16:38:01 +02:00

530 lines
17 KiB
C++

#include "../dll/dll.h"
#include "Hook_Manager.h"
#ifndef NO_OVERLAY
#include "../detours/detours.h"
#ifdef STEAM_WIN32
#include "DX12_Hook.h"
#include "DX11_Hook.h"
#include "DX10_Hook.h"
#include "DX9_Hook.h"
#include "Windows_Hook.h"
#endif
#include "OpenGL_Hook.h"
#include <algorithm>
constexpr int max_hook_retries = 500;
#ifdef STEAM_WIN32
static decltype(&IDXGISwapChain::Present) _IDXGISwapChain_Present = nullptr;
static decltype(&IDirect3DDevice9::Present) _IDirect3DDevice9_Present = nullptr;
static decltype(&IDirect3DDevice9Ex::PresentEx) _IDirect3DDevice9Ex_PresentEx = nullptr;
static decltype(wglMakeCurrent)* _wglMakeCurrent = nullptr;
HRESULT STDMETHODCALLTYPE Hook_Manager::MyIDXGISwapChain_Present(IDXGISwapChain* _this, UINT SyncInterval, UINT Flags)
{
Hook_Manager& inst = Hook_Manager::Inst();
if (!inst.stop_retry())
{
IUnknown* pDevice = nullptr;
_this->GetDevice(__uuidof(ID3D10Device), (void**)&pDevice);
if (pDevice)
{
DX10_Hook* hook = DX10_Hook::Inst();
if (hook->start_hook())
inst.AddHook(hook);
}
else
{
_this->GetDevice(__uuidof(ID3D11Device), (void**)& pDevice);
if (pDevice)
{
DX11_Hook* hook = DX11_Hook::Inst();
if (hook->start_hook())
inst.AddHook(hook);
}
else
{
_this->GetDevice(__uuidof(ID3D12Device), (void**)& pDevice);
if (pDevice)
{
// DX12_Hook* hook = DX12_Hook::Inst();
// if (hook->start_hook())
// inst.AddHook(hook);
}
}
}
if (pDevice) pDevice->Release();
}
return (_this->*_IDXGISwapChain_Present)(SyncInterval, Flags);
}
HRESULT STDMETHODCALLTYPE Hook_Manager::MyPresent(IDirect3DDevice9* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion)
{
Hook_Manager& inst = Hook_Manager::Inst();
if (!inst.stop_retry())
{
DX9_Hook* hook = DX9_Hook::Inst();
if (hook->start_hook())
inst.AddHook(hook);
}
return (_this->*_IDirect3DDevice9_Present)(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);
}
HRESULT STDMETHODCALLTYPE Hook_Manager::MyPresentEx(IDirect3DDevice9Ex* _this, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion, DWORD dwFlags)
{
Hook_Manager& inst = Hook_Manager::Inst();
if (!inst.stop_retry())
{
DX9_Hook* hook = DX9_Hook::Inst();
if (hook->start_hook())
inst.AddHook(hook);
}
return (_this->*_IDirect3DDevice9Ex_PresentEx)(pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion, dwFlags);
}
BOOL WINAPI Hook_Manager::MywglMakeCurrent(HDC hDC, HGLRC hGLRC)
{
Hook_Manager& inst = Hook_Manager::Inst();
if (!inst.stop_retry())
{
OpenGL_Hook* hook = OpenGL_Hook::Inst();
if (hook->start_hook())
inst.AddHook(hook);
}
return _wglMakeCurrent(hDC, hGLRC);
}
void Hook_Manager::HookDXGIPresent(IDXGISwapChain *pSwapChain)
{
if (!_dxgi_hooked)
{
_dxgi_hooked = true;
(void*&)_IDXGISwapChain_Present = (*reinterpret_cast<void***>(pSwapChain))[(int)IDXGISwapChainVTable::Present];
rendererdetect_hook->BeginHook();
rendererdetect_hook->HookFuncs(
std::pair<void**, void*>((PVOID*)& _IDXGISwapChain_Present, &Hook_Manager::MyIDXGISwapChain_Present)
);
rendererdetect_hook->EndHook();
}
}
void Hook_Manager::HookDX9Present(IDirect3DDevice9* pDevice, bool ex)
{
if (!_dx9_hooked)
{
_dx9_hooked = true;
(void*&)_IDirect3DDevice9_Present = (*reinterpret_cast<void***>(pDevice))[(int)IDirect3DDevice9VTable::Present];
if (ex)
(void*&)_IDirect3DDevice9Ex_PresentEx = (*reinterpret_cast<void***>(pDevice))[(int)IDirect3DDevice9VTable::PresentEx];
rendererdetect_hook->BeginHook();
rendererdetect_hook->HookFuncs(
std::pair<void**, void*>((PVOID*)& _IDirect3DDevice9_Present, &Hook_Manager::MyPresent)
);
if (ex)
{
rendererdetect_hook->HookFuncs(
std::pair<void**, void*>((PVOID*)& _IDirect3DDevice9Ex_PresentEx, &Hook_Manager::MyPresentEx)
);
}
rendererdetect_hook->EndHook();
}
}
void Hook_Manager::HookwglMakeCurrent()
{
if (!_ogl_hooked)
{
_ogl_hooked = true;
rendererdetect_hook->BeginHook();
rendererdetect_hook->HookFuncs(
std::pair<void**, void*>((PVOID*)& _wglMakeCurrent, &Hook_Manager::MywglMakeCurrent)
);
rendererdetect_hook->EndHook();
}
}
void Hook_Manager::hook_dx9()
{
if (!_dx9_hooked && !_renderer_found)
{
HWND hWnd = GetGameWindow();
if (!hWnd)
return;
IDirect3D9Ex* pD3D = nullptr;
IUnknown* pDevice = nullptr;
D3DPRESENT_PARAMETERS params = {};
params.BackBufferWidth = 1;
params.BackBufferHeight = 1;
params.hDeviceWindow = hWnd;
params.BackBufferCount = 1;
params.Windowed = TRUE;
params.SwapEffect = D3DSWAPEFFECT_DISCARD;
decltype(Direct3DCreate9Ex)* Direct3DCreate9Ex = (decltype(Direct3DCreate9Ex))GetProcAddress(GetModuleHandle(DX9_Hook::DLL_NAME), "Direct3DCreate9Ex");
if (Direct3DCreate9Ex != nullptr)
{
Direct3DCreate9Ex(D3D_SDK_VERSION, &pD3D);
pD3D->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_NULLREF, NULL, D3DCREATE_HARDWARE_VERTEXPROCESSING, &params, NULL, reinterpret_cast<IDirect3DDevice9Ex**>(&pDevice));
}
else
{
decltype(Direct3DCreate9)* Direct3DCreate9 = (decltype(Direct3DCreate9))GetProcAddress(GetModuleHandle(DX9_Hook::DLL_NAME), "Direct3DCreate9");
if (Direct3DCreate9)
{
pD3D = reinterpret_cast<IDirect3D9Ex*>(Direct3DCreate9(D3D_SDK_VERSION));
pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_NULLREF, NULL, D3DCREATE_HARDWARE_VERTEXPROCESSING, &params, reinterpret_cast<IDirect3DDevice9 * *>(&pDevice));
}
}
if (pDevice != nullptr)
{
PRINT_DEBUG("Hooked D3D9::Present to detect DX Version\n");
auto h = DX9_Hook::Inst();
h->loadFunctions(reinterpret_cast<IDirect3DDevice9*>(pDevice), Direct3DCreate9Ex != nullptr);
_hooks.insert(h);
HookDX9Present(reinterpret_cast<IDirect3DDevice9*>(pDevice), Direct3DCreate9Ex != nullptr);
}
else
{
PRINT_DEBUG("Failed to hook D3D9::Present to detect DX Version\n");
}
if (pDevice) pDevice->Release();
if (pD3D) pD3D->Release();
}
}
void Hook_Manager::hook_dx10()
{
if (!_dxgi_hooked && !_renderer_found)
{
HWND hWnd = GetGameWindow();
if (!hWnd)
return;
IDXGISwapChain* pSwapChain;
ID3D10Device* pDevice;
DXGI_SWAP_CHAIN_DESC SwapChainDesc = {};
decltype(D3D10CreateDeviceAndSwapChain)* D3D10CreateDeviceAndSwapChain =
(decltype(D3D10CreateDeviceAndSwapChain))GetProcAddress(GetModuleHandle(DX10_Hook::DLL_NAME), "D3D10CreateDeviceAndSwapChain");
SwapChainDesc.BufferCount = 1;
SwapChainDesc.BufferDesc.Width = 1;
SwapChainDesc.BufferDesc.Height = 1;
SwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
SwapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
SwapChainDesc.BufferDesc.RefreshRate.Denominator = 0;
SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
SwapChainDesc.OutputWindow = hWnd;
SwapChainDesc.SampleDesc.Count = 1;
SwapChainDesc.SampleDesc.Quality = 0;
SwapChainDesc.Windowed = TRUE;
D3D10CreateDeviceAndSwapChain(NULL, D3D10_DRIVER_TYPE_NULL, NULL, 0, D3D10_SDK_VERSION, &SwapChainDesc, &pSwapChain, &pDevice);
if (pDevice != nullptr && pSwapChain != nullptr)
{
PRINT_DEBUG("Hooked IDXGISwapChain::Present to detect DX Version\n");
auto h = DX10_Hook::Inst();
h->loadFunctions(pDevice, pSwapChain);
_hooks.insert(h);
HookDXGIPresent(pSwapChain);
}
else
{
PRINT_DEBUG("Failed to Hook IDXGISwapChain::Present to detect DX Version\n");
}
if (pDevice)pDevice->Release();
if (pSwapChain)pSwapChain->Release();
}
}
void Hook_Manager::hook_dx11()
{
if (!_dxgi_hooked && !_renderer_found)
{
HWND hWnd = GetGameWindow();
if (!hWnd)
return;
IDXGISwapChain* pSwapChain;
ID3D11Device* pDevice;
DXGI_SWAP_CHAIN_DESC SwapChainDesc = {};
decltype(D3D11CreateDeviceAndSwapChain)* D3D11CreateDeviceAndSwapChain =
(decltype(D3D11CreateDeviceAndSwapChain))GetProcAddress(GetModuleHandle(DX11_Hook::DLL_NAME), "D3D11CreateDeviceAndSwapChain");
SwapChainDesc.BufferCount = 1;
SwapChainDesc.BufferDesc.Width = 1;
SwapChainDesc.BufferDesc.Height = 1;
SwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
SwapChainDesc.BufferDesc.RefreshRate.Numerator = 0;
SwapChainDesc.BufferDesc.RefreshRate.Denominator = 0;
SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
SwapChainDesc.OutputWindow = hWnd;
SwapChainDesc.SampleDesc.Count = 1;
SwapChainDesc.SampleDesc.Quality = 0;
SwapChainDesc.Windowed = TRUE;
D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_NULL, NULL, 0, NULL, NULL, D3D11_SDK_VERSION, &SwapChainDesc, &pSwapChain, &pDevice, NULL, NULL);
if (pDevice != nullptr && pSwapChain != nullptr)
{
PRINT_DEBUG("Hooked IDXGISwapChain::Present to detect DX Version\n");
auto h = DX11_Hook::Inst();
h->loadFunctions(pDevice, pSwapChain);
_hooks.insert(h);
HookDXGIPresent(pSwapChain);
}
else
{
PRINT_DEBUG("Failed to Hook IDXGISwapChain::Present to detect DX Version\n");
}
if (pDevice) pDevice->Release();
if (pSwapChain) pSwapChain->Release();
}
}
void Hook_Manager::hook_dx12()
{
if (!_dxgi_hooked && !_renderer_found)
{
HWND hWnd = GetGameWindow();
if (!hWnd)
return;
IDXGIFactory4* pDXGIFactory = nullptr;
IDXGISwapChain1* pSwapChain = nullptr;
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
ID3D12CommandQueue* pCommandQueue = nullptr;
ID3D12Device* pDevice = nullptr;
decltype(D3D12CreateDevice)* D3D12CreateDevice =
(decltype(D3D12CreateDevice))GetProcAddress(GetModuleHandle(DX12_Hook::DLL_NAME), "D3D12CreateDevice");
D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0, IID_PPV_ARGS(&pDevice));
if (pDevice)
{
DXGI_SWAP_CHAIN_DESC1 SwapChainDesc = {};
SwapChainDesc.BufferCount = 2;
SwapChainDesc.Width = 1;
SwapChainDesc.Height = 1;
SwapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
SwapChainDesc.Stereo = FALSE;
SwapChainDesc.SampleDesc = { 1, 0 };
SwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
SwapChainDesc.Scaling = DXGI_SCALING_NONE;
SwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
SwapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
pDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&pCommandQueue));
if (pCommandQueue)
{
reinterpret_cast<decltype(CreateDXGIFactory1)*>(GetProcAddress(GetModuleHandle("dxgi.dll"), "CreateDXGIFactory1"))(IID_PPV_ARGS(&pDXGIFactory));
pDXGIFactory->CreateSwapChainForHwnd(pCommandQueue, hWnd, &SwapChainDesc, NULL, NULL, &pSwapChain);
if (pSwapChain != nullptr)
{
PRINT_DEBUG("Hooked IDXGISwapChain::Present to detect DX Version\n");
auto h = DX12_Hook::Inst();
h->loadFunctions(pDevice, pCommandQueue, pSwapChain);
_hooks.insert(h);
HookDXGIPresent(pSwapChain);
}
else
{
PRINT_DEBUG("Failed to Hook IDXGISwapChain::Present to detect DX Version\n");
}
}
}
if (pSwapChain) pSwapChain->Release();
if (pDXGIFactory) pDXGIFactory->Release();
if (pCommandQueue) pCommandQueue->Release();
if (pDevice) pDevice->Release();
}
}
void Hook_Manager::hook_opengl()
{
if (!_ogl_hooked && !_renderer_found)
{
_wglMakeCurrent = (decltype(_wglMakeCurrent))GetProcAddress(GetModuleHandle(OpenGL_Hook::DLL_NAME), "wglMakeCurrent");
HookwglMakeCurrent();
}
}
void Hook_Manager::create_hook(const char* libname)
{
if (!_stricmp(libname, "d3d9.dll"))
Hook_Manager::Inst().hook_dx9();
else if (!_stricmp(libname, "d3d10.dll"))
Hook_Manager::Inst().hook_dx10();
else if (!_stricmp(libname, "d3d11.dll"))
Hook_Manager::Inst().hook_dx11();
else if (!_stricmp(libname, "d3d12.dll"))
Hook_Manager::Inst().hook_dx12();
else if (!_stricmp(libname, "opengl32.dll"))
Hook_Manager::Inst().hook_opengl();
}
//void Hook_Manager::create_hookW(const wchar_t *libname)
//{
// if (!_wcsicmp(libname, L"d3d9.dll"))
// Hook_Manager::Inst().hook_dx9();
// else if (!_wcsicmp(libname, L"d3d10.dll"))
// Hook_Manager::Inst().hook_dx10();
// else if (!_wcsicmp(libname, L"d3d11.dll"))
// Hook_Manager::Inst().hook_dx11();
// else if (!_wcsicmp(libname, L"d3d12.dll"))
// Hook_Manager::Inst().hook_dx12();
// else if (!_wcsicmp(libname, L"opengl32.dll"))
// Hook_Manager::Inst().hook_opengl();
//}
//
//HMODULE WINAPI Hook_Manager::MyLoadLibraryA(LPCTSTR lpLibFileName)
//{
// auto res = _LoadLibraryA(lpLibFileName);
// Hook_Manager::Inst().create_hookA(lpLibFileName);
// return res;
//}
//
//HMODULE WINAPI Hook_Manager::MyLoadLibraryW(LPCWSTR lpLibFileName)
//{
// auto res = _LoadLibraryW(lpLibFileName);
// Hook_Manager::Inst().create_hookW(lpLibFileName);
// return res;
//}
//
//HMODULE WINAPI Hook_Manager::MyLoadLibraryExA(LPCTSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
//{
// auto res = _LoadLibraryA(lpLibFileName);
// Hook_Manager::Inst().create_hookA(lpLibFileName);
// return res;
//}
//
//HMODULE WINAPI Hook_Manager::MyLoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
//{
// auto res = _LoadLibraryExW(lpLibFileName, hFile, dwFlags);
// Hook_Manager::Inst().create_hookW(lpLibFileName);
// return res;
//}
bool Hook_Manager::stop_retry()
{
// Retry or not
bool stop = ++_hook_retries >= max_hook_retries;
if (stop)
FoundRenderer(nullptr);
return stop;
}
void Hook_Manager::find_renderer(Hook_Manager* _this)
{
_this->rendererdetect_hook = new Base_Hook();
_this->AddHook(_this->rendererdetect_hook);
std::vector<std::string> const libraries = { "opengl32.dll", "d3d12.dll", "d3d11.dll", "d3d10.dll", "d3d9.dll" };
while (!_this->_renderer_found && !_this->stop_retry())
{
std::vector<std::string>::const_iterator it = libraries.begin();
while (it != libraries.end())
{
it = std::find_if(it, libraries.end(), [](std::string const& name) {
auto x = GetModuleHandle(name.c_str());
if (x != NULL)
return true;
return false;
});
if (it == libraries.end())
break;
_this->create_hook(it->c_str());
++it;
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
#endif
Hook_Manager::Hook_Manager():
_hook_retries(0),
#ifdef STEAM_WIN32
_loadlibrary_hooked(false),
_dxgi_hooked(false),
_dx9_hooked(false),
#endif
_renderer_found(false),
_ogl_hooked(false)
{}
Hook_Manager::~Hook_Manager()
{
for (auto& i : _hooks)
delete i;
}
Hook_Manager& Hook_Manager::Inst()
{
static Hook_Manager hook;
return hook;
}
void Hook_Manager::HookRenderer()
{
#ifdef STEAM_WIN32
_hook_thread = new std::thread(&Hook_Manager::find_renderer, this);
#endif
}
void Hook_Manager::FoundRenderer(Base_Hook* hook)
{
if (!_renderer_found)
{
_renderer_found = true;
game_renderer = hook;
if (hook == nullptr)
PRINT_DEBUG("We found a renderer but couldn't hook it, aborting overlay hook.\n");
else
PRINT_DEBUG("Hooked renderer in %d tries\n", _hook_retries);
_hook_thread->join();
delete _hook_thread;
_hook_thread = nullptr;
// Remove all hooks that are unused
std::set<Base_Hook*>::iterator item;
while ((item = std::find_if(_hooks.begin(), _hooks.end(), [hook](Base_Hook* item) {return item != hook; })) != _hooks.end())
{
delete *item;
_hooks.erase(item);
}
}
}
#endif//NO_OVERLAY