win-capture: Add graphics-hook library

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)
This commit is contained in:
jp9000 2014-11-10 15:04:40 -08:00
parent cb0db6240b
commit a5872955f4
13 changed files with 4921 additions and 0 deletions

View File

@ -24,5 +24,6 @@ target_link_libraries(win-capture
install_obs_plugin_with_data(win-capture data)
add_subdirectory(graphics-hook)
add_subdirectory(get-graphics-offsets)
add_subdirectory(inject-helper)

View File

@ -0,0 +1,39 @@
project(graphics-hook)
set(graphics-hook_HEADERS
graphics-hook.h
../graphics-hook-info.h
../hook-helpers.h
../funchook.h
../obfuscate.h
gl-decs.h
d3d9-patches.hpp)
set(graphics-hook_SOURCES
graphics-hook.c
../funchook.c
../obfuscate.c
gl-capture.c
d3d9-capture.cpp
dxgi-capture.cpp
d3d10-capture.cpp
d3d11-capture.cpp)
add_library(graphics-hook MODULE
${graphics-hook_SOURCES}
${graphics-hook_HEADERS})
target_link_libraries(graphics-hook
ipc-util)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_output_suffix "64")
else()
set(_output_suffix "32")
endif()
set_target_properties(graphics-hook
PROPERTIES
OUTPUT_NAME "graphics-hook${_output_suffix}")
install_obs_datatarget(graphics-hook "obs-plugins/win-capture")

View File

@ -0,0 +1,805 @@
#define _CRT_SECURE_NO_WARNINGS
#include <d3d10.h>
#include <dxgi.h>
#include "dxgi-helpers.hpp"
#include "graphics-hook.h"
#include "../funchook.h"
struct d3d10_data {
ID3D10Device *device; /* do not release */
uint32_t base_cx;
uint32_t base_cy;
uint32_t cx;
uint32_t cy;
DXGI_FORMAT format;
bool using_shtex : 1;
bool using_scale : 1;
bool multisampled : 1;
ID3D10Texture2D *scale_tex;
ID3D10ShaderResourceView *scale_resource;
ID3D10VertexShader *vertex_shader;
ID3D10InputLayout *vertex_layout;
ID3D10PixelShader *pixel_shader;
ID3D10SamplerState *sampler_state;
ID3D10BlendState *blend_state;
ID3D10DepthStencilState *zstencil_state;
ID3D10RasterizerState *raster_state;
ID3D10Buffer *vertex_buffer;
union {
/* shared texture */
struct {
struct shtex_data *shtex_info;
ID3D10Texture2D *texture;
ID3D10RenderTargetView *render_target;
HANDLE handle;
};
/* shared memory */
struct {
struct shmem_data *shmem_info;
ID3D10Texture2D *copy_surfaces[NUM_BUFFERS];
ID3D10Texture2D *textures[NUM_BUFFERS];
ID3D10RenderTargetView *render_targets[NUM_BUFFERS];
bool texture_ready[NUM_BUFFERS];
bool texture_mapped[NUM_BUFFERS];
uint32_t pitch;
int cur_tex;
int copy_wait;
};
};
};
struct d3d10_data data = {};
void d3d10_free(void)
{
if (data.scale_tex)
data.scale_tex->Release();
if (data.scale_resource)
data.scale_resource->Release();
if (data.vertex_shader)
data.vertex_shader->Release();
if (data.vertex_layout)
data.vertex_layout->Release();
if (data.pixel_shader)
data.pixel_shader->Release();
if (data.sampler_state)
data.sampler_state->Release();
if (data.blend_state)
data.blend_state->Release();
if (data.zstencil_state)
data.zstencil_state->Release();
if (data.raster_state)
data.raster_state->Release();
if (data.vertex_buffer)
data.vertex_buffer->Release();
capture_free();
if (data.using_shtex) {
if (data.texture)
data.texture->Release();
if (data.render_target)
data.render_target->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]->Unmap(0);
data.copy_surfaces[i]->Release();
}
if (data.textures[i])
data.textures[i]->Release();
if (data.render_targets[i])
data.render_targets[i]->Release();
}
}
memset(&data, 0, sizeof(data));
hlog("----------------- d3d10 capture freed ----------------");
}
static bool create_d3d10_stage_surface(ID3D10Texture2D **tex)
{
HRESULT hr;
D3D10_TEXTURE2D_DESC desc = {};
desc.Width = data.cx;
desc.Height = data.cy;
desc.Format = data.format;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_STAGING;
desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ;
hr = data.device->CreateTexture2D(&desc, nullptr, tex);
if (FAILED(hr)) {
hlog_hr("create_d3d10_stage_surface: failed to create texture",
hr);
return false;
}
return true;
}
static bool create_d3d10_tex(uint32_t cx, uint32_t cy,
ID3D10Texture2D **tex,
ID3D10ShaderResourceView **resource,
ID3D10RenderTargetView **render_target,
HANDLE *handle)
{
UINT flags = 0;
UINT misc_flags = 0;
HRESULT hr;
if (!!resource)
flags |= D3D10_BIND_SHADER_RESOURCE;
if (!!render_target)
flags |= D3D10_BIND_RENDER_TARGET;
if (!!handle)
misc_flags |= D3D10_RESOURCE_MISC_SHARED;
D3D10_TEXTURE2D_DESC desc = {};
desc.Width = cx;
desc.Height = cy;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = data.format;
desc.BindFlags = flags;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DEFAULT;
desc.MiscFlags = misc_flags;
hr = data.device->CreateTexture2D(&desc, nullptr, tex);
if (FAILED(hr)) {
hlog_hr("create_d3d10_tex: failed to create texture", hr);
return false;
}
if (!!resource) {
D3D10_SHADER_RESOURCE_VIEW_DESC res_desc = {};
res_desc.Format = data.format;
res_desc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
res_desc.Texture2D.MipLevels = 1;
hr = data.device->CreateShaderResourceView(*tex, &res_desc,
resource);
if (FAILED(hr)) {
hlog_hr("create_d3d10_tex: failed to create resource "
"view", hr);
return false;
}
}
if (!!render_target) {
hr = data.device->CreateRenderTargetView(*tex, nullptr,
render_target);
if (FAILED(hr)) {
hlog_hr("create_d3d10_tex: failed to create render "
"target view", hr);
return false;
}
}
if (!!handle) {
IDXGIResource *dxgi_res;
hr = (*tex)->QueryInterface(__uuidof(IDXGIResource),
(void**)&dxgi_res);
if (FAILED(hr)) {
hlog_hr("create_d3d10_tex: failed to query "
"IDXGIResource interface from texture", hr);
return false;
}
hr = dxgi_res->GetSharedHandle(handle);
dxgi_res->Release();
if (FAILED(hr)) {
hlog_hr("create_d3d10_tex: failed to get shared handle",
hr);
return false;
}
}
return true;
}
static inline bool d3d10_init_format(IDXGISwapChain *swap, HWND &window)
{
DXGI_SWAP_CHAIN_DESC desc;
HRESULT hr;
hr = swap->GetDesc(&desc);
if (FAILED(hr)) {
hlog_hr("d3d10_init_format: swap->GetDesc failed", hr);
return false;
}
data.format = fix_dxgi_format(desc.BufferDesc.Format);
data.multisampled = desc.SampleDesc.Count > 1;
window = desc.OutputWindow;
data.base_cx = desc.BufferDesc.Width;
data.base_cy = desc.BufferDesc.Height;
if (data.using_scale) {
data.cx = global_hook_info->cx;
data.cy = global_hook_info->cy;
} else {
data.cx = desc.BufferDesc.Width;
data.cy = desc.BufferDesc.Height;
}
return true;
}
static inline bool d3d10_init_vertex_shader(void)
{
D3D10_INPUT_ELEMENT_DESC desc[2];
uint8_t *vs_data;
size_t size;
HRESULT hr;
vs_data = get_d3d1x_vertex_shader(&size);
hr = data.device->CreateVertexShader(vs_data, size,
&data.vertex_shader);
if (FAILED(hr)) {
hlog_hr("d3d10_init_vertex_shader: failed to create shader",
hr);
return false;
}
desc[0].SemanticName = "SV_Position";
desc[0].SemanticIndex = 0;
desc[0].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
desc[0].InputSlot = 0;
desc[0].AlignedByteOffset = 0;
desc[0].InputSlotClass = D3D10_INPUT_PER_VERTEX_DATA;
desc[0].InstanceDataStepRate = 0;
desc[1].SemanticName = "TEXCOORD";
desc[1].SemanticIndex = 0;
desc[1].Format = DXGI_FORMAT_R32G32_FLOAT;
desc[1].InputSlot = 0;
desc[1].AlignedByteOffset = D3D10_APPEND_ALIGNED_ELEMENT;
desc[1].InputSlotClass = D3D10_INPUT_PER_VERTEX_DATA;
desc[1].InstanceDataStepRate = 0;
hr = data.device->CreateInputLayout(desc, 2, vs_data, size,
&data.vertex_layout);
if (FAILED(hr)) {
hlog_hr("d3d10_init_vertex_shader: failed to create layout",
hr);
return false;
}
return true;
}
static inline bool d3d10_init_pixel_shader(void)
{
uint8_t *ps_data;
size_t size;
HRESULT hr;
ps_data = get_d3d1x_pixel_shader(&size);
hr = data.device->CreatePixelShader(ps_data, size, &data.pixel_shader);
if (FAILED(hr)) {
hlog_hr("d3d10_init_pixel_shader: failed to create shader", hr);
return false;
}
return true;
}
static inline bool d3d10_init_sampler_state(void)
{
HRESULT hr;
D3D10_SAMPLER_DESC desc = {};
desc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR;
desc.AddressU = D3D10_TEXTURE_ADDRESS_CLAMP;
desc.AddressV = D3D10_TEXTURE_ADDRESS_CLAMP;
desc.AddressW = D3D10_TEXTURE_ADDRESS_CLAMP;
hr = data.device->CreateSamplerState(&desc, &data.sampler_state);
if (FAILED(hr)) {
hlog_hr("d3d10_init_sampler_state: failed to create sampler "
"state", hr);
return false;
}
return true;
}
static inline bool d3d10_init_blend_state(void)
{
D3D10_BLEND_DESC desc = {};
HRESULT hr;
for (size_t i = 0; i < 8; i++)
desc.RenderTargetWriteMask[i] = D3D10_COLOR_WRITE_ENABLE_ALL;
hr = data.device->CreateBlendState(&desc, &data.blend_state);
if (FAILED(hr)) {
hlog_hr("d3d10_init_blend_state: failed to create blend state",
hr);
return false;
}
return true;
}
static inline bool d3d10_init_zstencil_state(void)
{
D3D10_DEPTH_STENCIL_DESC desc = {}; /* defaults all to off */
HRESULT hr;
hr = data.device->CreateDepthStencilState(&desc, &data.zstencil_state);
if (FAILED(hr)) {
hlog_hr("d3d10_init_zstencil_state: failed to create "
"zstencil state", hr);
return false;
}
return true;
}
static inline bool d3d10_init_raster_state(void)
{
D3D10_RASTERIZER_DESC desc = {};
HRESULT hr;
desc.FillMode = D3D10_FILL_SOLID;
desc.CullMode = D3D10_CULL_NONE;
hr = data.device->CreateRasterizerState(&desc, &data.raster_state);
if (FAILED(hr)) {
hlog_hr("d3d10_init_raster_state: failed to create raster "
"state", hr);
return false;
}
return true;
}
#define NUM_VERTS 4
static inline bool d3d10_init_vertex_buffer(void)
{
HRESULT hr;
const vertex verts[NUM_VERTS] = {
{{-1.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f}},
{{-1.0f, -1.0f, 0.0f, 1.0f}, {0.0f, 1.0f}},
{{ 1.0f, 1.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
{{ 1.0f, -1.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}
};
D3D10_BUFFER_DESC desc;
desc.ByteWidth = sizeof(vertex) * NUM_VERTS;
desc.Usage = D3D10_USAGE_DEFAULT;
desc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA srd = {};
srd.pSysMem = (const void*)verts;
hr = data.device->CreateBuffer(&desc, &srd, &data.vertex_buffer);
if (FAILED(hr)) {
hlog_hr("d3d10_init_vertex_buffer: failed to create vertex "
"buffer", hr);
return false;
}
return true;
}
static bool d3d10_init_scaling(void)
{
bool success;
success = create_d3d10_tex(data.base_cx, data.base_cy,
&data.scale_tex, &data.scale_resource, nullptr,
nullptr);
if (!success) {
hlog("d3d10_init_scaling: failed to create scale texture");
return false;
}
if (!d3d10_init_vertex_shader()) {
return false;
}
if (!d3d10_init_pixel_shader()) {
return false;
}
if (!d3d10_init_sampler_state()) {
return false;
}
if (!d3d10_init_blend_state()) {
return false;
}
if (!d3d10_init_zstencil_state()) {
return false;
}
if (!d3d10_init_raster_state()) {
return false;
}
if (!d3d10_init_vertex_buffer()) {
return false;
}
return true;
}
static bool d3d10_shmem_init_buffers(size_t idx)
{
bool success;
success = create_d3d10_stage_surface(&data.copy_surfaces[idx]);
if (!success) {
hlog("d3d10_shmem_init_buffers: failed to create copy surface");
return false;
}
if (idx == 0) {
D3D10_MAPPED_TEXTURE2D map = {};
HRESULT hr;
hr = data.copy_surfaces[idx]->Map(0, D3D10_MAP_READ, 0, &map);
if (FAILED(hr)) {
hlog_hr("d3d10_shmem_init_buffers: failed to get "
"pitch", hr);
return false;
}
data.pitch = map.RowPitch;
data.copy_surfaces[idx]->Unmap(0);
}
success = create_d3d10_tex(data.cx, data.cy, &data.textures[idx],
nullptr, &data.render_targets[idx], nullptr);
if (!success) {
hlog("d3d10_shmem_init_buffers: failed to create texture");
return false;
}
return true;
}
static bool d3d10_shmem_init(HWND window)
{
data.using_shtex = false;
for (size_t i = 0; i < NUM_BUFFERS; i++) {
if (!d3d10_shmem_init_buffers(i)) {
return false;
}
}
if (!capture_init_shmem(&data.shmem_info, window,
data.base_cx, data.base_cy, data.cx, data.cy,
data.pitch, data.format, false)) {
return false;
}
hlog("d3d10 memory capture successful");
return true;
}
static bool d3d10_shtex_init(HWND window)
{
ID3D10ShaderResourceView *resource = nullptr;
bool success;
data.using_shtex = true;
success = create_d3d10_tex(data.cx, data.cy, &data.texture, &resource,
&data.render_target, &data.handle);
if (resource)
resource->Release();
if (!success) {
hlog("d3d10_shtex_init: failed to create texture");
return false;
}
if (!capture_init_shtex(&data.shtex_info, window,
data.base_cx, data.base_cy, data.cx, data.cy,
data.format, false, (uint32_t)data.handle)) {
return false;
}
hlog("d3d10 shared texture capture successful");
return true;
}
static void d3d10_init(IDXGISwapChain *swap)
{
bool success = true;
HWND window;
HRESULT hr;
data.using_scale = global_hook_info->use_scale;
hr = swap->GetDevice(__uuidof(ID3D10Device), (void**)&data.device);
if (FAILED(hr)) {
hlog_hr("d3d10_init: failed to get device from swap", hr);
return;
}
/* remove the unneeded extra reference */
data.device->Release();
if (!d3d10_init_format(swap, window)) {
return;
}
if (data.using_scale && !d3d10_init_scaling()) {
hlog("d3d10_init: failed to initialize scaling");
success = false;
}
if (success) {
if (global_hook_info->force_shmem) {
success = d3d10_shmem_init(window);
} else {
success = d3d10_shtex_init(window);
}
}
if (!success)
d3d10_free();
}
#define MAX_RENDER_TARGETS D3D10_SIMULTANEOUS_RENDER_TARGET_COUNT
#define MAX_SO_TARGETS 4
struct d3d10_state {
ID3D10GeometryShader *geom_shader;
ID3D10InputLayout *vertex_layout;
D3D10_PRIMITIVE_TOPOLOGY topology;
ID3D10Buffer *vertex_buffer;
UINT vb_stride;
UINT vb_offset;
ID3D10BlendState *blend_state;
float blend_factor[4];
UINT sample_mask;
ID3D10DepthStencilState *zstencil_state;
UINT zstencil_ref;
ID3D10RenderTargetView *render_targets[MAX_RENDER_TARGETS];
ID3D10DepthStencilView *zstencil_view;
ID3D10SamplerState *sampler_state;
ID3D10PixelShader *pixel_shader;
ID3D10ShaderResourceView *resource;
ID3D10RasterizerState *raster_state;
UINT num_viewports;
D3D10_VIEWPORT *viewports;
ID3D10Buffer *stream_output_targets[MAX_SO_TARGETS];
UINT so_offsets[MAX_SO_TARGETS];
ID3D10VertexShader *vertex_shader;
};
static inline void d3d10_save_state(struct d3d10_state *state)
{
data.device->GSGetShader(&state->geom_shader);
data.device->IAGetInputLayout(&state->vertex_layout);
data.device->IAGetPrimitiveTopology(&state->topology);
data.device->IAGetVertexBuffers(0, 1, &state->vertex_buffer,
&state->vb_stride, &state->vb_offset);
data.device->OMGetBlendState(&state->blend_state, state->blend_factor,
&state->sample_mask);
data.device->OMGetDepthStencilState(&state->zstencil_state,
&state->zstencil_ref);
data.device->OMGetRenderTargets(MAX_RENDER_TARGETS,
state->render_targets, &state->zstencil_view);
data.device->PSGetSamplers(0, 1, &state->sampler_state);
data.device->PSGetShader(&state->pixel_shader);
data.device->PSGetShaderResources(0, 1, &state->resource);
data.device->RSGetState(&state->raster_state);
data.device->RSGetViewports(&state->num_viewports, nullptr);
if (state->num_viewports) {
state->viewports = (D3D10_VIEWPORT*)malloc(
sizeof(D3D10_VIEWPORT) * state->num_viewports);
data.device->RSGetViewports(&state->num_viewports,
state->viewports);
}
data.device->SOGetTargets(MAX_SO_TARGETS, state->stream_output_targets,
state->so_offsets);
data.device->VSGetShader(&state->vertex_shader);
}
static inline void safe_release(IUnknown *p)
{
if (p) p->Release();
}
static inline void d3d10_restore_state(struct d3d10_state *state)
{
data.device->GSSetShader(state->geom_shader);
data.device->IASetInputLayout(state->vertex_layout);
data.device->IASetPrimitiveTopology(state->topology);
data.device->IASetVertexBuffers(0, 1, &state->vertex_buffer,
&state->vb_stride, &state->vb_offset);
data.device->OMSetBlendState(state->blend_state, state->blend_factor,
state->sample_mask);
data.device->OMSetDepthStencilState(state->zstencil_state,
state->zstencil_ref);
data.device->OMSetRenderTargets(MAX_RENDER_TARGETS,
state->render_targets, state->zstencil_view);
data.device->PSSetSamplers(0, 1, &state->sampler_state);
data.device->PSSetShader(state->pixel_shader);
data.device->PSSetShaderResources(0, 1, &state->resource);
data.device->RSSetState(state->raster_state);
data.device->RSSetViewports(state->num_viewports, state->viewports);
data.device->SOSetTargets(MAX_SO_TARGETS, state->stream_output_targets,
state->so_offsets);
data.device->VSSetShader(state->vertex_shader);
safe_release(state->geom_shader);
safe_release(state->vertex_layout);
safe_release(state->vertex_buffer);
safe_release(state->blend_state);
safe_release(state->zstencil_state);
for (size_t i = 0; i < MAX_RENDER_TARGETS; i++)
safe_release(state->render_targets[i]);
safe_release(state->zstencil_view);
safe_release(state->sampler_state);
safe_release(state->pixel_shader);
safe_release(state->resource);
safe_release(state->raster_state);
for (size_t i = 0; i < MAX_SO_TARGETS; i++)
safe_release(state->stream_output_targets[i]);
safe_release(state->vertex_shader);
free(state->viewports);
memset(state, 0, sizeof(*state));
}
static inline void d3d10_setup_pipeline(ID3D10RenderTargetView *target,
ID3D10ShaderResourceView *resource)
{
D3D10_VIEWPORT viewport = {0};
const float factor[4] = {1.0f, 1.0f, 1.0f, 1.0f};
void *emptyptr = nullptr;
UINT stride = sizeof(vertex);
UINT zero = 0;
viewport.Width = data.cx;
viewport.Height = data.cy;
viewport.MaxDepth = 1.0f;
data.device->GSSetShader(nullptr);
data.device->IASetInputLayout(data.vertex_layout);
data.device->IASetPrimitiveTopology(
D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
data.device->IASetVertexBuffers(0, 1, &data.vertex_buffer, &stride,
&zero);
data.device->OMSetBlendState(data.blend_state, factor, 0xFFFFFFFF);
data.device->OMSetDepthStencilState(data.zstencil_state, 0);
data.device->OMSetRenderTargets(1, &target, nullptr);
data.device->PSSetSamplers(0, 1, &data.sampler_state);
data.device->PSSetShader(data.pixel_shader);
data.device->PSSetShaderResources(0, 1, &resource);
data.device->RSSetState(data.raster_state);
data.device->RSSetViewports(1, &viewport);
data.device->SOSetTargets(1, (ID3D10Buffer**)&emptyptr, &zero);
data.device->VSSetShader(data.vertex_shader);
}
static inline void d3d10_scale_texture(ID3D10RenderTargetView *target,
ID3D10ShaderResourceView *resource)
{
struct d3d10_state old_state = {};
d3d10_save_state(&old_state);
d3d10_setup_pipeline(target, resource);
data.device->Draw(4, 0);
d3d10_restore_state(&old_state);
}
static inline void d3d10_copy_texture(ID3D10Resource *dst, ID3D10Resource *src)
{
if (data.multisampled) {
data.device->ResolveSubresource(dst, 0, src, 0, data.format);
} else {
data.device->CopyResource(dst, src);
}
}
static inline void d3d10_shtex_capture(ID3D10Resource *backbuffer)
{
if (data.using_scale) {
d3d10_copy_texture(data.scale_tex, backbuffer);
d3d10_scale_texture(data.render_target, data.scale_resource);
} else {
d3d10_copy_texture(data.texture, backbuffer);
}
}
static inline void d3d10_shmem_queue_copy()
{
for (size_t i = 0; i < NUM_BUFFERS; i++) {
D3D10_MAPPED_TEXTURE2D map;
HRESULT hr;
if (data.texture_ready[i]) {
data.texture_ready[i] = false;
hr = data.copy_surfaces[i]->Map(0, D3D10_MAP_READ,
0, &map);
if (SUCCEEDED(hr)) {
data.texture_mapped[i] = true;
shmem_copy_data(i, map.pData);
}
break;
}
}
}
static inline void d3d10_shmem_capture(ID3D10Resource *backbuffer)
{
int next_tex;
d3d10_shmem_queue_copy();
next_tex = (data.cur_tex == NUM_BUFFERS - 1) ? 0 : data.cur_tex + 1;
if (data.using_scale) {
d3d10_copy_texture(data.scale_tex, backbuffer);
d3d10_scale_texture(data.render_targets[data.cur_tex],
data.scale_resource);
} else {
d3d10_copy_texture(data.textures[data.cur_tex], backbuffer);
}
if (data.copy_wait < NUM_BUFFERS - 1) {
data.copy_wait++;
} else {
ID3D10Texture2D *src = data.textures[next_tex];
ID3D10Texture2D *dst = data.copy_surfaces[next_tex];
if (shmem_texture_data_lock(next_tex)) {
dst->Unmap(0);
data.texture_mapped[next_tex] = false;
shmem_texture_data_unlock(next_tex);
}
d3d10_copy_texture(dst, src);
data.texture_ready[next_tex] = true;
}
data.cur_tex = next_tex;
}
void d3d10_capture(void *swap_ptr, void *backbuffer_ptr)
{
IDXGIResource *dxgi_backbuffer = (IDXGIResource*)backbuffer_ptr;
IDXGISwapChain *swap = (IDXGISwapChain*)swap_ptr;
HRESULT hr;
if (capture_should_stop()) {
d3d10_free();
}
if (capture_should_init()) {
d3d10_init(swap);
}
if (capture_ready()) {
ID3D10Resource *backbuffer;
hr = dxgi_backbuffer->QueryInterface(__uuidof(ID3D10Resource),
(void**)&backbuffer);
if (FAILED(hr)) {
hlog_hr("d3d10_shtex_capture: failed to get "
"backbuffer", hr);
return;
}
if (data.using_shtex)
d3d10_shtex_capture(backbuffer);
else
d3d10_shmem_capture(backbuffer);
backbuffer->Release();
}
}

View File

@ -0,0 +1,847 @@
#define _CRT_SECURE_NO_WARNINGS
#include <d3d11.h>
#include <dxgi.h>
#include "dxgi-helpers.hpp"
#include "graphics-hook.h"
#include "../funchook.h"
struct d3d11_data {
ID3D11Device *device; /* do not release */
ID3D11DeviceContext *context; /* do not release */
uint32_t base_cx;
uint32_t base_cy;
uint32_t cx;
uint32_t cy;
DXGI_FORMAT format;
bool using_shtex : 1;
bool using_scale : 1;
bool multisampled : 1;
ID3D11Texture2D *scale_tex;
ID3D11ShaderResourceView *scale_resource;
ID3D11VertexShader *vertex_shader;
ID3D11InputLayout *vertex_layout;
ID3D11PixelShader *pixel_shader;
ID3D11SamplerState *sampler_state;
ID3D11BlendState *blend_state;
ID3D11DepthStencilState *zstencil_state;
ID3D11RasterizerState *raster_state;
ID3D11Buffer *vertex_buffer;
union {
/* shared texture */
struct {
struct shtex_data *shtex_info;
ID3D11Texture2D *texture;
ID3D11RenderTargetView *render_target;
HANDLE handle;
};
/* shared memory */
struct {
ID3D11Texture2D *copy_surfaces[NUM_BUFFERS];
ID3D11Texture2D *textures[NUM_BUFFERS];
ID3D11RenderTargetView *render_targets[NUM_BUFFERS];
bool texture_ready[NUM_BUFFERS];
bool texture_mapped[NUM_BUFFERS];
uint32_t pitch;
struct shmem_data *shmem_info;
int cur_tex;
int copy_wait;
};
};
};
struct d3d11_data data = {};
void d3d11_free(void)
{
if (data.scale_tex)
data.scale_tex->Release();
if (data.scale_resource)
data.scale_resource->Release();
if (data.vertex_shader)
data.vertex_shader->Release();
if (data.vertex_layout)
data.vertex_layout->Release();
if (data.pixel_shader)
data.pixel_shader->Release();
if (data.sampler_state)
data.sampler_state->Release();
if (data.blend_state)
data.blend_state->Release();
if (data.zstencil_state)
data.zstencil_state->Release();
if (data.raster_state)
data.raster_state->Release();
if (data.vertex_buffer)
data.vertex_buffer->Release();
capture_free();
if (data.using_shtex) {
if (data.texture)
data.texture->Release();
if (data.render_target)
data.render_target->Release();
} else {
for (size_t i = 0; i < NUM_BUFFERS; i++) {
if (data.copy_surfaces[i]) {
if (data.texture_mapped[i])
data.context->Unmap(
data.copy_surfaces[i],
0);
data.copy_surfaces[i]->Release();
}
if (data.textures[i])
data.textures[i]->Release();
if (data.render_targets[i])
data.render_targets[i]->Release();
}
}
memset(&data, 0, sizeof(data));
hlog("----------------- d3d11 capture freed ----------------");
}
static bool create_d3d11_stage_surface(ID3D11Texture2D **tex)
{
HRESULT hr;
D3D11_TEXTURE2D_DESC desc = {};
desc.Width = data.cx;
desc.Height = data.cy;
desc.Format = data.format;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
hr = data.device->CreateTexture2D(&desc, nullptr, tex);
if (FAILED(hr)) {
hlog_hr("create_d3d11_stage_surface: failed to create texture",
hr);
return false;
}
return true;
}
static bool create_d3d11_tex(uint32_t cx, uint32_t cy,
ID3D11Texture2D **tex,
ID3D11ShaderResourceView **resource,
ID3D11RenderTargetView **render_target,
HANDLE *handle)
{
UINT flags = 0;
UINT misc_flags = 0;
HRESULT hr;
if (!!resource)
flags |= D3D11_BIND_SHADER_RESOURCE;
if (!!render_target)
flags |= D3D11_BIND_RENDER_TARGET;
if (!!handle)
misc_flags |= D3D11_RESOURCE_MISC_SHARED;
D3D11_TEXTURE2D_DESC desc = {};
desc.Width = cx;
desc.Height = cy;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = data.format;
desc.BindFlags = flags;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.MiscFlags = misc_flags;
hr = data.device->CreateTexture2D(&desc, nullptr, tex);
if (FAILED(hr)) {
hlog_hr("create_d3d11_tex: failed to create texture", hr);
return false;
}
if (!!resource) {
D3D11_SHADER_RESOURCE_VIEW_DESC res_desc = {};
res_desc.Format = data.format;
res_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
res_desc.Texture2D.MipLevels = 1;
hr = data.device->CreateShaderResourceView(*tex, &res_desc,
resource);
if (FAILED(hr)) {
hlog_hr("create_d3d11_tex: failed to create resource "
"view", hr);
return false;
}
}
if (!!render_target) {
hr = data.device->CreateRenderTargetView(*tex, nullptr,
render_target);
if (FAILED(hr)) {
hlog_hr("create_d3d11_tex: failed to create render "
"target view", hr);
return false;
}
}
if (!!handle) {
IDXGIResource *dxgi_res;
hr = (*tex)->QueryInterface(__uuidof(IDXGIResource),
(void**)&dxgi_res);
if (FAILED(hr)) {
hlog_hr("create_d3d11_tex: failed to query "
"IDXGIResource interface from texture", hr);
return false;
}
hr = dxgi_res->GetSharedHandle(handle);
dxgi_res->Release();
if (FAILED(hr)) {
hlog_hr("create_d3d11_tex: failed to get shared handle",
hr);
return false;
}
}
return true;
}
static inline bool d3d11_init_format(IDXGISwapChain *swap, HWND &window)
{
DXGI_SWAP_CHAIN_DESC desc;
HRESULT hr;
hr = swap->GetDesc(&desc);
if (FAILED(hr)) {
hlog_hr("d3d11_init_format: swap->GetDesc failed", hr);
return false;
}
data.format = fix_dxgi_format(desc.BufferDesc.Format);
data.multisampled = desc.SampleDesc.Count > 1;
window = desc.OutputWindow;
data.base_cx = desc.BufferDesc.Width;
data.base_cy = desc.BufferDesc.Height;
if (data.using_scale) {
data.cx = global_hook_info->cx;
data.cy = global_hook_info->cy;
} else {
data.cx = desc.BufferDesc.Width;
data.cy = desc.BufferDesc.Height;
}
return true;
}
static inline bool d3d11_init_vertex_shader(void)
{
D3D11_INPUT_ELEMENT_DESC desc[2];
uint8_t *vs_data;
size_t size;
HRESULT hr;
vs_data = get_d3d1x_vertex_shader(&size);
hr = data.device->CreateVertexShader(vs_data, size, nullptr,
&data.vertex_shader);
if (FAILED(hr)) {
hlog_hr("d3d11_init_vertex_shader: failed to create shader",
hr);
return false;
}
desc[0].SemanticName = "SV_Position";
desc[0].SemanticIndex = 0;
desc[0].Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
desc[0].InputSlot = 0;
desc[0].AlignedByteOffset = 0;
desc[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
desc[0].InstanceDataStepRate = 0;
desc[1].SemanticName = "TEXCOORD";
desc[1].SemanticIndex = 0;
desc[1].Format = DXGI_FORMAT_R32G32_FLOAT;
desc[1].InputSlot = 0;
desc[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
desc[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
desc[1].InstanceDataStepRate = 0;
hr = data.device->CreateInputLayout(desc, 2, vs_data, size,
&data.vertex_layout);
if (FAILED(hr)) {
hlog_hr("d3d11_init_vertex_shader: failed to create layout",
hr);
return false;
}
return true;
}
static inline bool d3d11_init_pixel_shader(void)
{
uint8_t *ps_data;
size_t size;
HRESULT hr;
ps_data = get_d3d1x_pixel_shader(&size);
hr = data.device->CreatePixelShader(ps_data, size, nullptr,
&data.pixel_shader);
if (FAILED(hr)) {
hlog_hr("d3d11_init_pixel_shader: failed to create shader", hr);
return false;
}
return true;
}
static inline bool d3d11_init_sampler_state(void)
{
HRESULT hr;
D3D11_SAMPLER_DESC desc = {};
desc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
desc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
desc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
desc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
hr = data.device->CreateSamplerState(&desc, &data.sampler_state);
if (FAILED(hr)) {
hlog_hr("d3d11_init_sampler_state: failed to create sampler "
"state", hr);
return false;
}
return true;
}
static inline bool d3d11_init_blend_state(void)
{
D3D11_BLEND_DESC desc = {};
HRESULT hr;
for (size_t i = 0; i < 8; i++)
desc.RenderTarget[i].RenderTargetWriteMask =
D3D11_COLOR_WRITE_ENABLE_ALL;
hr = data.device->CreateBlendState(&desc, &data.blend_state);
if (FAILED(hr)) {
hlog_hr("d3d11_init_blend_state: failed to create blend state",
hr);
return false;
}
return true;
}
static inline bool d3d11_init_zstencil_state(void)
{
D3D11_DEPTH_STENCIL_DESC desc = {}; /* defaults all to off */
HRESULT hr;
hr = data.device->CreateDepthStencilState(&desc, &data.zstencil_state);
if (FAILED(hr)) {
hlog_hr("d3d11_init_zstencil_state: failed to create "
"zstencil state", hr);
return false;
}
return true;
}
static inline bool d3d11_init_raster_state(void)
{
D3D11_RASTERIZER_DESC desc = {};
HRESULT hr;
desc.FillMode = D3D11_FILL_SOLID;
desc.CullMode = D3D11_CULL_NONE;
hr = data.device->CreateRasterizerState(&desc, &data.raster_state);
if (FAILED(hr)) {
hlog_hr("d3d11_init_raster_state: failed to create raster "
"state", hr);
return false;
}
return true;
}
#define NUM_VERTS 4
static inline bool d3d11_init_vertex_buffer(void)
{
HRESULT hr;
const vertex verts[NUM_VERTS] = {
{{-1.0f, 1.0f, 0.0f, 1.0f}, {0.0f, 0.0f}},
{{-1.0f, -1.0f, 0.0f, 1.0f}, {0.0f, 1.0f}},
{{ 1.0f, 1.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
{{ 1.0f, -1.0f, 0.0f, 1.0f}, {1.0f, 1.0f}}
};
D3D11_BUFFER_DESC desc;
desc.ByteWidth = sizeof(vertex) * NUM_VERTS;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
desc.CPUAccessFlags = 0;
desc.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA srd = {};
srd.pSysMem = (const void*)verts;
hr = data.device->CreateBuffer(&desc, &srd, &data.vertex_buffer);
if (FAILED(hr)) {
hlog_hr("d3d11_init_vertex_buffer: failed to create vertex "
"buffer", hr);
return false;
}
return true;
}
static bool d3d11_init_scaling(void)
{
bool success;
success = create_d3d11_tex(data.base_cx, data.base_cy,
&data.scale_tex, &data.scale_resource, nullptr,
nullptr);
if (!success) {
hlog("d3d11_init_scaling: failed to create scale texture");
return false;
}
if (!d3d11_init_vertex_shader()) {
return false;
}
if (!d3d11_init_pixel_shader()) {
return false;
}
if (!d3d11_init_sampler_state()) {
return false;
}
if (!d3d11_init_blend_state()) {
return false;
}
if (!d3d11_init_zstencil_state()) {
return false;
}
if (!d3d11_init_raster_state()) {
return false;
}
if (!d3d11_init_vertex_buffer()) {
return false;
}
return true;
}
static bool d3d11_shmem_init_buffers(size_t idx)
{
bool success;
success = create_d3d11_stage_surface(&data.copy_surfaces[idx]);
if (!success) {
hlog("d3d11_shmem_init_buffers: failed to create copy surface");
return false;
}
if (idx == 0) {
D3D11_MAPPED_SUBRESOURCE map = {};
HRESULT hr;
hr = data.context->Map(data.copy_surfaces[idx], 0,
D3D11_MAP_READ, 0, &map);
if (FAILED(hr)) {
hlog_hr("d3d11_shmem_init_buffers: failed to get "
"pitch", hr);
return false;
}
data.pitch = map.RowPitch;
data.context->Unmap(data.copy_surfaces[idx], 0);
}
success = create_d3d11_tex(data.cx, data.cy, &data.textures[idx],
nullptr, &data.render_targets[idx], nullptr);
if (!success) {
hlog("d3d11_shmem_init_buffers: failed to create texture");
return false;
}
return true;
}
static bool d3d11_shmem_init(HWND window)
{
data.using_shtex = false;
for (size_t i = 0; i < NUM_BUFFERS; i++) {
if (!d3d11_shmem_init_buffers(i)) {
return false;
}
}
if (!capture_init_shmem(&data.shmem_info, window,
data.base_cx, data.base_cy, data.cx, data.cy,
data.pitch, data.format, false)) {
return false;
}
hlog("d3d11 memory capture successful");
return true;
}
static bool d3d11_shtex_init(HWND window)
{
ID3D11ShaderResourceView *resource = nullptr;
bool success;
data.using_shtex = true;
success = create_d3d11_tex(data.cx, data.cy, &data.texture, &resource,
&data.render_target, &data.handle);
if (resource)
resource->Release();
if (!success) {
hlog("d3d11_shtex_init: failed to create texture");
return false;
}
if (!capture_init_shtex(&data.shtex_info, window,
data.base_cx, data.base_cy, data.cx, data.cy,
data.format, false, (uint32_t)data.handle)) {
return false;
}
hlog("d3d11 shared texture capture successful");
return true;
}
static void d3d11_init(IDXGISwapChain *swap)
{
bool success = true;
HWND window;
HRESULT hr;
data.using_scale = global_hook_info->use_scale;
hr = swap->GetDevice(__uuidof(ID3D11Device), (void**)&data.device);
if (FAILED(hr)) {
hlog_hr("d3d11_init: failed to get device from swap", hr);
return;
}
data.device->Release();
data.device->GetImmediateContext(&data.context);
data.context->Release();
if (!d3d11_init_format(swap, window)) {
return;
}
if (data.using_scale && !d3d11_init_scaling()) {
hlog("d3d11_init: failed to initialize scaling");
success = false;
}
if (success) {
if (global_hook_info->force_shmem) {
success = d3d11_shmem_init(window);
} else {
success = d3d11_shtex_init(window);
}
}
if (!success)
d3d11_free();
}
#define MAX_RENDER_TARGETS D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT
#define MAX_SO_TARGETS 4
#define MAX_CLASS_INSTS 256
struct d3d11_state {
ID3D11GeometryShader *geom_shader;
ID3D11InputLayout *vertex_layout;
D3D11_PRIMITIVE_TOPOLOGY topology;
ID3D11Buffer *vertex_buffer;
UINT vb_stride;
UINT vb_offset;
ID3D11BlendState *blend_state;
float blend_factor[4];
UINT sample_mask;
ID3D11DepthStencilState *zstencil_state;
UINT zstencil_ref;
ID3D11RenderTargetView *render_targets[MAX_RENDER_TARGETS];
ID3D11DepthStencilView *zstencil_view;
ID3D11SamplerState *sampler_state;
ID3D11PixelShader *pixel_shader;
ID3D11ShaderResourceView *resource;
ID3D11RasterizerState *raster_state;
UINT num_viewports;
D3D11_VIEWPORT *viewports;
ID3D11Buffer *stream_output_targets[MAX_SO_TARGETS];
ID3D11VertexShader *vertex_shader;
ID3D11ClassInstance *gs_class_instances[MAX_CLASS_INSTS];
ID3D11ClassInstance *ps_class_instances[MAX_CLASS_INSTS];
ID3D11ClassInstance *vs_class_instances[MAX_CLASS_INSTS];
UINT gs_class_inst_count;
UINT ps_class_inst_count;
UINT vs_class_inst_count;
};
static inline void d3d11_save_state(struct d3d11_state *state)
{
state->gs_class_inst_count = MAX_CLASS_INSTS;
state->ps_class_inst_count = MAX_CLASS_INSTS;
state->vs_class_inst_count = MAX_CLASS_INSTS;
data.context->GSGetShader(&state->geom_shader,
state->gs_class_instances,
&state->gs_class_inst_count);
data.context->IAGetInputLayout(&state->vertex_layout);
data.context->IAGetPrimitiveTopology(&state->topology);
data.context->IAGetVertexBuffers(0, 1, &state->vertex_buffer,
&state->vb_stride, &state->vb_offset);
data.context->OMGetBlendState(&state->blend_state, state->blend_factor,
&state->sample_mask);
data.context->OMGetDepthStencilState(&state->zstencil_state,
&state->zstencil_ref);
data.context->OMGetRenderTargets(MAX_RENDER_TARGETS,
state->render_targets, &state->zstencil_view);
data.context->PSGetSamplers(0, 1, &state->sampler_state);
data.context->PSGetShader(&state->pixel_shader,
state->ps_class_instances,
&state->ps_class_inst_count);
data.context->PSGetShaderResources(0, 1, &state->resource);
data.context->RSGetState(&state->raster_state);
data.context->RSGetViewports(&state->num_viewports, nullptr);
if (state->num_viewports) {
state->viewports = (D3D11_VIEWPORT*)malloc(
sizeof(D3D11_VIEWPORT) * state->num_viewports);
data.context->RSGetViewports(&state->num_viewports,
state->viewports);
}
data.context->SOGetTargets(MAX_SO_TARGETS,
state->stream_output_targets);
data.context->VSGetShader(&state->vertex_shader,
state->vs_class_instances,
&state->vs_class_inst_count);
}
static inline void safe_release(IUnknown *p)
{
if (p) p->Release();
}
#define SO_APPEND ((UINT)-1)
static inline void d3d11_restore_state(struct d3d11_state *state)
{
UINT so_offsets[MAX_SO_TARGETS] =
{SO_APPEND, SO_APPEND, SO_APPEND, SO_APPEND};
data.context->GSSetShader(state->geom_shader,
state->gs_class_instances,
state->gs_class_inst_count);
data.context->IASetInputLayout(state->vertex_layout);
data.context->IASetPrimitiveTopology(state->topology);
data.context->IASetVertexBuffers(0, 1, &state->vertex_buffer,
&state->vb_stride, &state->vb_offset);
data.context->OMSetBlendState(state->blend_state, state->blend_factor,
state->sample_mask);
data.context->OMSetDepthStencilState(state->zstencil_state,
state->zstencil_ref);
data.context->OMSetRenderTargets(MAX_RENDER_TARGETS,
state->render_targets,
state->zstencil_view);
data.context->PSSetSamplers(0, 1, &state->sampler_state);
data.context->PSSetShader(state->pixel_shader,
state->ps_class_instances,
state->ps_class_inst_count);
data.context->PSSetShaderResources(0, 1, &state->resource);
data.context->RSSetState(state->raster_state);
data.context->RSSetViewports(state->num_viewports, state->viewports);
data.context->SOSetTargets(MAX_SO_TARGETS,
state->stream_output_targets, so_offsets);
data.context->VSSetShader(state->vertex_shader,
state->vs_class_instances,
state->vs_class_inst_count);
safe_release(state->geom_shader);
safe_release(state->vertex_layout);
safe_release(state->vertex_buffer);
safe_release(state->blend_state);
safe_release(state->zstencil_state);
for (size_t i = 0; i < MAX_RENDER_TARGETS; i++)
safe_release(state->render_targets[i]);
safe_release(state->zstencil_view);
safe_release(state->sampler_state);
safe_release(state->pixel_shader);
safe_release(state->resource);
safe_release(state->raster_state);
for (size_t i = 0; i < MAX_SO_TARGETS; i++)
safe_release(state->stream_output_targets[i]);
safe_release(state->vertex_shader);
for (size_t i = 0; i < state->gs_class_inst_count; i++)
state->gs_class_instances[i]->Release();
for (size_t i = 0; i < state->ps_class_inst_count; i++)
state->ps_class_instances[i]->Release();
for (size_t i = 0; i < state->vs_class_inst_count; i++)
state->vs_class_instances[i]->Release();
free(state->viewports);
memset(state, 0, sizeof(*state));
}
static inline void d3d11_setup_pipeline(ID3D11RenderTargetView *target,
ID3D11ShaderResourceView *resource)
{
const float factor[4] = {1.0f, 1.0f, 1.0f, 1.0f};
D3D11_VIEWPORT viewport = {0};
UINT stride = sizeof(vertex);
void *emptyptr = nullptr;
UINT zero = 0;
viewport.Width = (float)data.cx;
viewport.Height = (float)data.cy;
viewport.MaxDepth = 1.0f;
data.context->GSSetShader(nullptr, nullptr, 0);
data.context->IASetInputLayout(data.vertex_layout);
data.context->IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
data.context->IASetVertexBuffers(0, 1, &data.vertex_buffer, &stride,
&zero);
data.context->OMSetBlendState(data.blend_state, factor, 0xFFFFFFFF);
data.context->OMSetDepthStencilState(data.zstencil_state, 0);
data.context->OMSetRenderTargets(1, &target, nullptr);
data.context->PSSetSamplers(0, 1, &data.sampler_state);
data.context->PSSetShader(data.pixel_shader, nullptr, 0);
data.context->PSSetShaderResources(0, 1, &resource);
data.context->RSSetState(data.raster_state);
data.context->RSSetViewports(1, &viewport);
data.context->SOSetTargets(1, (ID3D11Buffer**)&emptyptr, &zero);
data.context->VSSetShader(data.vertex_shader, nullptr, 0);
}
static inline void d3d11_scale_texture(ID3D11RenderTargetView *target,
ID3D11ShaderResourceView *resource)
{
static struct d3d11_state old_state = {0};
d3d11_save_state(&old_state);
d3d11_setup_pipeline(target, resource);
data.context->Draw(4, 0);
d3d11_restore_state(&old_state);
}
static inline void d3d11_copy_texture(ID3D11Resource *dst, ID3D11Resource *src)
{
if (data.multisampled) {
data.context->ResolveSubresource(dst, 0, src, 0, data.format);
} else {
data.context->CopyResource(dst, src);
}
}
static inline void d3d11_shtex_capture(ID3D11Resource *backbuffer)
{
if (data.using_scale) {
d3d11_copy_texture(data.scale_tex, backbuffer);
d3d11_scale_texture(data.render_target, data.scale_resource);
} else {
d3d11_copy_texture(data.texture, backbuffer);
}
}
static inline void d3d11_shmem_queue_copy()
{
for (size_t i = 0; i < NUM_BUFFERS; i++) {
D3D11_MAPPED_SUBRESOURCE map;
HRESULT hr;
if (data.texture_ready[i]) {
data.texture_ready[i] = false;
hr = data.context->Map(data.copy_surfaces[i], 0,
D3D11_MAP_READ, 0, &map);
if (SUCCEEDED(hr)) {
data.texture_mapped[i] = true;
shmem_copy_data(i, map.pData);
}
break;
}
}
}
static inline void d3d11_shmem_capture(ID3D11Resource *backbuffer)
{
int next_tex;
d3d11_shmem_queue_copy();
next_tex = (data.cur_tex == NUM_BUFFERS - 1) ? 0 : data.cur_tex + 1;
if (data.using_scale) {
d3d11_copy_texture(data.scale_tex, backbuffer);
d3d11_scale_texture(data.render_targets[data.cur_tex],
data.scale_resource);
} else {
d3d11_copy_texture(data.textures[data.cur_tex], backbuffer);
}
if (data.copy_wait < NUM_BUFFERS - 1) {
data.copy_wait++;
} else {
ID3D11Texture2D *src = data.textures[next_tex];
ID3D11Texture2D *dst = data.copy_surfaces[next_tex];
if (shmem_texture_data_lock(next_tex)) {
data.context->Unmap(dst, 0);
data.texture_mapped[next_tex] = false;
shmem_texture_data_unlock(next_tex);
}
d3d11_copy_texture(dst, src);
data.texture_ready[next_tex] = true;
}
data.cur_tex = next_tex;
}
void d3d11_capture(void *swap_ptr, void *backbuffer_ptr)
{
IDXGIResource *dxgi_backbuffer = (IDXGIResource*)backbuffer_ptr;
IDXGISwapChain *swap = (IDXGISwapChain*)swap_ptr;
HRESULT hr;
if (capture_should_stop()) {
d3d11_free();
}
if (capture_should_init()) {
d3d11_init(swap);
}
if (capture_ready()) {
ID3D11Resource *backbuffer;
hr = dxgi_backbuffer->QueryInterface(__uuidof(ID3D11Resource),
(void**)&backbuffer);
if (FAILED(hr)) {
hlog_hr("d3d11_shtex_capture: failed to get "
"backbuffer", hr);
return;
}
if (data.using_shtex)
d3d11_shtex_capture(backbuffer);
else
d3d11_shmem_capture(backbuffer);
backbuffer->Release();
}
}

View File

@ -0,0 +1,33 @@
#pragma once
static const char vertex_shader_string[] =
"struct VertData \
{ \
float4 pos : SV_Position; \
float2 texCoord : TexCoord0; \
}; \
VertData main(VertData input) \
{ \
VertData output; \
output.pos = input.pos; \
output.texCoord = input.texCoord; \
return output; \
}";
static const char pixel_shader_string[] =
"uniform Texture2D diffuseTexture; \
SamplerState textureSampler \
{ \
AddressU = Clamp; \
AddressV = Clamp; \
Filter = Linear; \
}; \
struct VertData \
{ \
float4 pos : SV_Position; \
float2 texCoord : TexCoord0; \
}; \
float4 main(VertData input) : SV_Target \
{ \
return diffuseTexture.Sample(textureSampler, input.texCoord); \
}";

View File

@ -0,0 +1,795 @@
#define _CRT_SECURE_NO_WARNINGS
#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 : 1;
bool using_scale : 1;
union {
/* shared texture */
struct {
IDirect3DSurface9 *d3d9_copytex;
ID3D11Device *d3d11_device;
ID3D11DeviceContext *d3d11_context;
ID3D11Resource *d3d11_tex;
struct shtex_data *shtex_info;
HANDLE handle;
int patch;
};
/* shared memory */
struct {
IDirect3DSurface9 *copy_surfaces[NUM_BUFFERS];
IDirect3DSurface9 *render_targets[NUM_BUFFERS];
IDirect3DQuery9 *queries[NUM_BUFFERS];
struct shmem_data *shmem_info;
volatile bool issued_queries[NUM_BUFFERS];
bool texture_mapped[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 (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()
{
uint8_t *patch_addr = get_d3d9_patch_addr(data.d3d9, data.patch);
uint8_t saved_data[MAX_PATCH_SIZE];
size_t patch_size = 0;
IDirect3DTexture9 *tex;
DWORD protect_val;
HRESULT hr;
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 (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,
(uint32_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 success;
uint32_t cx;
uint32_t cy;
HWND window;
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 {
data.patch = get_d3d9_patch(data.d3d9);
}
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)) {
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.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 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;
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;
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)
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;
}
bool hook_d3d9(void)
{
HMODULE d3d9_module = get_system_module("d3d9.dll");
void *present_addr;
void *present_ex_addr;
void *present_swap_addr;
void *reset_addr;
void *reset_ex_addr;
if (!d3d9_module) {
return false;
}
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);
reset_addr = get_offset_addr(d3d9_module,
global_hook_info->offsets.d3d9.reset);
reset_ex_addr = get_offset_addr(d3d9_module,
global_hook_info->offsets.d3d9.reset_ex);
hook_init(&present, present_addr, hook_present,
"IDirect3DDevice9::Present");
hook_init(&present_ex, present_ex_addr, hook_present_ex,
"IDirect3DDevice9Ex::PresentEx");
hook_init(&present_swap, present_swap_addr, hook_present_swap,
"IDirect3DSwapChain9::Present");
hook_init(&reset, reset_addr, hook_reset,
"IDirect3DDevice9::Reset");
hook_init(&reset_ex, reset_ex_addr, hook_reset_ex,
"IDirect3DDevice9Ex::ResetEx");
rehook(&reset_ex);
rehook(&reset);
rehook(&present_swap);
rehook(&present_ex);
rehook(&present);
hlog("Hooked D3D9");
return true;
}

View File

@ -0,0 +1,135 @@
#pragma once
#include <stdint.h>
static inline int safe_memcmp(const void *p1, const void *p2, size_t size)
{
__try {
return memcmp(p1, p2, size);
} __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) {
return -1;
}
}
struct patch_info {
size_t size;
const BYTE *data;
};
#define NEW_PATCH(x) {sizeof(x), (x)}
#define MAX_PATCH_SIZE 2
static const BYTE force_jump[] = {0xEB};
static const BYTE ignore_jump[] = {0x90, 0x90};
#ifdef _WIN64
#define NUM_VERS (10)
#define CMP_SIZE (13)
static const uintptr_t patch_offset[NUM_VERS] = {
0x54FE6, //win7 - 6.1.7600.16385
0x55095, //win7 - 6.1.7601.16562
0x550C5, //win7 - 6.1.7601.17514
0x8BDB5, //win8.1 - 6.3.9431.00000
0x8E635, //win8.1 - 6.3.9600.17415
0x90352, //win8.1 - 6.3.9600.17085
0x9038A, //win8.1 - 6.3.9600.17095
0x93AFA, //win8.1 - 6.3.9600.16384
0x93B8A, //win8.1 - 6.3.9600.16404
0x1841E5 //win8 - 6.2.9200.16384
};
static const uint8_t patch_cmp[NUM_VERS][CMP_SIZE] = {
{0x48, 0x8b, 0x81, 0xb8, 0x3d, 0x00, 0x00, 0x39, 0x98, 0x68, 0x50, 0x00, 0x00},
{0x48, 0x8b, 0x81, 0xb8, 0x3d, 0x00, 0x00, 0x39, 0x98, 0x68, 0x50, 0x00, 0x00},
{0x48, 0x8b, 0x81, 0xb8, 0x3d, 0x00, 0x00, 0x39, 0x98, 0x68, 0x50, 0x00, 0x00},
{0x48, 0x8b, 0x81, 0xb8, 0x3d, 0x00, 0x00, 0x39, 0xB0, 0x28, 0x51, 0x00, 0x00},
{0x48, 0x8b, 0x81, 0xb8, 0x3d, 0x00, 0x00, 0x39, 0xA8, 0x28, 0x51, 0x00, 0x00},
{0x8b, 0x81, 0xb8, 0x3d, 0x00, 0x00, 0x44, 0x39, 0xA0, 0x28, 0x51, 0x00, 0x00},
{0x8b, 0x81, 0xb8, 0x3d, 0x00, 0x00, 0x44, 0x39, 0xA0, 0x28, 0x51, 0x00, 0x00},
{0x8b, 0x81, 0xb8, 0x3d, 0x00, 0x00, 0x44, 0x39, 0xA0, 0x28, 0x51, 0x00, 0x00},
{0x8b, 0x81, 0xb8, 0x3d, 0x00, 0x00, 0x44, 0x39, 0xA0, 0x28, 0x51, 0x00, 0x00},
{0x49, 0x8b, 0x85, 0xb8, 0x3d, 0x00, 0x00, 0x39, 0x88, 0xc8, 0x50, 0x00, 0x00},
};
static const struct patch_info patch[NUM_VERS] = {
NEW_PATCH(force_jump),
NEW_PATCH(force_jump),
NEW_PATCH(force_jump),
NEW_PATCH(ignore_jump),
NEW_PATCH(ignore_jump),
NEW_PATCH(ignore_jump),
NEW_PATCH(ignore_jump),
NEW_PATCH(ignore_jump),
NEW_PATCH(ignore_jump),
NEW_PATCH(ignore_jump),
};
#else
#define NUM_VERS (10)
#define CMP_SIZE (12)
static const uintptr_t patch_offset[NUM_VERS] = {
0x79AA6, //win7 - 6.1.7601.16562
0x79C9E, //win7 - 6.1.7600.16385
0x79D96, //win7 - 6.1.7601.17514
0x7F9BD, //win8.1 - 6.3.9431.00000
0x8A3F4, //win8.1 - 6.3.9600.16404
0x8E9F7, //win8.1 - 6.3.9600.17095
0x8F00F, //win8.1 - 6.3.9600.17085
0x8FBB1, //win8.1 - 6.3.9600.16384
0x90264, //win8.1 - 6.3.9600.17415
0x166A08 //win8 - 6.2.9200.16384
};
static const uint8_t patch_cmp[NUM_VERS][CMP_SIZE] = {
{0x8b, 0x89, 0xe8, 0x29, 0x00, 0x00, 0x39, 0xb9, 0x80, 0x4b, 0x00, 0x00},
{0x8b, 0x89, 0xe8, 0x29, 0x00, 0x00, 0x39, 0xb9, 0x80, 0x4b, 0x00, 0x00},
{0x8b, 0x89, 0xe8, 0x29, 0x00, 0x00, 0x39, 0xb9, 0x80, 0x4b, 0x00, 0x00},
{0x8b, 0x80, 0xe8, 0x29, 0x00, 0x00, 0x39, 0xb0, 0x40, 0x4c, 0x00, 0x00},
{0x80, 0xe8, 0x29, 0x00, 0x00, 0x83, 0xb8, 0x40, 0x4c, 0x00, 0x00, 0x00},
{0x80, 0xe8, 0x29, 0x00, 0x00, 0x83, 0xb8, 0x40, 0x4c, 0x00, 0x00, 0x00},
{0x80, 0xe8, 0x29, 0x00, 0x00, 0x83, 0xb8, 0x40, 0x4c, 0x00, 0x00, 0x00},
{0x80, 0xe8, 0x29, 0x00, 0x00, 0x83, 0xb8, 0x40, 0x4c, 0x00, 0x00, 0x00},
{0x87, 0xe8, 0x29, 0x00, 0x00, 0x83, 0xb8, 0x40, 0x4c, 0x00, 0x00, 0x00},
{0x8b, 0x80, 0xe8, 0x29, 0x00, 0x00, 0x39, 0x90, 0xb0, 0x4b, 0x00, 0x00},
};
static const struct patch_info patch[NUM_VERS] = {
NEW_PATCH(force_jump),
NEW_PATCH(force_jump),
NEW_PATCH(force_jump),
NEW_PATCH(force_jump),
NEW_PATCH(force_jump),
NEW_PATCH(force_jump),
NEW_PATCH(force_jump),
NEW_PATCH(force_jump),
NEW_PATCH(ignore_jump),
NEW_PATCH(force_jump),
};
#endif
static inline int get_d3d9_patch(HMODULE d3d9)
{
uint8_t *addr = (uint8_t*)d3d9;
for (int i = 0; i < NUM_VERS; i++) {
int ret = safe_memcmp(addr + patch_offset[i], patch_cmp[i],
CMP_SIZE);
if (ret == 0)
return i;
}
return -1;
}
static inline uint8_t *get_d3d9_patch_addr(HMODULE d3d9, int patch)
{
if (patch == -1)
return nullptr;
uint8_t *addr = (uint8_t*)d3d9;
return addr + patch_offset[patch] + CMP_SIZE;
}

View File

@ -0,0 +1,242 @@
#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;
}

View File

@ -0,0 +1,14 @@
#pragma once
static inline DXGI_FORMAT fix_dxgi_format(DXGI_FORMAT format)
{
switch (format) {
case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
return DXGI_FORMAT_B8G8R8A8_UNORM;
case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
return DXGI_FORMAT_R8G8B8A8_UNORM;
}
return format;
}

View File

@ -0,0 +1,872 @@
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable : 4214) /* nonstandard extension, non-int bitfield */
#pragma warning(disable : 4054) /* function pointer to data pointer */
#include <windows.h>
#define COBJMACROS
#include <dxgi.h>
#include <d3d11.h>
#include "gl-decs.h"
#include "graphics-hook.h"
#include "../funchook.h"
#define DUMMY_WINDOW_CLASS_NAME L"graphics_hook_gl_dummy_window"
static const GUID GUID_IDXGIFactory1 =
{0x770aae78, 0xf26f, 0x4dba, {0xa8, 0x29, 0x25, 0x3c, 0x83, 0xd1, 0xb3, 0x87}};
static const GUID GUID_IDXGIResource =
{0x035f3ab4, 0x482e, 0x4e50, {0xb4, 0x1f, 0x8a, 0x7f, 0x8b, 0xd8, 0x96, 0x0b}};
static struct func_hook swap_buffers;
static struct func_hook wgl_swap_layer_buffers;
static struct func_hook wgl_swap_buffers;
static struct func_hook wgl_delete_context;
struct gl_data {
HDC hdc;
uint32_t base_cx;
uint32_t base_cy;
uint32_t cx;
uint32_t cy;
DXGI_FORMAT format;
GLuint fbo;
bool using_shtex : 1;
bool using_scale : 1;
union {
/* shared texture */
struct {
struct shtex_data *shtex_info;
ID3D11Device *d3d11_device;
ID3D11DeviceContext *d3d11_context;
ID3D11Texture2D *d3d11_tex;
IDXGISwapChain *dxgi_swap;
HANDLE gl_device;
HANDLE gl_dxobj;
HANDLE handle;
HWND hwnd;
GLuint texture;
};
/* shared memory */
struct {
struct shmem_data *shmem_info;
int cur_tex;
int copy_wait;
GLuint pbos[NUM_BUFFERS];
GLuint textures[NUM_BUFFERS];
bool texture_ready[NUM_BUFFERS];
bool texture_mapped[NUM_BUFFERS];
};
};
};
static HMODULE gl = NULL;
static bool nv_capture_available = false;
static struct gl_data data = {0};
static inline bool gl_error(const char *func, const char *str)
{
GLenum error = glGetError();
if (error != 0) {
hlog("%s: %s: %lu", func, str, error);
return true;
}
return false;
}
static void gl_free(void)
{
capture_free();
if (data.using_shtex) {
if (data.gl_dxobj)
jimglDXUnregisterObjectNV(data.gl_device,
data.gl_dxobj);
if (data.gl_device)
jimglDXCloseDeviceNV(data.gl_device);
if (data.texture)
glDeleteTextures(1, &data.texture);
if (data.d3d11_tex)
ID3D11Resource_Release(data.d3d11_tex);
if (data.d3d11_context)
ID3D11DeviceContext_Release(data.d3d11_context);
if (data.d3d11_device)
ID3D11Device_Release(data.d3d11_device);
if (data.dxgi_swap)
IDXGISwapChain_Release(data.dxgi_swap);
if (data.hwnd)
DestroyWindow(data.hwnd);
} else {
for (size_t i = 0; i < NUM_BUFFERS; i++) {
if (data.pbos[i]) {
if (data.texture_mapped[i]) {
glBindBuffer(GL_PIXEL_PACK_BUFFER,
data.pbos[i]);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
}
glDeleteBuffers(1, &data.pbos[i]);
}
if (data.textures[i])
glDeleteTextures(1, &data.textures[i]);
}
}
if (data.fbo)
glDeleteFramebuffers(1, &data.fbo);
gl_error("gl_free", "GL error occurred on free");
memset(&data, 0, sizeof(data));
hlog("------------------ gl capture freed ------------------");
}
static inline void *base_get_proc(const char *name)
{
return (void*)GetProcAddress(gl, name);
}
static inline void *wgl_get_proc(const char *name)
{
return (void*)jimglGetProcAddress(name);
}
static inline void *get_proc(const char *name)
{
void *func = wgl_get_proc(name);
if (!func)
func = base_get_proc(name);
return func;
}
static void init_nv_functions(void)
{
jimglDXSetResourceShareHandleNV =
get_proc("wglDXSetResourceShareHandleNV");
jimglDXOpenDeviceNV = get_proc("wglDXOpenDeviceNV");
jimglDXCloseDeviceNV = get_proc("wglDXCloseDeviceNV");
jimglDXRegisterObjectNV = get_proc("wglDXRegisterObjectNV");
jimglDXUnregisterObjectNV = get_proc("wglDXUnregisterObjectNV");
jimglDXObjectAccessNV = get_proc("wglDXObjectAccessNV");
jimglDXLockObjectsNV = get_proc("wglDXLockObjectsNV");
jimglDXUnlockObjectsNV = get_proc("wglDXUnlockObjectsNV");
nv_capture_available =
!!jimglDXSetResourceShareHandleNV &&
!!jimglDXOpenDeviceNV &&
!!jimglDXCloseDeviceNV &&
!!jimglDXRegisterObjectNV &&
!!jimglDXUnregisterObjectNV &&
!!jimglDXObjectAccessNV &&
!!jimglDXLockObjectsNV &&
!!jimglDXUnlockObjectsNV;
if (nv_capture_available)
hlog("Shared-texture OpenGL capture available");
}
#define GET_PROC(cur_func, ptr, func) \
do { \
ptr = get_proc(#func); \
if (!ptr) { \
hlog("%s: failed to get function '%s'", #cur_func, \
#func); \
success = false; \
} \
} while (false)
static bool init_gl_functions(void)
{
bool success = true;
jimglGetProcAddress = base_get_proc("wglGetProcAddress");
if (!jimglGetProcAddress) {
hlog("init_gl_functions: failed to get wglGetProcAddress");
return false;
}
GET_PROC(init_gl_functions, jimglMakeCurrent, wglMakeCurrent);
GET_PROC(init_gl_functions, jimglGetCurrentDC, wglGetCurrentDC);
GET_PROC(init_gl_functions, jimglGetCurrentContext,
wglGetCurrentContext);
GET_PROC(init_gl_functions, glTexImage2D, glTexImage2D);
GET_PROC(init_gl_functions, glReadBuffer, glReadBuffer);
GET_PROC(init_gl_functions, glGetTexImage, glGetTexImage);
GET_PROC(init_gl_functions, glDrawBuffer, glDrawBuffer);
GET_PROC(init_gl_functions, glGetError, glGetError);
GET_PROC(init_gl_functions, glBufferData, glBufferData);
GET_PROC(init_gl_functions, glDeleteBuffers, glDeleteBuffers);
GET_PROC(init_gl_functions, glDeleteTextures, glDeleteTextures);
GET_PROC(init_gl_functions, glGenBuffers, glGenBuffers);
GET_PROC(init_gl_functions, glGenTextures, glGenTextures);
GET_PROC(init_gl_functions, glMapBuffer, glMapBuffer);
GET_PROC(init_gl_functions, glUnmapBuffer, glUnmapBuffer);
GET_PROC(init_gl_functions, glBindBuffer, glBindBuffer);
GET_PROC(init_gl_functions, glGetIntegerv, glGetIntegerv);
GET_PROC(init_gl_functions, glBindTexture, glBindTexture);
GET_PROC(init_gl_functions, glGenFramebuffers, glGenFramebuffers);
GET_PROC(init_gl_functions, glDeleteFramebuffers, glDeleteFramebuffers);
GET_PROC(init_gl_functions, glBindFramebuffer, glBindFramebuffer);
GET_PROC(init_gl_functions, glBlitFramebuffer, glBlitFramebuffer);
GET_PROC(init_gl_functions, glFramebufferTexture2D,
glFramebufferTexture2D);
init_nv_functions();
return success;
}
static void get_window_size(HDC hdc, uint32_t *cx, uint32_t *cy)
{
HWND hwnd = WindowFromDC(hdc);
RECT rc = {0};
GetClientRect(hwnd, &rc);
*cx = rc.right;
*cy = rc.bottom;
}
static inline bool gl_shtex_init_window(void)
{
data.hwnd = CreateWindowExW(0, DUMMY_WINDOW_CLASS_NAME,
L"Dummy GL window, ignore",
WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, 2, 2, NULL, NULL, GetModuleHandle(NULL), NULL);
if (!data.hwnd) {
hlog("gl_shtex_init_window: failed to create window: %d",
GetLastError());
return false;
}
return true;
}
typedef HRESULT (WINAPI *create_dxgi_factory1_t)(REFIID, void **);
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 gl_shtex_init_d3d11(void)
{
D3D_FEATURE_LEVEL level_used;
IDXGIFactory1 *factory;
IDXGIAdapter *adapter;
HRESULT hr;
HMODULE d3d11 = load_system_library("d3d11.dll");
if (!d3d11) {
hlog("gl_shtex_init_d3d11: failed to load D3D11.dll: %d",
GetLastError());
return false;
}
HMODULE dxgi = load_system_library("dxgi.dll");
if (!dxgi) {
hlog("gl_shtex_init_d3d11: failed to load DXGI.dll: %d",
GetLastError());
return false;
}
DXGI_SWAP_CHAIN_DESC desc = {0};
desc.BufferCount = 2;
desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.BufferDesc.Width = 2;
desc.BufferDesc.Height = 2;
desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.SampleDesc.Count = 1;
desc.Windowed = true;
desc.OutputWindow = data.hwnd;
create_dxgi_factory1_t create_factory = (void*)GetProcAddress(dxgi,
"CreateDXGIFactory1");
if (!create_factory) {
hlog("gl_shtex_init_d3d11: failed to load CreateDXGIFactory1 "
"procedure: %d", GetLastError());
return false;
}
PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN create = (void*)GetProcAddress(
d3d11, "D3D11CreateDeviceAndSwapChain");
if (!create) {
hlog("gl_shtex_init_d3d11: failed to load "
"D3D11CreateDeviceAndSwapChain procedure: %d",
GetLastError());
return false;
}
hr = create_factory(&GUID_IDXGIFactory1, (void**)&factory);
if (FAILED(hr)) {
hlog_hr("gl_shtex_init_d3d11: failed to create factory", hr);
return false;
}
hr = IDXGIFactory1_EnumAdapters1(factory, 0, (IDXGIAdapter1**)&adapter);
IDXGIFactory1_Release(factory);
if (FAILED(hr)) {
hlog_hr("gl_shtex_init_d3d11: failed to create adapter", hr);
return false;
}
hr = create(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, 0, feature_levels,
sizeof(feature_levels) / sizeof(D3D_FEATURE_LEVEL),
D3D11_SDK_VERSION, &desc, &data.dxgi_swap,
&data.d3d11_device, &level_used, &data.d3d11_context);
IDXGIAdapter_Release(adapter);
if (FAILED(hr)) {
hlog_hr("gl_shtex_init_d3d11: failed to create device", hr);
return false;
}
return true;
}
static inline bool gl_shtex_init_d3d11_tex(void)
{
IDXGIResource *dxgi_res;
HRESULT hr;
D3D11_TEXTURE2D_DESC desc = {0};
desc.Width = data.cx;
desc.Height = data.cy;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
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 = ID3D11Device_CreateTexture2D(data.d3d11_device, &desc, NULL,
&data.d3d11_tex);
if (FAILED(hr)) {
hlog_hr("gl_shtex_init_d3d11_tex: failed to create texture",
hr);
return false;
}
hr = ID3D11Device_QueryInterface(data.d3d11_tex,
&GUID_IDXGIResource, (void**)&dxgi_res);
if (FAILED(hr)) {
hlog_hr("gl_shtex_init_d3d11_tex: failed to get IDXGIResource",
hr);
return false;
}
hr = IDXGIResource_GetSharedHandle(dxgi_res, &data.handle);
IDXGIResource_Release(dxgi_res);
if (FAILED(hr)) {
hlog_hr("gl_shtex_init_d3d11_tex: failed to get shared handle",
hr);
return false;
}
return true;
}
static inline bool gl_shtex_init_gl_tex(void)
{
data.gl_device = jimglDXOpenDeviceNV(data.d3d11_device);
if (!data.gl_device) {
hlog("gl_shtex_init_gl_tex: failed to open device");
return false;
}
glGenTextures(1, &data.texture);
if (gl_error("gl_shtex_init_gl_tex", "failed to generate texture")) {
return false;
}
data.gl_dxobj = jimglDXRegisterObjectNV(data.gl_device, data.d3d11_tex,
data.texture, GL_TEXTURE_2D,
WGL_ACCESS_WRITE_DISCARD_NV);
if (!data.gl_dxobj) {
hlog("gl_shtex_init_gl_tex: failed to register object");
return false;
}
return true;
}
static inline bool gl_init_fbo(void)
{
glGenFramebuffers(1, &data.fbo);
return !gl_error("gl_init_fbo", "failed to initialize FBO");
}
static bool gl_shtex_init(HWND window)
{
if (!gl_shtex_init_window()) {
return false;
}
if (!gl_shtex_init_d3d11()) {
return false;
}
if (!gl_shtex_init_d3d11_tex()) {
return false;
}
if (!gl_shtex_init_gl_tex()) {
return false;
}
if (!gl_init_fbo()) {
return false;
}
if (!capture_init_shtex(&data.shtex_info, window,
data.base_cx, data.base_cy, data.cx, data.cy,
data.format, true, (uint32_t)data.handle)) {
return false;
}
hlog("gl shared texture capture successful");
return true;
}
static inline bool gl_shmem_init_data(size_t idx, size_t size)
{
glBindBuffer(GL_PIXEL_PACK_BUFFER, data.pbos[idx]);
if (gl_error("gl_shmem_init_data", "failed to bind pbo")) {
return false;
}
glBufferData(GL_PIXEL_PACK_BUFFER, size, 0, GL_STREAM_READ);
if (gl_error("gl_shmem_init_data", "failed to set pbo data")) {
return false;
}
glBindTexture(GL_TEXTURE_2D, data.textures[idx]);
if (gl_error("gl_shmem_init_data", "failed to set bind texture")) {
return false;
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, data.cx, data.cy,
0, GL_BGRA, GL_UNSIGNED_BYTE, NULL);
if (gl_error("gl_shmem_init_data", "failed to set texture data")) {
return false;
}
return true;
}
static inline bool gl_shmem_init_buffers(void)
{
uint32_t size = data.cx * data.cy * 4;
GLint last_pbo;
GLint last_tex;
glGenBuffers(NUM_BUFFERS, data.pbos);
if (gl_error("gl_shmem_init_buffers", "failed to generate buffers")) {
return false;
}
glGenTextures(NUM_BUFFERS, data.textures);
if (gl_error("gl_shmem_init_buffers", "failed to generate textures")) {
return false;
}
glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &last_pbo);
if (gl_error("gl_shmem_init_buffers",
"failed to save pixel pack buffer")) {
return false;
}
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex);
if (gl_error("gl_shmem_init_buffers", "failed to save texture")) {
return false;
}
for (size_t i = 0; i < NUM_BUFFERS; i++) {
if (!gl_shmem_init_data(i, size)) {
return false;
}
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, last_pbo);
glBindTexture(GL_TEXTURE_2D, last_tex);
return true;
}
static bool gl_shmem_init(HWND window)
{
if (!gl_shmem_init_buffers()) {
return false;
}
if (!gl_init_fbo()) {
return false;
}
if (!capture_init_shmem(&data.shmem_info, window,
data.base_cx, data.base_cy, data.cx, data.cy,
data.cx * 4, data.format, true)) {
return false;
}
hlog("gl memory capture successful");
return true;
}
static void gl_init(HDC hdc)
{
HWND window = WindowFromDC(hdc);
bool success = false;
RECT rc = {0};
GetClientRect(window, &rc);
data.base_cx = rc.right;
data.base_cy = rc.bottom;
data.hdc = hdc;
data.format = DXGI_FORMAT_B8G8R8A8_UNORM;
data.using_scale = global_hook_info->use_scale;
data.using_shtex = nv_capture_available &&
!global_hook_info->force_shmem;
if (data.using_scale) {
data.cx = global_hook_info->cx;
data.cy = global_hook_info->cy;
} else {
data.cx = data.base_cx;
data.cy = data.base_cy;
}
if (data.using_shtex)
success = gl_shtex_init(window);
else
success = gl_shmem_init(window);
if (!success)
gl_free();
}
static void gl_copy_backbuffer(GLuint dst)
{
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, data.fbo);
if (gl_error("gl_shtex_capture_blit", "failed to bind FBO")) {
return;
}
glBindTexture(GL_TEXTURE_2D, dst);
if (gl_error("gl_shtex_capture_blit", "failed to bind texture")) {
return;
}
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, dst, 0);
if (gl_error("gl_shtex_capture_blit", "failed to set frame buffer")) {
return;
}
glReadBuffer(GL_BACK);
if (gl_error("gl_shtex_capture_blit", "failed to set read buffer")) {
return;
}
glDrawBuffer(GL_COLOR_ATTACHMENT0);
if (gl_error("gl_shtex_capture_blit", "failed to set draw buffer")) {
return;
}
glBlitFramebuffer(0, 0, data.base_cx, data.base_cy,
0, 0, data.cx, data.cy, GL_COLOR_BUFFER_BIT, GL_LINEAR);
gl_error("gl_shtex_capture_blit", "failed to blit");
}
static void gl_shtex_capture(void)
{
GLint last_fbo;
GLint last_tex;
jimglDXLockObjectsNV(data.gl_device, 1, &data.gl_dxobj);
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &last_fbo);
if (gl_error("gl_shtex_capture", "failed to get last fbo")) {
return;
}
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex);
if (gl_error("gl_shtex_capture", "failed to get last texture")) {
return;
}
gl_copy_backbuffer(data.texture);
glBindTexture(GL_TEXTURE_2D, last_tex);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, last_fbo);
jimglDXUnlockObjectsNV(data.gl_device, 1, &data.gl_dxobj);
IDXGISwapChain_Present(data.dxgi_swap, 0, 0);
}
static inline void gl_shmem_capture_queue_copy(void)
{
for (int i = 0; i < NUM_BUFFERS; i++) {
if (data.texture_ready[i]) {
GLvoid *buffer;
data.texture_ready[i] = false;
glBindBuffer(GL_PIXEL_PACK_BUFFER, data.pbos[i]);
if (gl_error("gl_shmem_capture_queue_copy",
"failed to bind pbo")) {
return;
}
buffer = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
if (buffer) {
data.texture_mapped[i] = true;
shmem_copy_data(i, buffer);
}
break;
}
}
}
static inline void gl_shmem_capture_stage(GLuint dst_pbo, GLuint src_tex)
{
glBindTexture(GL_TEXTURE_2D, src_tex);
if (gl_error("gl_shmem_capture_stage", "failed to bind src_tex")) {
return;
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, dst_pbo);
if (gl_error("gl_shmem_capture_stage", "failed to bind dst_pbo")) {
return;
}
glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);
if (gl_error("gl_shmem_capture_stage", "failed to read src_tex")) {
return;
}
}
static void gl_shmem_capture(void)
{
int next_tex;
GLint last_fbo;
GLint last_tex;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &last_fbo);
if (gl_error("gl_shmem_capture", "failed to get last fbo")) {
return;
}
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_tex);
if (gl_error("gl_shmem_capture", "failed to get last texture")) {
return;
}
gl_shmem_capture_queue_copy();
next_tex = (data.cur_tex == NUM_BUFFERS - 1) ? 0 : data.cur_tex + 1;
gl_copy_backbuffer(data.textures[next_tex]);
if (data.copy_wait < NUM_BUFFERS - 1) {
data.copy_wait++;
} else {
GLuint src = data.textures[next_tex];
GLuint dst = data.pbos[next_tex];
if (shmem_texture_data_lock(next_tex)) {
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
data.texture_mapped[next_tex] = false;
shmem_texture_data_unlock(next_tex);
}
gl_shmem_capture_stage(dst, src);
data.texture_ready[next_tex] = true;
}
glBindTexture(GL_TEXTURE_2D, last_tex);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, last_fbo);
}
static void gl_capture(HDC hdc)
{
static bool functions_initialized = false;
static bool critical_failure = false;
static bool reacquireing = false;
if (critical_failure) {
return;
}
if (!functions_initialized) {
functions_initialized = init_gl_functions();
if (!functions_initialized) {
critical_failure = true;
return;
}
}
if (capture_should_stop()) {
gl_free();
}
if (capture_should_init()) {
gl_init(hdc);
}
if (capture_ready() && hdc == data.hdc) {
uint32_t new_cx;
uint32_t new_cy;
/* reset capture if resized */
get_window_size(hdc, &new_cx, &new_cy);
if (new_cx != data.base_cx || new_cy != data.base_cy) {
gl_free();
return;
}
if (data.using_shtex)
gl_shtex_capture();
else
gl_shmem_capture();
}
}
static BOOL WINAPI hook_swap_buffers(HDC hdc)
{
BOOL ret;
if (!global_hook_info->capture_overlay)
gl_capture(hdc);
unhook(&swap_buffers);
BOOL (WINAPI *call)(HDC) = swap_buffers.call_addr;
ret = call(hdc);
rehook(&swap_buffers);
if (global_hook_info->capture_overlay)
gl_capture(hdc);
return ret;
}
static BOOL WINAPI hook_wgl_swap_buffers(HDC hdc)
{
BOOL ret;
if (!global_hook_info->capture_overlay)
gl_capture(hdc);
unhook(&wgl_swap_buffers);
BOOL (WINAPI *call)(HDC) = wgl_swap_buffers.call_addr;
ret = call(hdc);
rehook(&wgl_swap_buffers);
if (global_hook_info->capture_overlay)
gl_capture(hdc);
return ret;
}
static BOOL WINAPI hook_wgl_swap_layer_buffers(HDC hdc, UINT planes)
{
BOOL ret;
if (!global_hook_info->capture_overlay)
gl_capture(hdc);
unhook(&wgl_swap_layer_buffers);
BOOL (WINAPI *call)(HDC, UINT) = wgl_swap_layer_buffers.call_addr;
ret = call(hdc, planes);
rehook(&wgl_swap_layer_buffers);
if (global_hook_info->capture_overlay)
gl_capture(hdc);
return ret;
}
static BOOL WINAPI hook_wgl_delete_context(HGLRC hrc)
{
BOOL ret;
if (capture_active()) {
HDC last_hdc = jimglGetCurrentDC();
HGLRC last_hrc = jimglGetCurrentContext();
jimglMakeCurrent(data.hdc, hrc);
gl_free();
jimglMakeCurrent(last_hdc, last_hrc);
}
unhook(&wgl_delete_context);
BOOL (WINAPI *call)(HGLRC) = wgl_delete_context.call_addr;
ret = call(hrc);
rehook(&wgl_delete_context);
return ret;
}
static bool gl_register_window(void)
{
WNDCLASSW wc = {0};
wc.style = CS_OWNDC;
wc.hInstance = GetModuleHandle(NULL);
wc.lpfnWndProc = DefWindowProc;
wc.lpszClassName = DUMMY_WINDOW_CLASS_NAME;
if (!RegisterClassW(&wc)) {
hlog("gl_register_window: failed to register window class: %d",
GetLastError());
return false;
}
return true;
}
bool hook_gl(void)
{
void *wgl_dc_proc;
void *wgl_slb_proc;
void *wgl_sb_proc;
gl = get_system_module("opengl32.dll");
if (!gl) {
return false;
}
if (!gl_register_window()) {
return true;
}
wgl_dc_proc = base_get_proc("wglDeleteContext");
wgl_slb_proc = base_get_proc("wglSwapLayerBuffers");
wgl_sb_proc = base_get_proc("wglSwapBuffers");
hook_init(&swap_buffers, SwapBuffers, hook_swap_buffers, "SwapBuffers");
if (wgl_dc_proc) {
hook_init(&wgl_delete_context, wgl_dc_proc,
hook_wgl_delete_context,
"wglDeleteContext");
rehook(&wgl_delete_context);
}
if (wgl_slb_proc) {
hook_init(&wgl_swap_layer_buffers, wgl_slb_proc,
hook_wgl_swap_layer_buffers,
"wglSwapLayerBuffers");
rehook(&wgl_swap_layer_buffers);
}
if (wgl_sb_proc) {
hook_init(&wgl_swap_buffers, wgl_sb_proc,
hook_wgl_swap_buffers,
"wglSwapBuffers");
rehook(&wgl_swap_buffers);
}
rehook(&swap_buffers);
return true;
}

View File

@ -0,0 +1,152 @@
#pragma once
typedef unsigned int GLenum;
typedef unsigned int GLbitfield;
typedef unsigned int GLuint;
typedef int GLint;
typedef int GLsizei;
typedef unsigned char GLboolean;
typedef signed char GLbyte;
typedef short GLshort;
typedef unsigned char GLubyte;
typedef unsigned short GLushort;
typedef unsigned long GLulong;
typedef float GLfloat;
typedef float GLclampf;
typedef double GLdouble;
typedef double GLclampd;
typedef void GLvoid;
typedef ptrdiff_t GLintptrARB;
typedef ptrdiff_t GLsizeiptrARB;
#define GL_FRONT 0x0404
#define GL_BACK 0x0405
#define GL_UNSIGNED_BYTE 0x1401
#define GL_RGB 0x1907
#define GL_RGBA 0x1908
#define GL_BGR 0x80E0
#define GL_BGRA 0x80E1
#define GL_NEAREST 0x2600
#define GL_LINEAR 0x2601
#define GL_READ_ONLY 0x88B8
#define GL_WRITE_ONLY 0x88B9
#define GL_READ_WRITE 0x88BA
#define GL_BUFFER_ACCESS 0x88BB
#define GL_BUFFER_MAPPED 0x88BC
#define GL_BUFFER_MAP_POINTER 0x88BD
#define GL_STREAM_DRAW 0x88E0
#define GL_STREAM_READ 0x88E1
#define GL_STREAM_COPY 0x88E2
#define GL_STATIC_DRAW 0x88E4
#define GL_STATIC_READ 0x88E5
#define GL_STATIC_COPY 0x88E6
#define GL_DYNAMIC_DRAW 0x88E8
#define GL_DYNAMIC_READ 0x88E9
#define GL_DYNAMIC_COPY 0x88EA
#define GL_PIXEL_PACK_BUFFER 0x88EB
#define GL_PIXEL_UNPACK_BUFFER 0x88EC
#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED
#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF
#define GL_TEXTURE_2D 0x0DE1
#define GL_TEXTURE_BINDING_2D 0x8069
#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6
#define WGL_ACCESS_READ_ONLY_NV 0x0000
#define WGL_ACCESS_READ_WRITE_NV 0x0001
#define WGL_ACCESS_WRITE_DISCARD_NV 0x0002
#define GL_READ_FRAMEBUFFER 0x8CA8
#define GL_DRAW_FRAMEBUFFER 0x8CA9
#define GL_COLOR_BUFFER_BIT 0x00004000
#define GL_COLOR_ATTACHMENT0 0x8CE0
#define GL_COLOR_ATTACHMENT1 0x8CE1
typedef void (WINAPI *GLTEXIMAGE2DPROC)(GLenum target, GLint level,
GLint internal_format, GLsizei width, GLsizei height,
GLint border, GLenum format, GLenum type, const GLvoid *data);
typedef void (WINAPI *GLGETTEXIMAGEPROC)(GLenum target, GLint level,
GLenum format, GLenum type, GLvoid *img);
typedef void (WINAPI *GLREADBUFFERPROC)(GLenum);
typedef void (WINAPI *GLDRAWBUFFERPROC)(GLenum mode);
typedef void (WINAPI *GLGETINTEGERVPROC)(GLenum pname, GLint *params);
typedef GLenum (WINAPI *GLGETERRORPROC)();
typedef BOOL (WINAPI *WGLSWAPLAYERBUFFERSPROC)(HDC, UINT);
typedef BOOL (WINAPI *WGLSWAPBUFFERSPROC)(HDC);
typedef BOOL (WINAPI *WGLDELETECONTEXTPROC)(HGLRC);
typedef PROC (WINAPI *WGLGETPROCADDRESSPROC)(LPCSTR);
typedef BOOL (WINAPI *WGLMAKECURRENTPROC)(HDC, HGLRC);
typedef HDC (WINAPI *WGLGETCURRENTDCPROC)();
typedef HGLRC (WINAPI *WGLGETCURRENTCONTEXTPROC)();
typedef HGLRC (WINAPI *WGLCREATECONTEXTPROC)(HDC);
typedef void (WINAPI *GLBUFFERDATAARBPROC) (GLenum target, GLsizeiptrARB size,
const GLvoid* data, GLenum usage);
typedef void (WINAPI *GLDELETEBUFFERSARBPROC)(GLsizei n, const GLuint* buffers);
typedef void (WINAPI *GLDELETETEXTURESPROC)(GLsizei n, const GLuint* buffers);
typedef void (WINAPI *GLGENBUFFERSARBPROC)(GLsizei n, GLuint* buffers);
typedef void (WINAPI *GLGENTEXTURESPROC)(GLsizei n, GLuint* textures);
typedef GLvoid* (WINAPI *GLMAPBUFFERPROC)(GLenum target, GLenum access);
typedef GLboolean (WINAPI *GLUNMAPBUFFERPROC)(GLenum target);
typedef void (WINAPI *GLBINDBUFFERPROC)(GLenum target, GLuint buffer);
typedef void (WINAPI *GLBINDTEXTUREPROC)(GLenum target, GLuint texture);
typedef void (WINAPI *GLGENFRAMEBUFFERSPROC)(GLsizei n, GLuint* buffers);
typedef void (WINAPI *GLDELETEFRAMEBUFFERSPROC)(GLsizei n,
GLuint *framebuffers);
typedef void (WINAPI *GLBINDFRAMEBUFFERPROC)(GLenum target, GLuint framebuffer);
typedef void (WINAPI *GLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0,
GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0,
GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
typedef void (WINAPI *GLFRAMEBUFFERTEXTURE2DPROC)(GLenum target,
GLenum attachment, GLenum textarget, GLuint texture,
GLint level);
typedef BOOL (WINAPI *WGLSETRESOURCESHAREHANDLENVPROC)(void*, HANDLE);
typedef HANDLE (WINAPI *WGLDXOPENDEVICENVPROC)(void*);
typedef BOOL (WINAPI *WGLDXCLOSEDEVICENVPROC)(HANDLE);
typedef HANDLE (WINAPI *WGLDXREGISTEROBJECTNVPROC)(HANDLE, void *, GLuint, GLenum, GLenum);
typedef BOOL (WINAPI *WGLDXUNREGISTEROBJECTNVPROC)(HANDLE, HANDLE);
typedef BOOL (WINAPI *WGLDXOBJECTACCESSNVPROC)(HANDLE, GLenum);
typedef BOOL (WINAPI *WGLDXLOCKOBJECTSNVPROC)(HANDLE, GLint, HANDLE *);
typedef BOOL (WINAPI *WGLDXUNLOCKOBJECTSNVPROC)(HANDLE, GLint, HANDLE *);
static GLTEXIMAGE2DPROC glTexImage2D = NULL;
static GLGETTEXIMAGEPROC glGetTexImage = NULL;
static GLREADBUFFERPROC glReadBuffer = NULL;
static GLDRAWBUFFERPROC glDrawBuffer = NULL;
static GLGETINTEGERVPROC glGetIntegerv = NULL;
static GLGETERRORPROC glGetError = NULL;
static WGLSWAPLAYERBUFFERSPROC jimglSwapLayerBuffers = NULL;
static WGLSWAPBUFFERSPROC jimglSwapBuffers = NULL;
static WGLDELETECONTEXTPROC jimglDeleteContext = NULL;
static WGLGETPROCADDRESSPROC jimglGetProcAddress = NULL;
static WGLMAKECURRENTPROC jimglMakeCurrent = NULL;
static WGLGETCURRENTDCPROC jimglGetCurrentDC = NULL;
static WGLGETCURRENTCONTEXTPROC jimglGetCurrentContext = NULL;
static WGLCREATECONTEXTPROC jimglCreateContext = NULL;
static GLBUFFERDATAARBPROC glBufferData = NULL;
static GLDELETEBUFFERSARBPROC glDeleteBuffers = NULL;
static GLDELETETEXTURESPROC glDeleteTextures = NULL;
static GLGENBUFFERSARBPROC glGenBuffers = NULL;
static GLGENTEXTURESPROC glGenTextures = NULL;
static GLMAPBUFFERPROC glMapBuffer = NULL;
static GLUNMAPBUFFERPROC glUnmapBuffer = NULL;
static GLBINDBUFFERPROC glBindBuffer = NULL;
static GLBINDTEXTUREPROC glBindTexture = NULL;
static GLGENFRAMEBUFFERSPROC glGenFramebuffers = NULL;
static GLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers = NULL;
static GLBINDFRAMEBUFFERPROC glBindFramebuffer = NULL;
static GLBLITFRAMEBUFFERPROC glBlitFramebuffer = NULL;
static GLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = NULL;
static WGLSETRESOURCESHAREHANDLENVPROC jimglDXSetResourceShareHandleNV = NULL;
static WGLDXOPENDEVICENVPROC jimglDXOpenDeviceNV = NULL;
static WGLDXCLOSEDEVICENVPROC jimglDXCloseDeviceNV = NULL;
static WGLDXREGISTEROBJECTNVPROC jimglDXRegisterObjectNV = NULL;
static WGLDXUNREGISTEROBJECTNVPROC jimglDXUnregisterObjectNV = NULL;
static WGLDXOBJECTACCESSNVPROC jimglDXObjectAccessNV = NULL;
static WGLDXLOCKOBJECTSNVPROC jimglDXLockObjectsNV = NULL;
static WGLDXUNLOCKOBJECTSNVPROC jimglDXUnlockObjectsNV = NULL;

View File

@ -0,0 +1,904 @@
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <shlobj.h>
#include <psapi.h>
#include <ipc-util/pipe.h>
#include "graphics-hook.h"
#include "../obfuscate.h"
#include "../funchook.h"
#define DEBUG_OUTPUT
#ifdef DEBUG_OUTPUT
#define DbgOut(x) OutputDebugStringA(x)
#else
#define DbgOut(x)
#endif
struct thread_data {
CRITICAL_SECTION mutexes[NUM_BUFFERS];
CRITICAL_SECTION data_mutex;
void *volatile cur_data;
uint8_t *shmem_textures[2];
HANDLE copy_thread;
HANDLE copy_event;
HANDLE stop_event;
volatile int cur_tex;
unsigned int pitch;
unsigned int cy;
volatile bool locked_textures[NUM_BUFFERS];
};
static ipc_pipe_client_t pipe = {0};
static HANDLE signal_restart = NULL;
static HANDLE signal_stop = NULL;
static HANDLE signal_ready = NULL;
static HANDLE signal_exit = NULL;
static HANDLE tex_mutexes[2] = {NULL, NULL};
static HANDLE filemap_hook_info = NULL;
static volatile bool stop_loop = false;
static HANDLE capture_thread = NULL;
static char system_path[MAX_PATH] = {0};
static char process_name[MAX_PATH] = {0};
static char keepalive_name[64] = {0};
static unsigned int shmem_id_counter = 0;
static void *shmem_info = NULL;
static HANDLE shmem_file_handle = 0;
static struct thread_data thread_data = {0};
volatile bool active = false;
struct hook_info *global_hook_info = NULL;
static inline void wait_for_dll_main_finish(HANDLE thread_handle)
{
if (thread_handle) {
WaitForSingleObject(thread_handle, 100);
CloseHandle(thread_handle);
}
}
static inline bool init_pipe(void)
{
char new_name[64];
sprintf(new_name, "%s%d", PIPE_NAME, GetCurrentProcessId());
if (!ipc_pipe_client_open(&pipe, new_name)) {
DbgOut("Failed to open pipe\n");
return false;
}
return true;
}
static HANDLE init_event(const char *name, DWORD pid)
{
HANDLE handle = get_event_plus_id(name, pid);
if (!handle)
hlog("Failed to get event '%s': %lu", name, GetLastError());
return handle;
}
static HANDLE init_mutex(const char *name, DWORD pid)
{
char new_name[64];
HANDLE handle;
sprintf(new_name, "%s%d", name, pid);
handle = OpenMutexA(MUTEX_ALL_ACCESS, false, new_name);
if (!handle)
hlog("Failed to open mutex '%s': %lu", name, GetLastError());
return handle;
}
static inline bool init_signals(void)
{
DWORD pid = GetCurrentProcessId();
signal_restart = init_event(EVENT_CAPTURE_RESTART, pid);
if (!signal_restart) {
return false;
}
signal_stop = init_event(EVENT_CAPTURE_STOP, pid);
if (!signal_stop) {
return false;
}
signal_ready = init_event(EVENT_HOOK_READY, pid);
if (!signal_ready) {
return false;
}
signal_exit = init_event(EVENT_HOOK_EXIT, pid);
if (!signal_exit) {
return false;
}
return true;
}
static inline bool init_mutexes(void)
{
DWORD pid = GetCurrentProcessId();
tex_mutexes[0] = init_mutex(MUTEX_TEXTURE1, pid);
if (!tex_mutexes[0]) {
return false;
}
tex_mutexes[1] = init_mutex(MUTEX_TEXTURE2, pid);
if (!tex_mutexes[1]) {
return false;
}
return true;
}
static inline bool init_system_path(void)
{
HRESULT hr = SHGetFolderPathA(NULL, CSIDL_SYSTEM, NULL,
SHGFP_TYPE_CURRENT, system_path);
if (hr != S_OK) {
hlog("Failed to get windows system path: %08lX", hr);
return false;
}
return true;
}
static inline void log_current_process(void)
{
DWORD len = GetModuleBaseNameA(GetCurrentProcess(), NULL, process_name,
MAX_PATH);
if (len > 0) {
process_name[len] = 0;
hlog("Hooked to process: %s", process_name);
}
hlog("(half life scientist) everything.. seems to be in order");
}
static inline bool init_hook_info(void)
{
filemap_hook_info = get_hook_info(GetCurrentProcessId());
if (!filemap_hook_info) {
hlog("Failed to create hook info file mapping: %lu",
GetLastError());
return false;
}
global_hook_info = MapViewOfFile(filemap_hook_info, FILE_MAP_ALL_ACCESS,
0, 0, sizeof(struct hook_info));
if (!global_hook_info) {
hlog("Failed to map the hook info file mapping: %lu",
GetLastError());
return false;
}
return true;
}
static inline bool init_hook(HANDLE thread_handle)
{
wait_for_dll_main_finish(thread_handle);
sprintf(keepalive_name, "%s%d", EVENT_HOOK_KEEPALIVE,
GetCurrentProcessId());
if (!init_pipe()) {
return false;
}
if (!init_signals()) {
return false;
}
if (!init_mutexes()) {
return false;
}
if (!init_system_path()) {
return false;
}
if (!init_hook_info()) {
return false;
}
log_current_process();
SetEvent(signal_restart);
return true;
}
static inline void close_handle(HANDLE *handle)
{
if (*handle) {
CloseHandle(*handle);
*handle = NULL;
}
}
static void free_hook(void)
{
if (filemap_hook_info) {
CloseHandle(filemap_hook_info);
filemap_hook_info = NULL;
}
if (global_hook_info) {
UnmapViewOfFile(global_hook_info);
global_hook_info = NULL;
}
close_handle(&tex_mutexes[1]);
close_handle(&tex_mutexes[0]);
close_handle(&signal_exit);
close_handle(&signal_ready);
close_handle(&signal_stop);
close_handle(&signal_restart);
ipc_pipe_client_free(&pipe);
}
static inline bool d3d8_hookable(void)
{
return !!global_hook_info->offsets.d3d8.present &&
!!global_hook_info->offsets.d3d8.reset;
}
static inline bool ddraw_hookable(void)
{
return !!global_hook_info->offsets.ddraw.surface_create &&
!!global_hook_info->offsets.ddraw.surface_restore &&
!!global_hook_info->offsets.ddraw.surface_release &&
!!global_hook_info->offsets.ddraw.surface_unlock &&
!!global_hook_info->offsets.ddraw.surface_blt &&
!!global_hook_info->offsets.ddraw.surface_flip &&
!!global_hook_info->offsets.ddraw.surface_set_palette &&
!!global_hook_info->offsets.ddraw.palette_set_entries;
}
static inline bool d3d9_hookable(void)
{
return !!global_hook_info->offsets.d3d9.present &&
!!global_hook_info->offsets.d3d9.present_ex &&
!!global_hook_info->offsets.d3d9.present_swap &&
!!global_hook_info->offsets.d3d9.reset &&
!!global_hook_info->offsets.d3d9.reset_ex;
}
static inline bool dxgi_hookable(void)
{
return !!global_hook_info->offsets.dxgi.present &&
!!global_hook_info->offsets.dxgi.resize;
}
static inline bool attempt_hook(void)
{
static bool ddraw_hooked = false;
static bool d3d8_hooked = false;
static bool d3d9_hooked = false;
static bool dxgi_hooked = false;
static bool gl_hooked = false;
if (!d3d9_hooked) {
if (!d3d9_hookable()) {
d3d9_hooked = true;
} else {
d3d9_hooked = hook_d3d9();
if (d3d9_hooked) {
return true;
}
}
}
if (!dxgi_hooked) {
if (!dxgi_hookable()) {
dxgi_hooked = true;
} else {
dxgi_hooked = hook_dxgi();
if (dxgi_hooked) {
return true;
}
}
}
if (!gl_hooked) {
gl_hooked = hook_gl();
if (gl_hooked) {
return true;
}
/*} else {
rehook_gl();*/
}
/*if (!d3d8_hooked) {
if (!d3d8_hookable()) {
d3d8_hooked = true;
} else {
d3d8_hooked = hook_d3d8();
if (d3d8_hooked) {
return true;
}
}
}
if (!ddraw_hooked) {
if (!ddraw_hookable()) {
ddraw_hooked = true;
} else {
ddraw_hooked = hook_ddraw();
if (ddraw_hooked) {
return true;
}
}
}*/
return false;
}
static inline void capture_loop(void)
{
while (!attempt_hook())
Sleep(40);
for (size_t n = 0; !stop_loop; n++) {
/* this causes it to check every 4 seconds, but still with
* a small sleep interval in case the thread needs to stop */
if (n % 100 == 0) attempt_hook();
Sleep(40);
}
}
static DWORD WINAPI main_capture_thread(HANDLE thread_handle)
{
if (!init_hook(thread_handle)) {
DbgOut("Failed to init hook\n");
free_hook();
return 0;
}
capture_loop();
return 0;
}
static inline void hlogv(const char *format, va_list args)
{
char message[1024] = "";
int num = _vsprintf_p(message, 1024, format, args);
if (num) {
if (!ipc_pipe_client_write(&pipe, message, num + 1)) {
ipc_pipe_client_free(&pipe);
}
DbgOut(message);
DbgOut("\n");
}
}
void hlog(const char *format, ...)
{
va_list args;
va_start(args, format);
hlogv(format, args);
va_end(args);
}
void hlog_hr(const char *text, HRESULT hr)
{
LPSTR buffer = NULL;
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, hr, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
(LPSTR)&buffer, 0, NULL);
if (buffer) {
hlog("%s (0x%08lX): %s", text, hr, buffer);
LocalFree(buffer);
} else {
hlog("%s (0x%08lX)", text, hr);
}
}
inline const char *get_process_name(void)
{
return process_name;
}
inline HMODULE get_system_module(const char *module)
{
char base_path[MAX_PATH];
strcpy(base_path, system_path);
strcat(base_path, "\\");
strcat(base_path, module);
return GetModuleHandleA(module);
}
inline HMODULE load_system_library(const char *name)
{
char base_path[MAX_PATH];
HMODULE module;
strcpy(base_path, system_path);
strcat(base_path, "\\");
strcat(base_path, name);
module = GetModuleHandleA(base_path);
if (module)
return module;
return LoadLibraryA(base_path);
}
static inline bool capture_alive(void)
{
HANDLE event = OpenEventA(EVENT_ALL_ACCESS, false, keepalive_name);
if (event) {
CloseHandle(event);
return true;
}
return false;
}
inline bool capture_active(void)
{
return active;
}
static inline bool capture_stopped(void)
{
return WaitForSingleObject(signal_stop, 0) == WAIT_OBJECT_0;
}
static inline bool capture_restarted(void)
{
return WaitForSingleObject(signal_restart, 0) == WAIT_OBJECT_0;
}
inline bool capture_should_stop(void)
{
bool stop_requested = false;
if (capture_active())
stop_requested = capture_stopped() || !capture_alive();
return stop_requested;
}
inline bool capture_should_init(void)
{
if (!capture_active() && capture_restarted()) {
if (capture_alive()) {
if (!ipc_pipe_client_valid(&pipe)) {
init_pipe();
}
return true;
}
}
return false;
}
static inline uint64_t get_clockfreq(void)
{
static bool have_clockfreq = false;
static LARGE_INTEGER clock_freq;
if (!have_clockfreq) {
QueryPerformanceFrequency(&clock_freq);
have_clockfreq = true;
}
return clock_freq.QuadPart;
}
uint64_t os_gettime_ns(void)
{
LARGE_INTEGER current_time;
double time_val;
QueryPerformanceCounter(&current_time);
time_val = (double)current_time.QuadPart;
time_val *= 1000000000.0;
time_val /= (double)get_clockfreq();
return (uint64_t)time_val;
}
inline int try_lock_shmem_tex(int id)
{
int next = id == 0 ? 1 : 0;
if (WaitForSingleObject(tex_mutexes[id], 0) == WAIT_OBJECT_0) {
return id;
} else if (WaitForSingleObject(tex_mutexes[next], 0) == WAIT_OBJECT_0) {
return next;
}
return -1;
}
inline void unlock_shmem_tex(int id)
{
if (id != -1) {
ReleaseMutex(tex_mutexes[id]);
}
}
static inline bool frame_ready(uint64_t interval)
{
static uint64_t last_time = 0;
uint64_t elapsed;
uint64_t t;
if (!interval) {
return true;
}
t = os_gettime_ns();
elapsed = t - last_time;
if (elapsed < interval) {
return false;
}
last_time = (elapsed > interval * 2) ? t : last_time + interval;
return true;
}
inline bool capture_ready(void)
{
return capture_active() &&
frame_ready(global_hook_info->frame_interval);
}
static inline bool init_shared_info(size_t size)
{
char name[64];
sprintf_s(name, 64, "%s%u", SHMEM_TEXTURE, ++shmem_id_counter);
shmem_file_handle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, (DWORD)size, name);
if (!shmem_file_handle) {
hlog("init_shared_info: Failed to create shared memory: %d",
GetLastError());
return false;
}
shmem_info = MapViewOfFile(shmem_file_handle, FILE_MAP_ALL_ACCESS,
0, 0, size);
if (!shmem_info) {
hlog("init_shared_info: Failed to map shared memory: %d",
GetLastError());
return false;
}
return true;
}
bool capture_init_shtex(struct shtex_data **data, HWND window,
uint32_t base_cx, uint32_t base_cy, uint32_t cx, uint32_t cy,
uint32_t format, bool flip, uint32_t handle)
{
if (!init_shared_info(sizeof(struct shtex_data))) {
hlog("capture_init_shtex: Failed to initialize memory");
return false;
}
*data = shmem_info;
(*data)->tex_handle = handle;
global_hook_info->window = (uint32_t)window;
global_hook_info->type = CAPTURE_TYPE_TEXTURE;
global_hook_info->format = format;
global_hook_info->flip = flip;
global_hook_info->map_id = shmem_id_counter;
global_hook_info->map_size = sizeof(struct shtex_data);
global_hook_info->cx = cx;
global_hook_info->cy = cy;
global_hook_info->base_cx = base_cx;
global_hook_info->base_cy = base_cy;
if (!SetEvent(signal_ready)) {
hlog("capture_init_shtex: Failed to signal ready: %d",
GetLastError());
return false;
}
active = true;
return true;
}
static DWORD CALLBACK copy_thread(LPVOID unused)
{
uint32_t pitch = thread_data.pitch;
uint32_t cy = thread_data.cy;
HANDLE events[2] = {NULL, NULL};
int shmem_id = 0;
if (!duplicate_handle(&events[0], thread_data.copy_event)) {
hlog_hr("copy_thread: Failed to duplicate copy event: %d",
GetLastError());
return 0;
}
if (!duplicate_handle(&events[1], thread_data.stop_event)) {
hlog_hr("copy_thread: Failed to duplicate stop event: %d",
GetLastError());
goto finish;
}
for (;;) {
int copy_tex;
void *cur_data;
DWORD ret = WaitForMultipleObjects(2, events, false, INFINITE);
if (ret != WAIT_OBJECT_0) {
break;
}
EnterCriticalSection(&thread_data.data_mutex);
copy_tex = thread_data.cur_tex;
cur_data = thread_data.cur_data;
LeaveCriticalSection(&thread_data.data_mutex);
if (copy_tex < NUM_BUFFERS && !!cur_data) {
EnterCriticalSection(&thread_data.mutexes[copy_tex]);
int lock_id = try_lock_shmem_tex(shmem_id);
if (lock_id != -1) {
memcpy(thread_data.shmem_textures[lock_id],
cur_data, pitch * cy);
unlock_shmem_tex(lock_id);
((struct shmem_data*)shmem_info)->last_tex =
lock_id;
shmem_id = lock_id == 0 ? 1 : 0;
}
LeaveCriticalSection(&thread_data.mutexes[copy_tex]);
}
}
finish:
for (size_t i = 0; i < 2; i++) {
if (events[i]) {
CloseHandle(events[i]);
}
}
(void)unused;
return 0;
}
inline void shmem_copy_data(size_t idx, void *volatile data)
{
EnterCriticalSection(&thread_data.data_mutex);
thread_data.cur_tex = (int)idx;
thread_data.cur_data = data;
thread_data.locked_textures[idx] = true;
LeaveCriticalSection(&thread_data.data_mutex);
SetEvent(thread_data.copy_event);
}
inline bool shmem_texture_data_lock(int idx)
{
bool locked;
EnterCriticalSection(&thread_data.data_mutex);
locked = thread_data.locked_textures[idx];
LeaveCriticalSection(&thread_data.data_mutex);
if (locked) {
EnterCriticalSection(&thread_data.mutexes[idx]);
return true;
}
return false;
}
inline void shmem_texture_data_unlock(int idx)
{
EnterCriticalSection(&thread_data.data_mutex);
thread_data.locked_textures[idx] = false;
LeaveCriticalSection(&thread_data.data_mutex);
LeaveCriticalSection(&thread_data.mutexes[idx]);
}
static inline bool init_shmem_thread(uint32_t pitch, uint32_t cy)
{
struct shmem_data *data = shmem_info;
thread_data.pitch = pitch;
thread_data.cy = cy;
thread_data.shmem_textures[0] = (uint8_t*)data + data->tex1_offset;
thread_data.shmem_textures[1] = (uint8_t*)data + data->tex2_offset;
thread_data.copy_event = CreateEvent(NULL, false, false, NULL);
if (!thread_data.copy_event) {
hlog("init_shmem_thread: Failed to create copy event: %d",
GetLastError());
return false;
}
thread_data.stop_event = CreateEvent(NULL, true, false, NULL);
if (!thread_data.stop_event) {
hlog("init_shmem_thread: Failed to create stop event: %d",
GetLastError());
return false;
}
for (size_t i = 0; i < NUM_BUFFERS; i++) {
InitializeCriticalSection(&thread_data.mutexes[i]);
}
InitializeCriticalSection(&thread_data.data_mutex);
thread_data.copy_thread = CreateThread(NULL, 0, copy_thread, NULL, 0,
NULL);
if (!thread_data.copy_thread) {
hlog("init_shmem_thread: Failed to create thread: %d",
GetLastError());
return false;
}
return true;
}
#ifndef ALIGN
#define ALIGN(bytes, align) (((bytes) + ((align) - 1)) & ~((align) - 1))
#endif
bool capture_init_shmem(struct shmem_data **data, HWND window,
uint32_t base_cx, uint32_t base_cy, uint32_t cx, uint32_t cy,
uint32_t pitch, uint32_t format, bool flip)
{
uint32_t tex_size = cy * pitch;
uint32_t aligned_header = ALIGN(sizeof(struct shmem_data), 32);
uint32_t aligned_tex = ALIGN(tex_size, 32);
uint32_t total_size = aligned_header + aligned_tex * 2;
uintptr_t align_pos;
if (!init_shared_info(total_size)) {
hlog("capture_init_shmem: Failed to initialize memory");
return false;
}
*data = shmem_info;
/* to ensure fast copy rate, align texture data to 256bit addresses */
align_pos = (uintptr_t)shmem_info;
align_pos += aligned_header;
align_pos &= ~(32 - 1);
align_pos -= (uintptr_t)shmem_info;
(*data)->last_tex = -1;
(*data)->tex1_offset = (uint32_t)align_pos;
(*data)->tex2_offset = (*data)->tex1_offset + aligned_tex;
global_hook_info->window = (uint32_t)window;
global_hook_info->type = CAPTURE_TYPE_MEMORY;
global_hook_info->format = format;
global_hook_info->flip = flip;
global_hook_info->map_id = shmem_id_counter;
global_hook_info->map_size = total_size;
global_hook_info->pitch = pitch;
global_hook_info->cx = cx;
global_hook_info->cy = cy;
global_hook_info->base_cx = base_cx;
global_hook_info->base_cy = base_cy;
if (!init_shmem_thread(pitch, cy)) {
return false;
}
if (!SetEvent(signal_ready)) {
hlog("capture_init_shmem: Failed to signal ready: %d",
GetLastError());
return false;
}
active = true;
return true;
}
static inline void thread_data_free(void)
{
if (thread_data.copy_thread) {
DWORD ret;
SetEvent(thread_data.stop_event);
ret = WaitForSingleObject(thread_data.copy_thread, 500);
if (ret != WAIT_OBJECT_0)
TerminateThread(thread_data.copy_thread, (DWORD)-1);
CloseHandle(thread_data.copy_thread);
}
if (thread_data.stop_event)
CloseHandle(thread_data.stop_event);
if (thread_data.copy_event)
CloseHandle(thread_data.copy_event);
for (size_t i = 0; i < NUM_BUFFERS; i++)
DeleteCriticalSection(&thread_data.mutexes[i]);
DeleteCriticalSection(&thread_data.data_mutex);
memset(&thread_data, 0, sizeof(thread_data));
}
void capture_free(void)
{
thread_data_free();
if (shmem_info) {
UnmapViewOfFile(shmem_info);
shmem_info = NULL;
}
close_handle(&shmem_file_handle);
SetEvent(signal_restart);
active = false;
}
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID unused1)
{
if (reason == DLL_PROCESS_ATTACH) {
wchar_t name[MAX_PATH];
HANDLE cur_thread = OpenThread(THREAD_ALL_ACCESS, false,
GetCurrentThreadId());
/* this prevents the library from being automatically unloaded
* by the next FreeLibrary call */
GetModuleFileNameW(hinst, name, MAX_PATH);
LoadLibraryW(name);
capture_thread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)main_capture_thread,
(LPVOID)cur_thread, 0, 0);
if (!capture_thread) {
CloseHandle(cur_thread);
return false;
}
} else if (reason == DLL_PROCESS_DETACH) {
if (capture_thread) {
stop_loop = true;
WaitForSingleObject(capture_thread, 300);
CloseHandle(capture_thread);
}
free_hook();
}
(void)unused1;
return true;
}
__declspec(dllexport) LRESULT CALLBACK dummy_debug_proc(int code,
WPARAM wparam, LPARAM lparam)
{
static bool hooking = true;
MSG *msg = (MSG*)lparam;
if (hooking && msg->message == (WM_USER + 432)) {
HMODULE user32 = GetModuleHandleW(L"USER32");
BOOL (WINAPI *unhook_windows_hook_ex)(HHOOK) = NULL;
unhook_windows_hook_ex = get_obfuscated_func(user32,
"VojeleY`bdgxvM`hhDz",
0x7F55F80C9EE3A213ULL);
if (unhook_windows_hook_ex)
unhook_windows_hook_ex((HHOOK)msg->lParam);
hooking = false;
}
return CallNextHookEx(0, code, wparam, lparam);
}

View File

@ -0,0 +1,82 @@
#pragma once
/* conversion from data/function pointer */
#pragma warning(disable: 4152)
#include "../graphics-hook-info.h"
#ifdef __cplusplus
extern "C" {
#else
#ifndef inline
#define inline __inline
#endif
#endif
#define NUM_BUFFERS 3
extern void hlog(const char *format, ...);
extern void hlog_hr(const char *text, HRESULT hr);
extern inline const char *get_process_name(void);
extern inline HMODULE get_system_module(const char *module);
extern inline HMODULE load_system_library(const char *module);
extern uint64_t os_gettime_ns(void);
extern inline bool capture_active(void);
extern inline bool capture_ready(void);
extern inline bool capture_should_stop(void);
extern inline bool capture_should_init(void);
extern inline void shmem_copy_data(size_t idx, void *volatile data);
extern inline bool shmem_texture_data_lock(int idx);
extern inline void shmem_texture_data_unlock(int idx);
extern bool hook_ddraw(void);
extern bool hook_d3d8(void);
extern bool hook_d3d9(void);
extern bool hook_dxgi(void);
extern bool hook_gl(void);
extern void d3d10_capture(void *swap, void *backbuffer);
extern void d3d10_free(void);
extern void d3d11_capture(void *swap, void *backbuffer);
extern void d3d11_free(void);
extern uint8_t *get_d3d1x_vertex_shader(size_t *size);
extern uint8_t *get_d3d1x_pixel_shader(size_t *size);
extern bool rehook_gl(void);
extern bool capture_init_shtex(struct shtex_data **data, HWND window,
uint32_t base_cx, uint32_t base_cy, uint32_t cx, uint32_t cy,
uint32_t format, bool flip, uint32_t handle);
extern bool capture_init_shmem(struct shmem_data **data, HWND window,
uint32_t base_cx, uint32_t base_cy, uint32_t cx, uint32_t cy,
uint32_t pitch, uint32_t format, bool flip);
extern void capture_free(void);
extern struct hook_info *global_hook_info;
struct vertex {
struct {
float x, y, z, w;
} pos;
struct {
float u, v;
} tex;
};
static inline bool duplicate_handle(HANDLE *dst, HANDLE src)
{
return !!DuplicateHandle(GetCurrentProcess(), src, GetCurrentProcess(),
dst, 0, false, DUPLICATE_SAME_ACCESS);
}
static inline void *get_offset_addr(HMODULE module, uint32_t offset)
{
return (void*)((uintptr_t)module + (uintptr_t)offset);
}
#ifdef __cplusplus
}
#endif