a5872955f4
This library is a completely refactored and rewritten version of the original graphics hook. The code is more clean, readable, and has a variety of new features, such as scaling and forcing memory capture. Currently, only D3D9, 10, and 11 are implemented. (This commit may be updated on this branch)
243 lines
5.4 KiB
C++
243 lines
5.4 KiB
C++
#define _CRT_SECURE_NO_WARNINGS
|
|
#include <d3d10_1.h>
|
|
#include <d3d11.h>
|
|
#include <dxgi.h>
|
|
#include <d3dcompiler.h>
|
|
|
|
#include "d3d1x_shaders.hpp"
|
|
#include "graphics-hook.h"
|
|
#include "../funchook.h"
|
|
|
|
typedef HRESULT (WINAPI *resize_buffers_t)(IDXGISwapChain*, UINT, UINT, UINT,
|
|
DXGI_FORMAT, UINT);
|
|
typedef HRESULT (WINAPI *present_t)(IDXGISwapChain*, UINT, UINT);
|
|
|
|
static struct func_hook resize_buffers;
|
|
static struct func_hook present;
|
|
|
|
struct dxgi_swap_data {
|
|
IDXGISwapChain *swap;
|
|
void (*capture)(void*, void*);
|
|
void (*free)(void);
|
|
};
|
|
|
|
static struct dxgi_swap_data data = {};
|
|
|
|
static bool setup_dxgi(IDXGISwapChain *swap)
|
|
{
|
|
const char *process_name = get_process_name();
|
|
bool ignore_d3d10 = false;
|
|
IUnknown *device;
|
|
HRESULT hr;
|
|
|
|
/* Call of duty ghosts allows the context to be queried as a d3d10
|
|
* context when it's actually a d3d11 context. Why this is I don't
|
|
* quite know. */
|
|
if (_strcmpi(process_name, "iw6sp64_ship.exe") == 0 ||
|
|
_strcmpi(process_name, "iw6mp64_ship.exe") == 0) {
|
|
ignore_d3d10 = true;
|
|
}
|
|
|
|
if (!ignore_d3d10) {
|
|
hr = swap->GetDevice(__uuidof(ID3D10Device), (void**)&device);
|
|
if (SUCCEEDED(hr)) {
|
|
data.swap = swap;
|
|
data.capture = d3d10_capture;
|
|
data.free = d3d10_free;
|
|
device->Release();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
hr = swap->GetDevice(__uuidof(ID3D11Device), (void**)&device);
|
|
if (SUCCEEDED(hr)) {
|
|
data.swap = swap;
|
|
data.capture = d3d11_capture;
|
|
data.free = d3d11_free;
|
|
device->Release();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE hook_resize_buffers(IDXGISwapChain *swap,
|
|
UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format,
|
|
UINT flags)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (!!data.free)
|
|
data.free();
|
|
|
|
data.swap = nullptr;
|
|
data.free = nullptr;
|
|
data.capture = nullptr;
|
|
|
|
unhook(&resize_buffers);
|
|
resize_buffers_t call = (resize_buffers_t)resize_buffers.call_addr;
|
|
hr = call(swap, buffer_count, width, height, format, flags);
|
|
rehook(&resize_buffers);
|
|
|
|
return hr;
|
|
}
|
|
|
|
static inline IDXGIResource *get_dxgi_backbuffer(IDXGISwapChain *swap)
|
|
{
|
|
IDXGIResource *res = nullptr;
|
|
HRESULT hr;
|
|
|
|
hr = swap->GetBuffer(0, __uuidof(ID3D11Resource), (void**)&res);
|
|
if (FAILED(hr))
|
|
hlog_hr("get_dxgi_backbuffer: GetBuffer failed", hr);
|
|
|
|
return res;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE hook_present(IDXGISwapChain *swap,
|
|
UINT sync_interval, UINT flags)
|
|
{
|
|
IDXGIResource *backbuffer = nullptr;
|
|
bool test_draw = (flags & DXGI_PRESENT_TEST) != 0;
|
|
bool capture;
|
|
HRESULT hr;
|
|
|
|
if (!data.swap && !capture_active()) {
|
|
setup_dxgi(swap);
|
|
}
|
|
|
|
capture = !test_draw && swap == data.swap && !!data.capture;
|
|
if (capture) {
|
|
backbuffer = get_dxgi_backbuffer(swap);
|
|
capture = !!backbuffer;
|
|
|
|
if (capture && !global_hook_info->capture_overlay)
|
|
data.capture(swap, backbuffer);
|
|
}
|
|
|
|
unhook(&present);
|
|
present_t call = (present_t)present.call_addr;
|
|
hr = call(swap, sync_interval, flags);
|
|
rehook(&present);
|
|
|
|
if (capture) {
|
|
if (global_hook_info->capture_overlay)
|
|
data.capture(swap, backbuffer);
|
|
|
|
backbuffer->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
static pD3DCompile get_compiler(void)
|
|
{
|
|
pD3DCompile compile = nullptr;
|
|
char d3dcompiler[40] = {};
|
|
int ver = 49;
|
|
|
|
while (ver > 30) {
|
|
sprintf_s(d3dcompiler, 40, "D3DCompiler_%02d.dll", ver);
|
|
|
|
HMODULE module = LoadLibraryA(d3dcompiler);
|
|
if (module) {
|
|
compile = (pD3DCompile)GetProcAddress(module,
|
|
"D3DCompile");
|
|
if (compile) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ver--;
|
|
}
|
|
|
|
return compile;
|
|
}
|
|
|
|
static uint8_t vertex_shader_data[1024];
|
|
static uint8_t pixel_shader_data[1024];
|
|
static size_t vertex_shader_size = 0;
|
|
static size_t pixel_shader_size = 0;
|
|
|
|
bool hook_dxgi(void)
|
|
{
|
|
pD3DCompile compile;
|
|
ID3D10Blob *blob;
|
|
HMODULE dxgi_module = get_system_module("dxgi.dll");
|
|
HRESULT hr;
|
|
void *present_addr;
|
|
void *resize_addr;
|
|
|
|
if (!dxgi_module) {
|
|
return false;
|
|
}
|
|
|
|
compile = get_compiler();
|
|
if (!compile) {
|
|
hlog("hook_dxgi: failed to find d3d compiler library");
|
|
return true;
|
|
}
|
|
|
|
/* ---------------------- */
|
|
|
|
hr = compile(vertex_shader_string, sizeof(vertex_shader_string),
|
|
"vertex_shader_string", nullptr, nullptr, "main",
|
|
"vs_4_0", D3D10_SHADER_OPTIMIZATION_LEVEL1, 0, &blob,
|
|
nullptr);
|
|
if (FAILED(hr)) {
|
|
hlog_hr("hook_dxgi: failed to compile vertex shader", hr);
|
|
return true;
|
|
}
|
|
|
|
vertex_shader_size = (size_t)blob->GetBufferSize();
|
|
memcpy(vertex_shader_data, blob->GetBufferPointer(),
|
|
blob->GetBufferSize());
|
|
blob->Release();
|
|
|
|
/* ---------------------- */
|
|
|
|
hr = compile(pixel_shader_string, sizeof(pixel_shader_string),
|
|
"pixel_shader_string", nullptr, nullptr, "main",
|
|
"ps_4_0", D3D10_SHADER_OPTIMIZATION_LEVEL1, 0, &blob,
|
|
nullptr);
|
|
if (FAILED(hr)) {
|
|
hlog_hr("hook_dxgi: failed to compile pixel shader", hr);
|
|
return true;
|
|
}
|
|
|
|
pixel_shader_size = (size_t)blob->GetBufferSize();
|
|
memcpy(pixel_shader_data, blob->GetBufferPointer(),
|
|
blob->GetBufferSize());
|
|
blob->Release();
|
|
|
|
/* ---------------------- */
|
|
|
|
present_addr = get_offset_addr(dxgi_module,
|
|
global_hook_info->offsets.dxgi.present);
|
|
resize_addr = get_offset_addr(dxgi_module,
|
|
global_hook_info->offsets.dxgi.resize);
|
|
|
|
hook_init(&present, present_addr, hook_present,
|
|
"IDXGISwapChain::Present");
|
|
hook_init(&resize_buffers, resize_addr, hook_resize_buffers,
|
|
"IDXGISwapChain::ResizeBuffers");
|
|
|
|
rehook(&resize_buffers);
|
|
rehook(&present);
|
|
|
|
hlog("Hooked DXGI");
|
|
return true;
|
|
}
|
|
|
|
uint8_t *get_d3d1x_vertex_shader(size_t *size)
|
|
{
|
|
*size = vertex_shader_size;
|
|
return vertex_shader_data;
|
|
}
|
|
|
|
uint8_t *get_d3d1x_pixel_shader(size_t *size)
|
|
{
|
|
*size = pixel_shader_size;
|
|
return pixel_shader_data;
|
|
}
|