Created
March 4, 2019 05:30
-
-
Save wormyrocks/31317a3b694d0df4c1f23eee1d473a45 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "stdafx.h" | |
#include <d3d11.h> | |
#include "UniversalD3D11Hook.h" | |
#include "Console.h" | |
#include "MinHook.h" | |
#include "Globals.h" | |
#include "imgui.h" | |
#include "Defaults.h" | |
#include "imgui_impl_dx11.h" | |
#include "OverlayControl.h" | |
#include "OverlayConsole.h" | |
#include "Input.h" | |
#include <atomic> | |
#include <time.h> | |
#include <filesystem> | |
#include "stb_image.h" | |
#include "com_ptr.hpp" | |
//#include "stb_image_dds.h" | |
#include "stb_image_write.h" | |
#include "stb_image_resize.h" | |
#pragma comment(lib, "d3d11.lib") | |
namespace IGCS::DX11Hooker | |
{ | |
#define DXGI_PRESENT_INDEX 8 | |
#define DXGI_RESIZEBUFFERS_INDEX 13 | |
//-------------------------------------------------------------------------------------------------------------------------------- | |
// Forward declarations | |
void createRenderTarget(IDXGISwapChain* pSwapChain); | |
void cleanupRenderTarget(); | |
void initImGui(); | |
void initImGuiStyle(); | |
//-------------------------------------------------------------------------------------------------------------------------------- | |
// Typedefs of functions to hook | |
typedef HRESULT(__stdcall *D3D11PresentHook) (IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags); | |
typedef HRESULT(__stdcall *D3D11ResizeBuffersHook) (IDXGISwapChain* pSwapChain, UINT bufferCount, UINT width, UINT height, DXGI_FORMAT newFormat, UINT swapChainFlags); | |
static ID3D11Device* _device = nullptr; | |
static ID3D11DeviceContext* _context = nullptr; | |
static ID3D11RenderTargetView* _mainRenderTargetView = nullptr; | |
IDXGISwapChain* pSwapChain; | |
ID3D11Texture2D* pBackBuffer; | |
UINT _width; | |
UINT _height; | |
DXGI_FORMAT _backbuffer_format; | |
//-------------------------------------------------------------------------------------------------------------------------------- | |
// Pointers to the original hooked functions | |
static D3D11PresentHook hookedD3D11Present = nullptr; | |
static D3D11ResizeBuffersHook hookedD3D11ResizeBuffers = nullptr; | |
static bool _tmpSwapChainInitialized = false; | |
static atomic_bool _imGuiInitializing = false; | |
static atomic_bool _initializeDeviceAndContext = true; | |
static atomic_bool _presentInProgress = false; | |
HRESULT __stdcall detourD3D11ResizeBuffers(IDXGISwapChain* pSwapChain, UINT bufferCount, UINT width, UINT height, DXGI_FORMAT newFormat, UINT swapChainFlags) | |
{ | |
_imGuiInitializing = true; | |
ImGui_ImplDX11_InvalidateDeviceObjects(); | |
cleanupRenderTarget(); | |
HRESULT toReturn = hookedD3D11ResizeBuffers(pSwapChain, bufferCount, width, height, newFormat, swapChainFlags); | |
createRenderTarget(pSwapChain); | |
ImGui_ImplDX11_CreateDeviceObjects(); | |
_imGuiInitializing = false; | |
return toReturn; | |
} | |
HRESULT __stdcall detourD3D11Present(IDXGISwapChain* pSwapChain, UINT SyncInterval, UINT Flags) | |
{ | |
if (_presentInProgress) | |
{ | |
return S_OK; | |
} | |
_presentInProgress = true; | |
if (_tmpSwapChainInitialized) | |
{ | |
if (!(Flags & DXGI_PRESENT_TEST) && !_imGuiInitializing) | |
{ | |
if (_initializeDeviceAndContext) | |
{ | |
if (FAILED(pSwapChain->GetDevice(__uuidof(_device), (void**)&_device))) | |
{ | |
IGCS::Console::WriteError("Failed to get device from hooked swapchain"); | |
} | |
else | |
{ | |
OverlayConsole::instance().logDebug("DX11 Device: %p", (void*)_device); | |
_device->GetImmediateContext(&_context); | |
} | |
if (nullptr == _context) | |
{ | |
IGCS::Console::WriteError("Failed to get device context from hooked swapchain"); | |
} | |
else | |
{ | |
OverlayConsole::instance().logDebug("DX11 Context: %p", (void*)_context); | |
} | |
createRenderTarget(pSwapChain); | |
initImGui(); | |
_initializeDeviceAndContext = false; | |
} | |
// render our own stuff | |
_context->OMSetRenderTargets(1, &_mainRenderTargetView, NULL); | |
OverlayControl::renderOverlay(); | |
Input::resetKeyStates(); | |
Input::resetMouseState(); | |
} | |
} | |
HRESULT toReturn = hookedD3D11Present(pSwapChain, SyncInterval, Flags); | |
_presentInProgress = false; | |
return toReturn; | |
} | |
void initializeHook() | |
{ | |
_tmpSwapChainInitialized = false; | |
D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0; | |
DXGI_SWAP_CHAIN_DESC swapChainDesc; | |
ZeroMemory(&swapChainDesc, sizeof(swapChainDesc)); | |
swapChainDesc.BufferCount = 1; | |
swapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; | |
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; | |
swapChainDesc.OutputWindow = IGCS::Globals::instance().mainWindowHandle(); | |
swapChainDesc.SampleDesc.Count = 1; | |
swapChainDesc.Windowed = TRUE; | |
swapChainDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; | |
swapChainDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; | |
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; | |
_width = swapChainDesc.BufferDesc.Width; | |
_height = swapChainDesc.BufferDesc.Height; | |
_backbuffer_format = swapChainDesc.BufferDesc.Format; | |
ID3D11Device *pTmpDevice = NULL; | |
ID3D11DeviceContext *pTmpContext = NULL; | |
IDXGISwapChain* pTmpSwapChain; | |
if (FAILED(D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, &featureLevel, 1, D3D11_SDK_VERSION, &swapChainDesc, &pTmpSwapChain, &pTmpDevice, NULL, &pTmpContext))) | |
{ | |
IGCS::Console::WriteError("Failed to create directX device and swapchain!"); | |
return; | |
} | |
__int64* pSwapChainVtable = NULL; | |
__int64* pDeviceContextVTable = NULL; | |
pSwapChainVtable = (__int64*)pTmpSwapChain; | |
pSwapChainVtable = (__int64*)pSwapChainVtable[0]; | |
pDeviceContextVTable = (__int64*)pTmpContext; | |
pDeviceContextVTable = (__int64*)pDeviceContextVTable[0]; | |
OverlayConsole::instance().logDebug("Present Address: %p", (void*)(__int64*)pSwapChainVtable[DXGI_PRESENT_INDEX]); | |
if (MH_CreateHook((LPBYTE)pSwapChainVtable[DXGI_PRESENT_INDEX], &detourD3D11Present, reinterpret_cast<LPVOID*>(&hookedD3D11Present)) != MH_OK) | |
{ | |
IGCS::Console::WriteError("Hooking Present failed!"); | |
} | |
if (MH_EnableHook((LPBYTE)pSwapChainVtable[DXGI_PRESENT_INDEX]) != MH_OK) | |
{ | |
IGCS::Console::WriteError("Enabling of Present hook failed!"); | |
} | |
OverlayConsole::instance().logDebug("ResizeBuffers Address: %p", (__int64*)pSwapChainVtable[DXGI_RESIZEBUFFERS_INDEX]); | |
if (MH_CreateHook((LPBYTE)pSwapChainVtable[DXGI_RESIZEBUFFERS_INDEX], &detourD3D11ResizeBuffers, reinterpret_cast<LPVOID*>(&hookedD3D11ResizeBuffers)) != MH_OK) | |
{ | |
IGCS::Console::WriteError("Hooking ResizeBuffers failed!"); | |
} | |
if (MH_EnableHook((LPBYTE)pSwapChainVtable[DXGI_RESIZEBUFFERS_INDEX]) != MH_OK) | |
{ | |
IGCS::Console::WriteError("Enabling of ResizeBuffers hook failed!"); | |
} | |
pTmpDevice->Release(); | |
pTmpContext->Release(); | |
pTmpSwapChain->Release(); | |
_tmpSwapChainInitialized = true; | |
OverlayConsole::instance().logDebug("DX11 hooks set"); | |
} | |
void createRenderTarget(IDXGISwapChain* pSwapChain) | |
{ | |
DXGI_SWAP_CHAIN_DESC sd; | |
pSwapChain->GetDesc(&sd); | |
D3D11_RENDER_TARGET_VIEW_DESC render_target_view_desc; | |
ZeroMemory(&render_target_view_desc, sizeof(render_target_view_desc)); | |
render_target_view_desc.Format = sd.BufferDesc.Format; | |
render_target_view_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; | |
pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer); | |
_device->CreateRenderTargetView(pBackBuffer, &render_target_view_desc, &_mainRenderTargetView); | |
pBackBuffer->Release(); | |
} | |
void cleanupRenderTarget() | |
{ | |
if (nullptr != _mainRenderTargetView) | |
{ | |
_mainRenderTargetView->Release(); | |
_mainRenderTargetView = nullptr; | |
} | |
} | |
void initImGui() | |
{ | |
ImGui_ImplDX11_Init(IGCS::Globals::instance().mainWindowHandle(), _device, _context); | |
ImGuiIO& io = ImGui::GetIO(); | |
io.IniFilename = IGCS_OVERLAY_INI_FILENAME; | |
initImGuiStyle(); | |
} | |
void capture_frame_(uint8_t *buffer) | |
{ | |
if (_backbuffer_format != DXGI_FORMAT_R8G8B8A8_UNORM && | |
_backbuffer_format != DXGI_FORMAT_R8G8B8A8_UNORM_SRGB && | |
_backbuffer_format != DXGI_FORMAT_B8G8R8A8_UNORM && | |
_backbuffer_format != DXGI_FORMAT_B8G8R8A8_UNORM_SRGB) | |
{ | |
return; | |
} | |
D3D11_TEXTURE2D_DESC texture_desc = {}; | |
texture_desc.Width = _width; | |
texture_desc.Height = _height; | |
texture_desc.ArraySize = 1; | |
texture_desc.MipLevels = 1; | |
texture_desc.Format = _backbuffer_format; | |
texture_desc.SampleDesc.Count = 1; | |
texture_desc.Usage = D3D11_USAGE_STAGING; | |
texture_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; | |
com_ptr<ID3D11Texture2D> texture_staging; | |
HRESULT hr = _device->CreateTexture2D(&texture_desc, nullptr, &texture_staging); | |
if (FAILED(hr)) | |
{ | |
std::cout << "Failed to create staging resource for screenshot capture! HRESULT is '" << std::hex << hr << std::dec << "'."; | |
return; | |
} | |
_context->CopyResource(texture_staging.get(), pBackBuffer); | |
D3D11_MAPPED_SUBRESOURCE mapped; | |
hr = _context->Map(texture_staging.get(), 0, D3D11_MAP_READ, 0, &mapped); | |
if (FAILED(hr)) | |
{ | |
std::cout << "Failed to map staging resource with screenshot capture! HRESULT is '" << std::hex << hr << std::dec << "'."; | |
return; | |
} | |
auto mapped_data = static_cast<BYTE *>(mapped.pData); | |
const UINT pitch = texture_desc.Width * 4; | |
for (UINT y = 0; y < texture_desc.Height; y++) | |
{ | |
CopyMemory(buffer, mapped_data, min(pitch, static_cast<UINT>(mapped.RowPitch))); | |
for (UINT x = 0; x < pitch; x += 4) | |
{ | |
buffer[x + 3] = 0xFF; | |
if (texture_desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM || texture_desc.Format == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB) | |
{ | |
std::swap(buffer[x + 0], buffer[x + 2]); | |
} | |
} | |
buffer += pitch; | |
mapped_data += mapped.RowPitch; | |
} | |
_context->Unmap(texture_staging.get(), 0); | |
} | |
void capture_frame(uint8_t *buffer) | |
{ | |
D3D11_TEXTURE2D_DESC StagingDesc; | |
pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackBuffer); | |
pBackBuffer->GetDesc(&StagingDesc); | |
StagingDesc.Usage = D3D11_USAGE_STAGING; | |
StagingDesc.BindFlags = 0; | |
StagingDesc.Width = _width; | |
StagingDesc.Height = _height; | |
StagingDesc.ArraySize = 1; | |
StagingDesc.MipLevels = 1; | |
StagingDesc.Format = _backbuffer_format; | |
StagingDesc.SampleDesc.Count = 1; | |
StagingDesc.Usage = D3D11_USAGE_STAGING; | |
StagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; | |
ID3D11Texture2D *pBackBufferStaging = NULL; | |
HRESULT hr = _device->CreateTexture2D(&StagingDesc, NULL, &pBackBufferStaging); | |
if (FAILED(hr)) | |
{ | |
OverlayConsole::instance().logDebug("Failed to create staging resource for screenshot capture!"); | |
//std::cout << "Failed to create staging resource for screenshot capture! HRESULT is '" << std::hex << hr << std::dec << "'." << std::endl; | |
return; | |
} | |
_context->CopyResource(pBackBufferStaging, pBackBuffer); | |
D3D11_MAPPED_SUBRESOURCE mapped; | |
hr = _context->Map(pBackBufferStaging, 0, D3D11_MAP_READ, 0, &mapped); | |
if (FAILED(hr)) | |
{ | |
OverlayConsole::instance().logDebug("Failed to map staging resource for screenshot capture!"); | |
//std::cout << "Failed to map staging resource with screenshot capture! HRESULT is '" << std::hex << hr << std::dec << "'." << std::endl; | |
return; | |
} | |
auto mapped_data = static_cast<BYTE *>(mapped.pData); | |
const UINT pitch = StagingDesc.Width * 4; | |
for (UINT y = 0; y < StagingDesc.Height; y++) | |
{ | |
memcpy(buffer, mapped_data, min(pitch, static_cast<UINT>(mapped.RowPitch))); | |
for (UINT x = 0; x < pitch; x += 4) | |
{ | |
buffer[x + 3] = 0xFF; | |
if (StagingDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM || StagingDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB) | |
{ | |
std::swap(buffer[x + 0], buffer[x + 2]); | |
} | |
} | |
buffer += pitch; | |
mapped_data += mapped.RowPitch; | |
} | |
_context->Unmap(pBackBufferStaging, 0); | |
} | |
void takeScreenshot(char* filename) | |
{ | |
std::vector<uint8_t> data(_width * _height * 4); | |
capture_frame_(data.data()); | |
std::cout << "Saving screenshot to " << filename << " ..." << std::endl; | |
bool _screenshot_save_success = false; // Default to a save failure unless it is reported to succeed below | |
if (FILE *file; fopen_s(&file, filename, "wb") == 0) | |
{ | |
const auto write_callback = [](void *context, void *data, int size) { | |
fwrite(data, 1, size, static_cast<FILE *>(context)); | |
}; | |
_screenshot_save_success = stbi_write_png_to_func(write_callback, file, _width, _height, 4, data.data(), 0) != 0; | |
fclose(file); | |
} | |
if (!_screenshot_save_success) | |
{ | |
OverlayConsole::instance().logDebug("Failed to write screenshot of dimensions %dx%d to... %s", _width, _height, filename); | |
//std::cout << "Failed to write screenshot to " << screenshot_path.c_str() << '!' << std::endl; | |
} | |
else { | |
OverlayConsole::instance().logDebug("Successfully wrote screenshot of dimensions %dx%d to... %s", _width, _height, filename); | |
} | |
} | |
void initImGuiStyle() | |
{ | |
ImGuiStyle& style = ImGui::GetStyle(); | |
style.WindowRounding = 2.0f; | |
style.FrameRounding = 1.0f; | |
style.IndentSpacing = 25.0f; | |
style.ScrollbarSize = 16.0f; | |
style.ScrollbarRounding = 1.0f; | |
style.Colors[ImGuiCol_Text] = ImVec4(0.84f, 0.84f, 0.88f, 1.00f); | |
style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.24f, 0.24f, 0.29f, 1.00f); | |
style.Colors[ImGuiCol_WindowBg] = ImVec4(0.07f, 0.07f, 0.09f, 0.90f); | |
style.Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); | |
style.Colors[ImGuiCol_PopupBg] = ImVec4(0.07f, 0.07f, 0.09f, 1.00f); | |
style.Colors[ImGuiCol_Border] = ImVec4(0.80f, 0.80f, 0.83f, 0.88f); | |
style.Colors[ImGuiCol_BorderShadow] = ImVec4(0.92f, 0.91f, 0.88f, 0.00f); | |
style.Colors[ImGuiCol_FrameBg] = ImVec4(0.22f, 0.22f, 0.24f, 0.31f); | |
style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.24f, 0.24f, 0.25f, 1.00f); | |
style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.35f, 0.35f, 0.38f, 1.00f); | |
style.Colors[ImGuiCol_TitleBg] = ImVec4(0.27f, 0.27f, 0.33f, 0.37f); | |
style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f); | |
style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.07f, 0.07f, 0.09f, 1.00f); | |
style.Colors[ImGuiCol_MenuBarBg] = ImVec4(0.37f, 0.37f, 0.42f, 0.42f); | |
style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.09f, 0.09f, 0.10f, 1.00f); | |
style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.45f, 0.45f, 0.45f, 0.30f); | |
style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.59f, 0.59f, 0.59f, 1.00f); | |
style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.59f, 0.59f, 0.59f, 1.00f); | |
style.Colors[ImGuiCol_ComboBg] = ImVec4(0.13f, 0.13f, 0.16f, 1.00f); | |
style.Colors[ImGuiCol_CheckMark] = ImVec4(0.80f, 0.80f, 0.83f, 0.53f); | |
style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.65f, 0.31f, 0.00f, 0.71f); | |
style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.59f, 0.59f, 0.59f, 1.00f); | |
style.Colors[ImGuiCol_Button] = ImVec4(0.65f, 0.31f, 0.00f, 0.86f); | |
style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.80f, 0.41f, 0.00f, 1.00f); | |
style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.56f, 0.56f, 0.58f, 1.00f); | |
style.Colors[ImGuiCol_Header] = ImVec4(0.50f, 0.50f, 0.53f, 0.49f); | |
style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.47f, 0.47f, 0.49f, 1.00f); | |
style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.40f, 0.40f, 0.44f, 0.31f); | |
style.Colors[ImGuiCol_ResizeGrip] = ImVec4(0.44f, 0.44f, 0.44f, 0.30f); | |
style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.59f, 0.59f, 0.59f, 1.00f); | |
style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(0.59f, 0.59f, 0.59f, 1.00f); | |
style.Colors[ImGuiCol_CloseButton] = ImVec4(0.40f, 0.40f, 0.40f, 0.44f); | |
style.Colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.40f, 0.40f, 0.40f, 0.93f); | |
style.Colors[ImGuiCol_CloseButtonActive] = ImVec4(0.40f, 0.39f, 0.38f, 1.00f); | |
style.Colors[ImGuiCol_PlotLines] = ImVec4(0.40f, 0.39f, 0.38f, 0.63f); | |
style.Colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.25f, 1.00f, 0.00f, 1.00f); | |
style.Colors[ImGuiCol_PlotHistogram] = ImVec4(0.40f, 0.39f, 0.38f, 0.63f); | |
style.Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(0.25f, 1.00f, 0.00f, 1.00f); | |
style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.25f, 1.00f, 0.00f, 0.43f); | |
style.Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(1.00f, 0.98f, 0.95f, 0.73f); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment