jp9000 f53df7da64 clang-format: Apply formatting
Code submissions have continually suffered from formatting
inconsistencies that constantly have to be addressed.  Using
clang-format simplifies this by making code formatting more consistent,
and allows automation of the code formatting so that maintainers can
focus more on the code itself instead of code formatting.
2019-06-23 23:49:10 -07:00

955 lines
22 KiB
C++

#include <d3d9.h>
#include <d3d11.h>
#include <dxgi.h>
#include "graphics-hook.h"
#include "../funchook.h"
#include "d3d9-patches.hpp"
typedef HRESULT(STDMETHODCALLTYPE *present_t)(IDirect3DDevice9 *, CONST RECT *,
CONST RECT *, HWND,
CONST RGNDATA *);
typedef HRESULT(STDMETHODCALLTYPE *present_ex_t)(IDirect3DDevice9 *,
CONST RECT *, CONST RECT *,
HWND, CONST RGNDATA *, DWORD);
typedef HRESULT(STDMETHODCALLTYPE *present_swap_t)(IDirect3DSwapChain9 *,
CONST RECT *, CONST RECT *,
HWND, CONST RGNDATA *,
DWORD);
typedef HRESULT(STDMETHODCALLTYPE *reset_t)(IDirect3DDevice9 *,
D3DPRESENT_PARAMETERS *);
typedef HRESULT(STDMETHODCALLTYPE *reset_ex_t)(IDirect3DDevice9 *,
D3DPRESENT_PARAMETERS *,
D3DDISPLAYMODEEX *);
typedef HRESULT(WINAPI *createfactory1_t)(REFIID, void **);
static struct func_hook present;
static struct func_hook present_ex;
static struct func_hook present_swap;
static struct func_hook reset;
static struct func_hook reset_ex;
struct d3d9_data {
HMODULE d3d9;
IDirect3DDevice9 *device; /* do not release */
uint32_t cx;
uint32_t cy;
D3DFORMAT d3d9_format;
DXGI_FORMAT dxgi_format;
bool using_shtex;
bool using_scale;
/* shared texture */
IDirect3DSurface9 *d3d9_copytex;
ID3D11Device *d3d11_device;
ID3D11DeviceContext *d3d11_context;
ID3D11Resource *d3d11_tex;
struct shtex_data *shtex_info;
HANDLE handle;
int patch;
/* shared memory */
IDirect3DSurface9 *copy_surfaces[NUM_BUFFERS];
IDirect3DSurface9 *render_targets[NUM_BUFFERS];
IDirect3DQuery9 *queries[NUM_BUFFERS];
struct shmem_data *shmem_info;
bool texture_mapped[NUM_BUFFERS];
volatile bool issued_queries[NUM_BUFFERS];
uint32_t pitch;
int cur_tex;
int copy_wait;
};
static struct d3d9_data data = {};
static void d3d9_free()
{
capture_free();
if (data.using_shtex) {
if (data.d3d11_tex)
data.d3d11_tex->Release();
if (data.d3d11_context)
data.d3d11_context->Release();
if (data.d3d11_device)
data.d3d11_device->Release();
if (data.d3d9_copytex)
data.d3d9_copytex->Release();
} else {
for (size_t i = 0; i < NUM_BUFFERS; i++) {
if (data.copy_surfaces[i]) {
if (data.texture_mapped[i])
data.copy_surfaces[i]->UnlockRect();
data.copy_surfaces[i]->Release();
}
if (data.render_targets[i])
data.render_targets[i]->Release();
if (data.queries[i])
data.queries[i]->Release();
}
}
memset(&data, 0, sizeof(data));
hlog("----------------- d3d9 capture freed -----------------");
}
static DXGI_FORMAT d3d9_to_dxgi_format(D3DFORMAT format)
{
switch ((unsigned long)format) {
case D3DFMT_A2B10G10R10:
return DXGI_FORMAT_R10G10B10A2_UNORM;
case D3DFMT_A8R8G8B8:
return DXGI_FORMAT_B8G8R8A8_UNORM;
case D3DFMT_X8R8G8B8:
return DXGI_FORMAT_B8G8R8X8_UNORM;
}
return DXGI_FORMAT_UNKNOWN;
}
const static D3D_FEATURE_LEVEL feature_levels[] = {
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
};
static inline bool shex_init_d3d11()
{
PFN_D3D11_CREATE_DEVICE create_device;
createfactory1_t create_factory;
D3D_FEATURE_LEVEL level_used;
IDXGIFactory *factory;
IDXGIAdapter *adapter;
HMODULE d3d11;
HMODULE dxgi;
HRESULT hr;
d3d11 = load_system_library("d3d11.dll");
if (!d3d11) {
hlog("d3d9_init: Failed to load D3D11");
return false;
}
dxgi = load_system_library("dxgi.dll");
if (!dxgi) {
hlog("d3d9_init: Failed to load DXGI");
return false;
}
create_factory =
(createfactory1_t)GetProcAddress(dxgi, "CreateDXGIFactory1");
if (!create_factory) {
hlog("d3d9_init: Failed to get CreateDXGIFactory1 address");
return false;
}
create_device = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(
d3d11, "D3D11CreateDevice");
if (!create_device) {
hlog("d3d9_init: Failed to get D3D11CreateDevice address");
return false;
}
hr = create_factory(__uuidof(IDXGIFactory1), (void **)&factory);
if (FAILED(hr)) {
hlog_hr("d3d9_init: Failed to create factory object", hr);
return false;
}
hr = factory->EnumAdapters(0, &adapter);
factory->Release();
if (FAILED(hr)) {
hlog_hr("d3d9_init: Failed to get adapter", hr);
return false;
}
hr = create_device(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0,
feature_levels,
sizeof(feature_levels) / sizeof(D3D_FEATURE_LEVEL),
D3D11_SDK_VERSION, &data.d3d11_device, &level_used,
&data.d3d11_context);
adapter->Release();
if (FAILED(hr)) {
hlog_hr("d3d9_init: Failed to create D3D11 device", hr);
return false;
}
return true;
}
static inline bool d3d9_shtex_init_shtex()
{
IDXGIResource *res;
HRESULT hr;
D3D11_TEXTURE2D_DESC desc = {};
desc.Width = data.cx;
desc.Height = data.cy;
desc.Format = data.dxgi_format;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
hr = data.d3d11_device->CreateTexture2D(
&desc, nullptr, (ID3D11Texture2D **)&data.d3d11_tex);
if (FAILED(hr)) {
hlog_hr("d3d9_shtex_init_shtex: Failed to create D3D11 texture",
hr);
return false;
}
hr = data.d3d11_tex->QueryInterface(__uuidof(IDXGIResource),
(void **)&res);
if (FAILED(hr)) {
hlog_hr("d3d9_shtex_init_shtex: Failed to query IDXGIResource",
hr);
return false;
}
hr = res->GetSharedHandle(&data.handle);
res->Release();
if (FAILED(hr)) {
hlog_hr("d3d9_shtex_init_shtex: Failed to get shared handle",
hr);
return false;
}
return true;
}
static inline bool d3d9_shtex_init_copytex()
{
struct d3d9_offsets offsets = global_hook_info->offsets.d3d9;
uint8_t *patch_addr = nullptr;
BOOL *p_is_d3d9 = nullptr;
uint8_t saved_data[MAX_PATCH_SIZE];
size_t patch_size = 0;
BOOL was_d3d9ex = false;
IDirect3DTexture9 *tex;
DWORD protect_val;
HRESULT hr;
if (offsets.d3d9_clsoff && offsets.is_d3d9ex_clsoff) {
uint8_t *device_ptr = (uint8_t *)(data.device);
uint8_t *d3d9_ptr =
*(uint8_t **)(device_ptr + offsets.d3d9_clsoff);
p_is_d3d9 = (BOOL *)(d3d9_ptr + offsets.is_d3d9ex_clsoff);
} else {
patch_addr = get_d3d9_patch_addr(data.d3d9, data.patch);
}
if (p_is_d3d9) {
was_d3d9ex = *p_is_d3d9;
*p_is_d3d9 = true;
} else if (patch_addr) {
patch_size = patch[data.patch].size;
VirtualProtect(patch_addr, patch_size, PAGE_EXECUTE_READWRITE,
&protect_val);
memcpy(saved_data, patch_addr, patch_size);
memcpy(patch_addr, patch[data.patch].data, patch_size);
}
hr = data.device->CreateTexture(data.cx, data.cy, 1,
D3DUSAGE_RENDERTARGET, data.d3d9_format,
D3DPOOL_DEFAULT, &tex, &data.handle);
if (p_is_d3d9) {
*p_is_d3d9 = was_d3d9ex;
} else if (patch_addr && patch_size) {
memcpy(patch_addr, saved_data, patch_size);
VirtualProtect(patch_addr, patch_size, protect_val,
&protect_val);
}
if (FAILED(hr)) {
hlog_hr("d3d9_shtex_init_copytex: Failed to create shared texture",
hr);
return false;
}
hr = tex->GetSurfaceLevel(0, &data.d3d9_copytex);
tex->Release();
if (FAILED(hr)) {
hlog_hr("d3d9_shtex_init_copytex: Failed to get surface level",
hr);
return false;
}
return true;
}
static bool d3d9_shtex_init(uint32_t cx, uint32_t cy, HWND window)
{
data.using_shtex = true;
if (!shex_init_d3d11()) {
return false;
}
if (!d3d9_shtex_init_shtex()) {
return false;
}
if (!d3d9_shtex_init_copytex()) {
return false;
}
if (!capture_init_shtex(&data.shtex_info, window, cx, cy, data.cx,
data.cy, data.dxgi_format, false,
(uintptr_t)data.handle)) {
return false;
}
hlog("d3d9 shared texture capture successful");
return true;
}
static bool d3d9_shmem_init_buffers(size_t buffer)
{
HRESULT hr;
hr = data.device->CreateOffscreenPlainSurface(
data.cx, data.cy, data.d3d9_format, D3DPOOL_SYSTEMMEM,
&data.copy_surfaces[buffer], nullptr);
if (FAILED(hr)) {
hlog_hr("d3d9_shmem_init_buffers: Failed to create surface",
hr);
return false;
}
if (buffer == 0) {
D3DLOCKED_RECT rect;
hr = data.copy_surfaces[buffer]->LockRect(&rect, nullptr,
D3DLOCK_READONLY);
if (FAILED(hr)) {
hlog_hr("d3d9_shmem_init_buffers: Failed to lock "
"buffer",
hr);
return false;
}
data.pitch = rect.Pitch;
data.copy_surfaces[buffer]->UnlockRect();
}
hr = data.device->CreateRenderTarget(data.cx, data.cy, data.d3d9_format,
D3DMULTISAMPLE_NONE, 0, false,
&data.render_targets[buffer],
nullptr);
if (FAILED(hr)) {
hlog_hr("d3d9_shmem_init_buffers: Failed to create render "
"target",
hr);
return false;
}
hr = data.device->CreateQuery(D3DQUERYTYPE_EVENT,
&data.queries[buffer]);
if (FAILED(hr)) {
hlog_hr("d3d9_shmem_init_buffers: Failed to create query", hr);
return false;
}
return true;
}
static bool d3d9_shmem_init(uint32_t cx, uint32_t cy, HWND window)
{
data.using_shtex = false;
for (size_t i = 0; i < NUM_BUFFERS; i++) {
if (!d3d9_shmem_init_buffers(i)) {
return false;
}
}
if (!capture_init_shmem(&data.shmem_info, window, cx, cy, data.cx,
data.cy, data.pitch, data.dxgi_format, false)) {
return false;
}
hlog("d3d9 memory capture successful");
return true;
}
static bool d3d9_get_swap_desc(D3DPRESENT_PARAMETERS &pp)
{
IDirect3DSwapChain9 *swap = nullptr;
HRESULT hr;
hr = data.device->GetSwapChain(0, &swap);
if (FAILED(hr)) {
hlog_hr("d3d9_get_swap_desc: Failed to get swap chain", hr);
return false;
}
hr = swap->GetPresentParameters(&pp);
swap->Release();
if (FAILED(hr)) {
hlog_hr("d3d9_get_swap_desc: Failed to get "
"presentation parameters",
hr);
return false;
}
return true;
}
static bool d3d9_init_format_backbuffer(uint32_t &cx, uint32_t &cy,
HWND &window)
{
IDirect3DSurface9 *back_buffer = nullptr;
D3DPRESENT_PARAMETERS pp;
D3DSURFACE_DESC desc;
HRESULT hr;
if (!d3d9_get_swap_desc(pp)) {
return false;
}
hr = data.device->GetRenderTarget(0, &back_buffer);
if (FAILED(hr)) {
return false;
}
hr = back_buffer->GetDesc(&desc);
back_buffer->Release();
if (FAILED(hr)) {
hlog_hr("d3d9_init_format_backbuffer: Failed to get "
"backbuffer descriptor",
hr);
return false;
}
data.d3d9_format = desc.Format;
data.dxgi_format = d3d9_to_dxgi_format(desc.Format);
data.using_scale = global_hook_info->use_scale;
window = pp.hDeviceWindow;
cx = desc.Width;
cy = desc.Height;
if (data.using_scale) {
data.cx = global_hook_info->cx;
data.cy = global_hook_info->cy;
} else {
data.cx = desc.Width;
data.cy = desc.Height;
}
return true;
}
static bool d3d9_init_format_swapchain(uint32_t &cx, uint32_t &cy, HWND &window)
{
D3DPRESENT_PARAMETERS pp;
if (!d3d9_get_swap_desc(pp)) {
return false;
}
data.dxgi_format = d3d9_to_dxgi_format(pp.BackBufferFormat);
data.d3d9_format = pp.BackBufferFormat;
data.using_scale = global_hook_info->use_scale;
window = pp.hDeviceWindow;
cx = pp.BackBufferWidth;
cy = pp.BackBufferHeight;
if (data.using_scale) {
data.cx = global_hook_info->cx;
data.cy = global_hook_info->cy;
} else {
data.cx = pp.BackBufferWidth;
data.cy = pp.BackBufferHeight;
}
return true;
}
static void d3d9_init(IDirect3DDevice9 *device)
{
IDirect3DDevice9Ex *d3d9ex = nullptr;
bool has_d3d9ex_bool_offset =
global_hook_info->offsets.d3d9.d3d9_clsoff &&
global_hook_info->offsets.d3d9.is_d3d9ex_clsoff;
bool success;
uint32_t cx = 0;
uint32_t cy = 0;
HWND window = nullptr;
HRESULT hr;
data.d3d9 = get_system_module("d3d9.dll");
data.device = device;
hr = device->QueryInterface(__uuidof(IDirect3DDevice9Ex),
(void **)&d3d9ex);
if (SUCCEEDED(hr)) {
d3d9ex->Release();
data.patch = -1;
} else if (!has_d3d9ex_bool_offset) {
data.patch = get_d3d9_patch(data.d3d9);
} else {
data.patch = -1;
}
if (!d3d9_init_format_backbuffer(cx, cy, window)) {
if (!d3d9_init_format_swapchain(cx, cy, window)) {
return;
}
}
if (global_hook_info->force_shmem ||
(!d3d9ex && data.patch == -1 && !has_d3d9ex_bool_offset)) {
success = d3d9_shmem_init(cx, cy, window);
} else {
success = d3d9_shtex_init(cx, cy, window);
}
if (!success)
d3d9_free();
}
static inline HRESULT get_backbuffer(IDirect3DDevice9 *device,
IDirect3DSurface9 **surface)
{
static bool use_backbuffer = false;
static bool checked_exceptions = false;
if (!checked_exceptions) {
if (_strcmpi(get_process_name(), "hotd_ng.exe") == 0)
use_backbuffer = true;
checked_exceptions = true;
}
if (use_backbuffer) {
return device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO,
surface);
} else {
return device->GetRenderTarget(0, surface);
}
}
static inline void d3d9_shtex_capture(IDirect3DSurface9 *backbuffer)
{
D3DTEXTUREFILTERTYPE filter;
HRESULT hr;
filter = data.using_scale ? D3DTEXF_LINEAR : D3DTEXF_NONE;
hr = data.device->StretchRect(backbuffer, nullptr, data.d3d9_copytex,
nullptr, filter);
if (FAILED(hr))
hlog_hr("d3d9_shtex_capture: StretchRect failed", hr);
}
static inline void d3d9_shmem_capture_queue_copy()
{
for (int i = 0; i < NUM_BUFFERS; i++) {
IDirect3DSurface9 *target = data.copy_surfaces[i];
D3DLOCKED_RECT rect;
HRESULT hr;
if (!data.issued_queries[i]) {
continue;
}
if (data.queries[i]->GetData(0, 0, 0) != S_OK) {
continue;
}
data.issued_queries[i] = false;
hr = target->LockRect(&rect, nullptr, D3DLOCK_READONLY);
if (SUCCEEDED(hr)) {
data.texture_mapped[i] = true;
shmem_copy_data(i, rect.pBits);
}
break;
}
}
static inline void d3d9_shmem_capture(IDirect3DSurface9 *backbuffer)
{
D3DTEXTUREFILTERTYPE filter;
IDirect3DSurface9 *copy;
int next_tex;
HRESULT hr;
d3d9_shmem_capture_queue_copy();
next_tex = (data.cur_tex == NUM_BUFFERS - 1) ? 0 : data.cur_tex + 1;
filter = data.using_scale ? D3DTEXF_LINEAR : D3DTEXF_NONE;
copy = data.render_targets[data.cur_tex];
hr = data.device->StretchRect(backbuffer, nullptr, copy, nullptr,
filter);
if (FAILED(hr)) {
hlog_hr("d3d9_shmem_capture: StretchRect failed", hr);
return;
}
if (data.copy_wait < NUM_BUFFERS - 1) {
data.copy_wait++;
} else {
IDirect3DSurface9 *src = data.render_targets[next_tex];
IDirect3DSurface9 *dst = data.copy_surfaces[next_tex];
if (shmem_texture_data_lock(next_tex)) {
dst->UnlockRect();
data.texture_mapped[next_tex] = false;
shmem_texture_data_unlock(next_tex);
}
hr = data.device->GetRenderTargetData(src, dst);
if (FAILED(hr)) {
hlog_hr("d3d9_shmem_capture: GetRenderTargetData "
"failed",
hr);
}
data.queries[next_tex]->Issue(D3DISSUE_END);
data.issued_queries[next_tex] = true;
}
data.cur_tex = next_tex;
}
static void d3d9_capture(IDirect3DDevice9 *device,
IDirect3DSurface9 *backbuffer)
{
if (capture_should_stop()) {
d3d9_free();
}
if (capture_should_init()) {
d3d9_init(device);
}
if (capture_ready()) {
if (data.device != device) {
d3d9_free();
return;
}
if (data.using_shtex)
d3d9_shtex_capture(backbuffer);
else
d3d9_shmem_capture(backbuffer);
}
}
/* this is used just in case Present calls PresentEx or vise versa. */
static int present_recurse = 0;
static inline void present_begin(IDirect3DDevice9 *device,
IDirect3DSurface9 *&backbuffer)
{
HRESULT hr;
if (!present_recurse) {
hr = get_backbuffer(device, &backbuffer);
if (FAILED(hr)) {
hlog_hr("d3d9_shmem_capture: Failed to get "
"backbuffer",
hr);
}
if (!global_hook_info->capture_overlay) {
d3d9_capture(device, backbuffer);
}
}
present_recurse++;
}
static inline void present_end(IDirect3DDevice9 *device,
IDirect3DSurface9 *backbuffer)
{
present_recurse--;
if (!present_recurse) {
if (global_hook_info->capture_overlay) {
if (!present_recurse)
d3d9_capture(device, backbuffer);
}
if (backbuffer)
backbuffer->Release();
}
}
static bool hooked_reset = false;
static void setup_reset_hooks(IDirect3DDevice9 *device);
static HRESULT STDMETHODCALLTYPE hook_present(IDirect3DDevice9 *device,
CONST RECT *src_rect,
CONST RECT *dst_rect,
HWND override_window,
CONST RGNDATA *dirty_region)
{
IDirect3DSurface9 *backbuffer = nullptr;
HRESULT hr;
if (!hooked_reset)
setup_reset_hooks(device);
present_begin(device, backbuffer);
unhook(&present);
present_t call = (present_t)present.call_addr;
hr = call(device, src_rect, dst_rect, override_window, dirty_region);
rehook(&present);
present_end(device, backbuffer);
return hr;
}
static HRESULT STDMETHODCALLTYPE hook_present_ex(
IDirect3DDevice9 *device, CONST RECT *src_rect, CONST RECT *dst_rect,
HWND override_window, CONST RGNDATA *dirty_region, DWORD flags)
{
IDirect3DSurface9 *backbuffer = nullptr;
HRESULT hr;
if (!hooked_reset)
setup_reset_hooks(device);
present_begin(device, backbuffer);
unhook(&present_ex);
present_ex_t call = (present_ex_t)present_ex.call_addr;
hr = call(device, src_rect, dst_rect, override_window, dirty_region,
flags);
rehook(&present_ex);
present_end(device, backbuffer);
return hr;
}
static HRESULT STDMETHODCALLTYPE hook_present_swap(
IDirect3DSwapChain9 *swap, CONST RECT *src_rect, CONST RECT *dst_rect,
HWND override_window, CONST RGNDATA *dirty_region, DWORD flags)
{
IDirect3DSurface9 *backbuffer = nullptr;
IDirect3DDevice9 *device = nullptr;
HRESULT hr;
if (!present_recurse) {
hr = swap->GetDevice(&device);
if (SUCCEEDED(hr)) {
device->Release();
}
}
if (device) {
if (!hooked_reset)
setup_reset_hooks(device);
present_begin(device, backbuffer);
}
unhook(&present_swap);
present_swap_t call = (present_swap_t)present_swap.call_addr;
hr = call(swap, src_rect, dst_rect, override_window, dirty_region,
flags);
rehook(&present_swap);
if (device)
present_end(device, backbuffer);
return hr;
}
static HRESULT STDMETHODCALLTYPE hook_reset(IDirect3DDevice9 *device,
D3DPRESENT_PARAMETERS *params)
{
HRESULT hr;
if (capture_active())
d3d9_free();
unhook(&reset);
reset_t call = (reset_t)reset.call_addr;
hr = call(device, params);
rehook(&reset);
return hr;
}
static HRESULT STDMETHODCALLTYPE hook_reset_ex(IDirect3DDevice9 *device,
D3DPRESENT_PARAMETERS *params,
D3DDISPLAYMODEEX *dmex)
{
HRESULT hr;
if (capture_active())
d3d9_free();
unhook(&reset_ex);
reset_ex_t call = (reset_ex_t)reset_ex.call_addr;
hr = call(device, params, dmex);
rehook(&reset_ex);
return hr;
}
static void setup_reset_hooks(IDirect3DDevice9 *device)
{
IDirect3DDevice9Ex *d3d9ex = nullptr;
uintptr_t *vtable = *(uintptr_t **)device;
HRESULT hr;
hook_init(&reset, (void *)vtable[16], (void *)hook_reset,
"IDirect3DDevice9::Reset");
rehook(&reset);
hr = device->QueryInterface(__uuidof(IDirect3DDevice9Ex),
(void **)&d3d9ex);
if (SUCCEEDED(hr)) {
hook_init(&reset_ex, (void *)vtable[132], (void *)hook_reset_ex,
"IDirect3DDevice9Ex::ResetEx");
rehook(&reset_ex);
d3d9ex->Release();
}
hooked_reset = true;
}
typedef HRESULT(WINAPI *d3d9create_ex_t)(UINT, IDirect3D9Ex **);
static bool manually_get_d3d9_addrs(HMODULE d3d9_module, void **present_addr,
void **present_ex_addr,
void **present_swap_addr)
{
d3d9create_ex_t create_ex;
D3DPRESENT_PARAMETERS pp;
HRESULT hr;
IDirect3DDevice9Ex *device;
IDirect3D9Ex *d3d9ex;
hlog("D3D9 values invalid, manually obtaining");
create_ex = (d3d9create_ex_t)GetProcAddress(d3d9_module,
"Direct3DCreate9Ex");
if (!create_ex) {
hlog("Failed to load Direct3DCreate9Ex");
return false;
}
if (FAILED(create_ex(D3D_SDK_VERSION, &d3d9ex))) {
hlog("Failed to create D3D9 context");
return false;
}
memset(&pp, 0, sizeof(pp));
pp.Windowed = 1;
pp.SwapEffect = D3DSWAPEFFECT_FLIP;
pp.BackBufferFormat = D3DFMT_A8R8G8B8;
pp.BackBufferCount = 1;
pp.hDeviceWindow = (HWND)dummy_window;
pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
hr = d3d9ex->CreateDeviceEx(
D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, dummy_window,
D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_NOWINDOWCHANGES,
&pp, NULL, &device);
d3d9ex->Release();
if (SUCCEEDED(hr)) {
uintptr_t *vtable = *(uintptr_t **)device;
IDirect3DSwapChain9 *swap;
*present_addr = (void *)vtable[17];
*present_ex_addr = (void *)vtable[121];
hr = device->GetSwapChain(0, &swap);
if (SUCCEEDED(hr)) {
vtable = *(uintptr_t **)swap;
*present_swap_addr = (void *)vtable[3];
swap->Release();
}
device->Release();
} else {
hlog("Failed to create D3D9 device");
return false;
}
return true;
}
bool hook_d3d9(void)
{
HMODULE d3d9_module = get_system_module("d3d9.dll");
uint32_t d3d9_size;
void *present_addr = nullptr;
void *present_ex_addr = nullptr;
void *present_swap_addr = nullptr;
if (!d3d9_module) {
return false;
}
d3d9_size = module_size(d3d9_module);
if (global_hook_info->offsets.d3d9.present < d3d9_size &&
global_hook_info->offsets.d3d9.present_ex < d3d9_size &&
global_hook_info->offsets.d3d9.present_swap < d3d9_size) {
present_addr = get_offset_addr(
d3d9_module, global_hook_info->offsets.d3d9.present);
present_ex_addr = get_offset_addr(
d3d9_module, global_hook_info->offsets.d3d9.present_ex);
present_swap_addr = get_offset_addr(
d3d9_module,
global_hook_info->offsets.d3d9.present_swap);
} else {
if (!dummy_window) {
return false;
}
if (!manually_get_d3d9_addrs(d3d9_module, &present_addr,
&present_ex_addr,
&present_swap_addr)) {
hlog("Failed to get D3D9 values");
return true;
}
}
if (!present_addr && !present_ex_addr && !present_swap_addr) {
hlog("Invalid D3D9 values");
return true;
}
if (present_swap_addr) {
hook_init(&present_swap, present_swap_addr,
(void *)hook_present_swap,
"IDirect3DSwapChain9::Present");
rehook(&present_swap);
}
if (present_ex_addr) {
hook_init(&present_ex, present_ex_addr, (void *)hook_present_ex,
"IDirect3DDevice9Ex::PresentEx");
rehook(&present_ex);
}
if (present_addr) {
hook_init(&present, present_addr, (void *)hook_present,
"IDirect3DDevice9::Present");
rehook(&present);
}
hlog("Hooked D3D9");
return true;
}