d3d9-capture.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845
  1. #include <d3d9.h>
  2. #include <d3d11.h>
  3. #include <dxgi.h>
  4. #include "graphics-hook.h"
  5. #include "d3d9-patches.hpp"
  6. #include <detours.h>
  7. typedef HRESULT(STDMETHODCALLTYPE *present_t)(IDirect3DDevice9 *, CONST RECT *, CONST RECT *, HWND, CONST RGNDATA *);
  8. typedef HRESULT(STDMETHODCALLTYPE *present_ex_t)(IDirect3DDevice9Ex *, CONST RECT *, CONST RECT *, HWND,
  9. CONST RGNDATA *, DWORD);
  10. typedef HRESULT(STDMETHODCALLTYPE *present_swap_t)(IDirect3DSwapChain9 *, CONST RECT *, CONST RECT *, HWND,
  11. CONST RGNDATA *, DWORD);
  12. typedef HRESULT(STDMETHODCALLTYPE *reset_t)(IDirect3DDevice9 *, D3DPRESENT_PARAMETERS *);
  13. typedef HRESULT(STDMETHODCALLTYPE *reset_ex_t)(IDirect3DDevice9 *, D3DPRESENT_PARAMETERS *, D3DDISPLAYMODEEX *);
  14. typedef HRESULT(WINAPI *createfactory1_t)(REFIID, void **);
  15. present_t RealPresent = NULL;
  16. present_ex_t RealPresentEx = NULL;
  17. present_swap_t RealPresentSwap = NULL;
  18. reset_t RealReset = NULL;
  19. reset_ex_t RealResetEx = NULL;
  20. struct d3d9_data {
  21. HMODULE d3d9;
  22. IDirect3DDevice9 *device; /* do not release */
  23. uint32_t cx;
  24. uint32_t cy;
  25. D3DFORMAT d3d9_format;
  26. DXGI_FORMAT dxgi_format;
  27. bool using_shtex;
  28. /* shared texture */
  29. IDirect3DSurface9 *d3d9_copytex;
  30. ID3D11Device *d3d11_device;
  31. ID3D11DeviceContext *d3d11_context;
  32. ID3D11Resource *d3d11_tex;
  33. struct shtex_data *shtex_info;
  34. HANDLE handle;
  35. int patch;
  36. /* shared memory */
  37. IDirect3DSurface9 *copy_surfaces[NUM_BUFFERS];
  38. IDirect3DQuery9 *queries[NUM_BUFFERS];
  39. struct shmem_data *shmem_info;
  40. bool texture_mapped[NUM_BUFFERS];
  41. volatile bool issued_queries[NUM_BUFFERS];
  42. uint32_t pitch;
  43. int cur_tex;
  44. int copy_wait;
  45. };
  46. static struct d3d9_data data = {};
  47. static void d3d9_free()
  48. {
  49. capture_free();
  50. if (data.using_shtex) {
  51. if (data.d3d11_tex)
  52. data.d3d11_tex->Release();
  53. if (data.d3d11_context)
  54. data.d3d11_context->Release();
  55. if (data.d3d11_device)
  56. data.d3d11_device->Release();
  57. if (data.d3d9_copytex)
  58. data.d3d9_copytex->Release();
  59. } else {
  60. for (size_t i = 0; i < NUM_BUFFERS; i++) {
  61. if (data.copy_surfaces[i]) {
  62. if (data.texture_mapped[i])
  63. data.copy_surfaces[i]->UnlockRect();
  64. data.copy_surfaces[i]->Release();
  65. }
  66. if (data.queries[i])
  67. data.queries[i]->Release();
  68. }
  69. }
  70. memset(&data, 0, sizeof(data));
  71. hlog("----------------- d3d9 capture freed -----------------");
  72. }
  73. static DXGI_FORMAT d3d9_to_dxgi_format(D3DFORMAT format)
  74. {
  75. switch (format) {
  76. case D3DFMT_A2B10G10R10:
  77. return DXGI_FORMAT_R10G10B10A2_UNORM;
  78. case D3DFMT_A8R8G8B8:
  79. return DXGI_FORMAT_B8G8R8A8_UNORM;
  80. case D3DFMT_X8R8G8B8:
  81. return DXGI_FORMAT_B8G8R8X8_UNORM;
  82. }
  83. return DXGI_FORMAT_UNKNOWN;
  84. }
  85. const static D3D_FEATURE_LEVEL feature_levels[] = {
  86. D3D_FEATURE_LEVEL_11_0,
  87. D3D_FEATURE_LEVEL_10_1,
  88. D3D_FEATURE_LEVEL_10_0,
  89. };
  90. static inline bool shex_init_d3d11()
  91. {
  92. PFN_D3D11_CREATE_DEVICE create_device;
  93. createfactory1_t create_factory;
  94. D3D_FEATURE_LEVEL level_used;
  95. IDXGIFactory *factory;
  96. IDXGIAdapter *adapter;
  97. HMODULE d3d11;
  98. HMODULE dxgi;
  99. HRESULT hr;
  100. d3d11 = load_system_library("d3d11.dll");
  101. if (!d3d11) {
  102. hlog("d3d9_init: Failed to load D3D11");
  103. return false;
  104. }
  105. dxgi = load_system_library("dxgi.dll");
  106. if (!dxgi) {
  107. hlog("d3d9_init: Failed to load DXGI");
  108. return false;
  109. }
  110. create_factory = (createfactory1_t)GetProcAddress(dxgi, "CreateDXGIFactory1");
  111. if (!create_factory) {
  112. hlog("d3d9_init: Failed to get CreateDXGIFactory1 address");
  113. return false;
  114. }
  115. create_device = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(d3d11, "D3D11CreateDevice");
  116. if (!create_device) {
  117. hlog("d3d9_init: Failed to get D3D11CreateDevice address");
  118. return false;
  119. }
  120. hr = create_factory(__uuidof(IDXGIFactory1), (void **)&factory);
  121. if (FAILED(hr)) {
  122. hlog_hr("d3d9_init: Failed to create factory object", hr);
  123. return false;
  124. }
  125. hr = factory->EnumAdapters(0, &adapter);
  126. factory->Release();
  127. if (FAILED(hr)) {
  128. hlog_hr("d3d9_init: Failed to get adapter", hr);
  129. return false;
  130. }
  131. hr = create_device(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, feature_levels,
  132. sizeof(feature_levels) / sizeof(D3D_FEATURE_LEVEL), D3D11_SDK_VERSION, &data.d3d11_device,
  133. &level_used, &data.d3d11_context);
  134. adapter->Release();
  135. if (FAILED(hr)) {
  136. hlog_hr("d3d9_init: Failed to create D3D11 device", hr);
  137. return false;
  138. }
  139. return true;
  140. }
  141. static inline bool d3d9_shtex_init_shtex()
  142. {
  143. IDXGIResource *res;
  144. HRESULT hr;
  145. D3D11_TEXTURE2D_DESC desc = {};
  146. desc.Width = data.cx;
  147. desc.Height = data.cy;
  148. desc.Format = data.dxgi_format;
  149. desc.MipLevels = 1;
  150. desc.ArraySize = 1;
  151. desc.SampleDesc.Count = 1;
  152. desc.Usage = D3D11_USAGE_DEFAULT;
  153. desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
  154. desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
  155. hr = data.d3d11_device->CreateTexture2D(&desc, nullptr, (ID3D11Texture2D **)&data.d3d11_tex);
  156. if (FAILED(hr)) {
  157. hlog_hr("d3d9_shtex_init_shtex: Failed to create D3D11 texture", hr);
  158. return false;
  159. }
  160. hr = data.d3d11_tex->QueryInterface(__uuidof(IDXGIResource), (void **)&res);
  161. if (FAILED(hr)) {
  162. hlog_hr("d3d9_shtex_init_shtex: Failed to query IDXGIResource", hr);
  163. return false;
  164. }
  165. hr = res->GetSharedHandle(&data.handle);
  166. res->Release();
  167. if (FAILED(hr)) {
  168. hlog_hr("d3d9_shtex_init_shtex: Failed to get shared handle", hr);
  169. return false;
  170. }
  171. return true;
  172. }
  173. static inline bool d3d9_shtex_init_copytex()
  174. {
  175. struct d3d9_offsets offsets = global_hook_info->offsets.d3d9;
  176. uint8_t *patch_addr = nullptr;
  177. BOOL *p_is_d3d9 = nullptr;
  178. uint8_t saved_data[MAX_PATCH_SIZE];
  179. size_t patch_size = 0;
  180. BOOL was_d3d9ex = false;
  181. IDirect3DTexture9 *tex;
  182. DWORD protect_val;
  183. HRESULT hr;
  184. if (offsets.d3d9_clsoff && offsets.is_d3d9ex_clsoff) {
  185. uint8_t *device_ptr = (uint8_t *)(data.device);
  186. uint8_t *d3d9_ptr = *(uint8_t **)(device_ptr + offsets.d3d9_clsoff);
  187. p_is_d3d9 = (BOOL *)(d3d9_ptr + offsets.is_d3d9ex_clsoff);
  188. } else {
  189. patch_addr = get_d3d9_patch_addr(data.d3d9, data.patch);
  190. }
  191. if (p_is_d3d9) {
  192. was_d3d9ex = *p_is_d3d9;
  193. *p_is_d3d9 = true;
  194. } else if (patch_addr) {
  195. patch_size = patch[data.patch].size;
  196. VirtualProtect(patch_addr, patch_size, PAGE_EXECUTE_READWRITE, &protect_val);
  197. memcpy(saved_data, patch_addr, patch_size);
  198. memcpy(patch_addr, patch[data.patch].data, patch_size);
  199. }
  200. hr = data.device->CreateTexture(data.cx, data.cy, 1, D3DUSAGE_RENDERTARGET, data.d3d9_format, D3DPOOL_DEFAULT,
  201. &tex, &data.handle);
  202. if (p_is_d3d9) {
  203. *p_is_d3d9 = was_d3d9ex;
  204. } else if (patch_addr && patch_size) {
  205. memcpy(patch_addr, saved_data, patch_size);
  206. VirtualProtect(patch_addr, patch_size, protect_val, &protect_val);
  207. }
  208. if (FAILED(hr)) {
  209. hlog_hr("d3d9_shtex_init_copytex: Failed to create shared texture", hr);
  210. return false;
  211. }
  212. hr = tex->GetSurfaceLevel(0, &data.d3d9_copytex);
  213. tex->Release();
  214. if (FAILED(hr)) {
  215. hlog_hr("d3d9_shtex_init_copytex: Failed to get surface level", hr);
  216. return false;
  217. }
  218. return true;
  219. }
  220. static bool d3d9_shtex_init(HWND window)
  221. {
  222. data.using_shtex = true;
  223. if (!shex_init_d3d11()) {
  224. return false;
  225. }
  226. if (!d3d9_shtex_init_shtex()) {
  227. return false;
  228. }
  229. if (!d3d9_shtex_init_copytex()) {
  230. return false;
  231. }
  232. if (!capture_init_shtex(&data.shtex_info, window, data.cx, data.cy, data.dxgi_format, false,
  233. (uintptr_t)data.handle)) {
  234. return false;
  235. }
  236. hlog("d3d9 shared texture capture successful");
  237. return true;
  238. }
  239. static bool d3d9_shmem_init_buffers(size_t buffer)
  240. {
  241. HRESULT hr;
  242. hr = data.device->CreateOffscreenPlainSurface(data.cx, data.cy, data.d3d9_format, D3DPOOL_SYSTEMMEM,
  243. &data.copy_surfaces[buffer], nullptr);
  244. if (FAILED(hr)) {
  245. hlog_hr("d3d9_shmem_init_buffers: Failed to create surface", hr);
  246. return false;
  247. }
  248. if (buffer == 0) {
  249. D3DLOCKED_RECT rect;
  250. hr = data.copy_surfaces[buffer]->LockRect(&rect, nullptr, D3DLOCK_READONLY);
  251. if (FAILED(hr)) {
  252. hlog_hr("d3d9_shmem_init_buffers: Failed to lock "
  253. "buffer",
  254. hr);
  255. return false;
  256. }
  257. data.pitch = rect.Pitch;
  258. data.copy_surfaces[buffer]->UnlockRect();
  259. }
  260. hr = data.device->CreateQuery(D3DQUERYTYPE_EVENT, &data.queries[buffer]);
  261. if (FAILED(hr)) {
  262. hlog_hr("d3d9_shmem_init_buffers: Failed to create query", hr);
  263. return false;
  264. }
  265. return true;
  266. }
  267. static bool d3d9_shmem_init(HWND window)
  268. {
  269. data.using_shtex = false;
  270. for (size_t i = 0; i < NUM_BUFFERS; i++) {
  271. if (!d3d9_shmem_init_buffers(i)) {
  272. return false;
  273. }
  274. }
  275. if (!capture_init_shmem(&data.shmem_info, window, data.cx, data.cy, data.pitch, data.dxgi_format, false)) {
  276. return false;
  277. }
  278. hlog("d3d9 memory capture successful");
  279. return true;
  280. }
  281. static bool d3d9_get_swap_desc(D3DPRESENT_PARAMETERS &pp)
  282. {
  283. IDirect3DSwapChain9 *swap = nullptr;
  284. HRESULT hr;
  285. hr = data.device->GetSwapChain(0, &swap);
  286. if (FAILED(hr)) {
  287. hlog_hr("d3d9_get_swap_desc: Failed to get swap chain", hr);
  288. return false;
  289. }
  290. hr = swap->GetPresentParameters(&pp);
  291. swap->Release();
  292. if (FAILED(hr)) {
  293. hlog_hr("d3d9_get_swap_desc: Failed to get "
  294. "presentation parameters",
  295. hr);
  296. return false;
  297. }
  298. return true;
  299. }
  300. static bool d3d9_init_format_backbuffer(HWND &window)
  301. {
  302. IDirect3DSurface9 *back_buffer = nullptr;
  303. D3DPRESENT_PARAMETERS pp;
  304. D3DSURFACE_DESC desc;
  305. HRESULT hr;
  306. if (!d3d9_get_swap_desc(pp)) {
  307. return false;
  308. }
  309. hr = data.device->GetRenderTarget(0, &back_buffer);
  310. if (FAILED(hr)) {
  311. return false;
  312. }
  313. hr = back_buffer->GetDesc(&desc);
  314. back_buffer->Release();
  315. if (FAILED(hr)) {
  316. hlog_hr("d3d9_init_format_backbuffer: Failed to get "
  317. "backbuffer descriptor",
  318. hr);
  319. return false;
  320. }
  321. data.d3d9_format = desc.Format;
  322. data.dxgi_format = d3d9_to_dxgi_format(desc.Format);
  323. window = pp.hDeviceWindow;
  324. data.cx = desc.Width;
  325. data.cy = desc.Height;
  326. return true;
  327. }
  328. static bool d3d9_init_format_swapchain(HWND &window)
  329. {
  330. D3DPRESENT_PARAMETERS pp;
  331. if (!d3d9_get_swap_desc(pp)) {
  332. return false;
  333. }
  334. data.dxgi_format = d3d9_to_dxgi_format(pp.BackBufferFormat);
  335. data.d3d9_format = pp.BackBufferFormat;
  336. window = pp.hDeviceWindow;
  337. data.cx = pp.BackBufferWidth;
  338. data.cy = pp.BackBufferHeight;
  339. return true;
  340. }
  341. static void d3d9_init(IDirect3DDevice9 *device)
  342. {
  343. IDirect3DDevice9Ex *d3d9ex = nullptr;
  344. bool has_d3d9ex_bool_offset = global_hook_info->offsets.d3d9.d3d9_clsoff &&
  345. global_hook_info->offsets.d3d9.is_d3d9ex_clsoff;
  346. bool success;
  347. HWND window = nullptr;
  348. HRESULT hr;
  349. data.d3d9 = get_system_module("d3d9.dll");
  350. data.device = device;
  351. hr = device->QueryInterface(__uuidof(IDirect3DDevice9Ex), (void **)&d3d9ex);
  352. if (SUCCEEDED(hr)) {
  353. d3d9ex->Release();
  354. data.patch = -1;
  355. } else if (!has_d3d9ex_bool_offset) {
  356. data.patch = get_d3d9_patch(data.d3d9);
  357. } else {
  358. data.patch = -1;
  359. }
  360. if (!d3d9_init_format_backbuffer(window)) {
  361. if (!d3d9_init_format_swapchain(window)) {
  362. return;
  363. }
  364. }
  365. if (global_hook_info->force_shmem || (!d3d9ex && data.patch == -1 && !has_d3d9ex_bool_offset)) {
  366. success = d3d9_shmem_init(window);
  367. } else {
  368. success = d3d9_shtex_init(window);
  369. }
  370. if (!success)
  371. d3d9_free();
  372. }
  373. static inline HRESULT get_backbuffer(IDirect3DDevice9 *device, IDirect3DSurface9 **surface)
  374. {
  375. static bool use_backbuffer = false;
  376. static bool checked_exceptions = false;
  377. if (!checked_exceptions) {
  378. if (_strcmpi(get_process_name(), "hotd_ng.exe") == 0)
  379. use_backbuffer = true;
  380. checked_exceptions = true;
  381. }
  382. if (use_backbuffer) {
  383. return device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, surface);
  384. } else {
  385. return device->GetRenderTarget(0, surface);
  386. }
  387. }
  388. static inline void d3d9_shtex_capture(IDirect3DSurface9 *backbuffer)
  389. {
  390. HRESULT hr;
  391. hr = data.device->StretchRect(backbuffer, nullptr, data.d3d9_copytex, nullptr, D3DTEXF_NONE);
  392. if (FAILED(hr))
  393. hlog_hr("d3d9_shtex_capture: StretchRect failed", hr);
  394. }
  395. static void d3d9_shmem_capture_copy(int i)
  396. {
  397. IDirect3DSurface9 *target = data.copy_surfaces[i];
  398. D3DLOCKED_RECT rect;
  399. HRESULT hr;
  400. if (!data.issued_queries[i]) {
  401. return;
  402. }
  403. if (data.queries[i]->GetData(0, 0, 0) != S_OK) {
  404. return;
  405. }
  406. data.issued_queries[i] = false;
  407. hr = target->LockRect(&rect, nullptr, D3DLOCK_READONLY);
  408. if (SUCCEEDED(hr)) {
  409. data.texture_mapped[i] = true;
  410. shmem_copy_data(i, rect.pBits);
  411. }
  412. }
  413. static inline void d3d9_shmem_capture(IDirect3DSurface9 *backbuffer)
  414. {
  415. int next_tex;
  416. HRESULT hr;
  417. next_tex = (data.cur_tex + 1) % NUM_BUFFERS;
  418. d3d9_shmem_capture_copy(next_tex);
  419. if (data.copy_wait < NUM_BUFFERS - 1) {
  420. data.copy_wait++;
  421. } else {
  422. IDirect3DSurface9 *src = backbuffer;
  423. IDirect3DSurface9 *dst = data.copy_surfaces[data.cur_tex];
  424. if (shmem_texture_data_lock(data.cur_tex)) {
  425. dst->UnlockRect();
  426. data.texture_mapped[data.cur_tex] = false;
  427. shmem_texture_data_unlock(data.cur_tex);
  428. }
  429. hr = data.device->GetRenderTargetData(src, dst);
  430. if (FAILED(hr)) {
  431. hlog_hr("d3d9_shmem_capture: GetRenderTargetData "
  432. "failed",
  433. hr);
  434. }
  435. data.queries[data.cur_tex]->Issue(D3DISSUE_END);
  436. data.issued_queries[data.cur_tex] = true;
  437. }
  438. data.cur_tex = next_tex;
  439. }
  440. static void d3d9_capture(IDirect3DDevice9 *device, IDirect3DSurface9 *backbuffer)
  441. {
  442. if (capture_should_stop()) {
  443. d3d9_free();
  444. }
  445. if (capture_should_init()) {
  446. d3d9_init(device);
  447. }
  448. if (data.handle != nullptr && capture_ready()) {
  449. if (data.device != device) {
  450. d3d9_free();
  451. return;
  452. }
  453. if (data.using_shtex)
  454. d3d9_shtex_capture(backbuffer);
  455. else
  456. d3d9_shmem_capture(backbuffer);
  457. }
  458. }
  459. /* this is used just in case Present calls PresentEx or vise versa. */
  460. static int present_recurse = 0;
  461. static inline void present_begin(IDirect3DDevice9 *device, IDirect3DSurface9 *&backbuffer)
  462. {
  463. HRESULT hr;
  464. if (!present_recurse) {
  465. hr = get_backbuffer(device, &backbuffer);
  466. if (FAILED(hr)) {
  467. hlog_hr("d3d9_shmem_capture: Failed to get "
  468. "backbuffer",
  469. hr);
  470. }
  471. if (!global_hook_info->capture_overlay) {
  472. d3d9_capture(device, backbuffer);
  473. }
  474. }
  475. present_recurse++;
  476. }
  477. static inline void present_end(IDirect3DDevice9 *device, IDirect3DSurface9 *backbuffer)
  478. {
  479. present_recurse--;
  480. if (!present_recurse) {
  481. if (global_hook_info->capture_overlay) {
  482. if (!present_recurse)
  483. d3d9_capture(device, backbuffer);
  484. }
  485. if (backbuffer)
  486. backbuffer->Release();
  487. }
  488. }
  489. static bool hooked_reset = false;
  490. static void setup_reset_hooks(IDirect3DDevice9 *device);
  491. static HRESULT STDMETHODCALLTYPE hook_present(IDirect3DDevice9 *device, CONST RECT *src_rect, CONST RECT *dst_rect,
  492. HWND override_window, CONST RGNDATA *dirty_region)
  493. {
  494. IDirect3DSurface9 *backbuffer = nullptr;
  495. if (!hooked_reset)
  496. setup_reset_hooks(device);
  497. present_begin(device, backbuffer);
  498. const HRESULT hr = RealPresent(device, src_rect, dst_rect, override_window, dirty_region);
  499. present_end(device, backbuffer);
  500. return hr;
  501. }
  502. static HRESULT STDMETHODCALLTYPE hook_present_ex(IDirect3DDevice9Ex *device, CONST RECT *src_rect, CONST RECT *dst_rect,
  503. HWND override_window, CONST RGNDATA *dirty_region, DWORD flags)
  504. {
  505. IDirect3DSurface9 *backbuffer = nullptr;
  506. if (!hooked_reset)
  507. setup_reset_hooks(device);
  508. present_begin(device, backbuffer);
  509. const HRESULT hr = RealPresentEx(device, src_rect, dst_rect, override_window, dirty_region, flags);
  510. present_end(device, backbuffer);
  511. return hr;
  512. }
  513. static HRESULT STDMETHODCALLTYPE hook_present_swap(IDirect3DSwapChain9 *swap, CONST RECT *src_rect,
  514. CONST RECT *dst_rect, HWND override_window,
  515. CONST RGNDATA *dirty_region, DWORD flags)
  516. {
  517. IDirect3DSurface9 *backbuffer = nullptr;
  518. IDirect3DDevice9 *device = nullptr;
  519. if (!present_recurse) {
  520. IDirect3DDevice9 *temp;
  521. if (SUCCEEDED(swap->GetDevice(&temp))) {
  522. device = temp;
  523. }
  524. }
  525. if (device) {
  526. if (!hooked_reset)
  527. setup_reset_hooks(device);
  528. present_begin(device, backbuffer);
  529. }
  530. const HRESULT hr = RealPresentSwap(swap, src_rect, dst_rect, override_window, dirty_region, flags);
  531. if (device) {
  532. present_end(device, backbuffer);
  533. device->Release();
  534. }
  535. return hr;
  536. }
  537. static HRESULT STDMETHODCALLTYPE hook_reset(IDirect3DDevice9 *device, D3DPRESENT_PARAMETERS *params)
  538. {
  539. if (capture_active())
  540. d3d9_free();
  541. return RealReset(device, params);
  542. }
  543. static HRESULT STDMETHODCALLTYPE hook_reset_ex(IDirect3DDevice9 *device, D3DPRESENT_PARAMETERS *params,
  544. D3DDISPLAYMODEEX *dmex)
  545. {
  546. if (capture_active())
  547. d3d9_free();
  548. return RealResetEx(device, params, dmex);
  549. }
  550. static void setup_reset_hooks(IDirect3DDevice9 *device)
  551. {
  552. IDirect3DDevice9Ex *d3d9ex = nullptr;
  553. uintptr_t *vtable = *(uintptr_t **)device;
  554. HRESULT hr;
  555. DetourTransactionBegin();
  556. RealReset = (reset_t)vtable[16];
  557. DetourAttach((PVOID *)&RealReset, hook_reset);
  558. hr = device->QueryInterface(IID_PPV_ARGS(&d3d9ex));
  559. if (SUCCEEDED(hr)) {
  560. RealResetEx = (reset_ex_t)vtable[132];
  561. DetourAttach((PVOID *)&RealResetEx, hook_reset_ex);
  562. d3d9ex->Release();
  563. }
  564. const LONG error = DetourTransactionCommit();
  565. const bool success = error == NO_ERROR;
  566. if (success) {
  567. hlog("Hooked IDirect3DDevice9::Reset");
  568. if (RealResetEx)
  569. hlog("Hooked IDirect3DDevice9Ex::ResetEx");
  570. hooked_reset = true;
  571. } else {
  572. RealReset = nullptr;
  573. RealResetEx = nullptr;
  574. }
  575. }
  576. typedef HRESULT(WINAPI *d3d9create_ex_t)(UINT, IDirect3D9Ex **);
  577. static bool manually_get_d3d9_addrs(HMODULE d3d9_module, void **present_addr, void **present_ex_addr,
  578. void **present_swap_addr)
  579. {
  580. d3d9create_ex_t create_ex;
  581. D3DPRESENT_PARAMETERS pp;
  582. HRESULT hr;
  583. IDirect3DDevice9Ex *device;
  584. IDirect3D9Ex *d3d9ex;
  585. hlog("D3D9 values invalid, manually obtaining");
  586. create_ex = (d3d9create_ex_t)GetProcAddress(d3d9_module, "Direct3DCreate9Ex");
  587. if (!create_ex) {
  588. hlog("Failed to load Direct3DCreate9Ex");
  589. return false;
  590. }
  591. if (FAILED(create_ex(D3D_SDK_VERSION, &d3d9ex))) {
  592. hlog("Failed to create D3D9 context");
  593. return false;
  594. }
  595. memset(&pp, 0, sizeof(pp));
  596. pp.Windowed = 1;
  597. pp.SwapEffect = D3DSWAPEFFECT_FLIP;
  598. pp.BackBufferFormat = D3DFMT_A8R8G8B8;
  599. pp.BackBufferCount = 1;
  600. pp.hDeviceWindow = (HWND)dummy_window;
  601. pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
  602. hr = d3d9ex->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, dummy_window,
  603. D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_NOWINDOWCHANGES, &pp, NULL,
  604. &device);
  605. d3d9ex->Release();
  606. if (SUCCEEDED(hr)) {
  607. uintptr_t *vtable = *(uintptr_t **)device;
  608. IDirect3DSwapChain9 *swap;
  609. *present_addr = (void *)vtable[17];
  610. *present_ex_addr = (void *)vtable[121];
  611. hr = device->GetSwapChain(0, &swap);
  612. if (SUCCEEDED(hr)) {
  613. vtable = *(uintptr_t **)swap;
  614. *present_swap_addr = (void *)vtable[3];
  615. swap->Release();
  616. }
  617. device->Release();
  618. } else {
  619. hlog("Failed to create D3D9 device");
  620. return false;
  621. }
  622. return true;
  623. }
  624. bool hook_d3d9(void)
  625. {
  626. HMODULE d3d9_module = get_system_module("d3d9.dll");
  627. uint32_t d3d9_size;
  628. void *present_addr = nullptr;
  629. void *present_ex_addr = nullptr;
  630. void *present_swap_addr = nullptr;
  631. if (!d3d9_module) {
  632. return false;
  633. }
  634. d3d9_size = module_size(d3d9_module);
  635. if (global_hook_info->offsets.d3d9.present < d3d9_size &&
  636. global_hook_info->offsets.d3d9.present_ex < d3d9_size &&
  637. global_hook_info->offsets.d3d9.present_swap < d3d9_size) {
  638. present_addr = get_offset_addr(d3d9_module, global_hook_info->offsets.d3d9.present);
  639. present_ex_addr = get_offset_addr(d3d9_module, global_hook_info->offsets.d3d9.present_ex);
  640. present_swap_addr = get_offset_addr(d3d9_module, global_hook_info->offsets.d3d9.present_swap);
  641. } else {
  642. if (!dummy_window) {
  643. return false;
  644. }
  645. if (!manually_get_d3d9_addrs(d3d9_module, &present_addr, &present_ex_addr, &present_swap_addr)) {
  646. hlog("Failed to get D3D9 values");
  647. return true;
  648. }
  649. }
  650. if (!present_addr && !present_ex_addr && !present_swap_addr) {
  651. hlog("Invalid D3D9 values");
  652. return true;
  653. }
  654. DetourTransactionBegin();
  655. if (present_swap_addr) {
  656. RealPresentSwap = (present_swap_t)present_swap_addr;
  657. DetourAttach((PVOID *)&RealPresentSwap, hook_present_swap);
  658. }
  659. if (present_ex_addr) {
  660. RealPresentEx = (present_ex_t)present_ex_addr;
  661. DetourAttach((PVOID *)&RealPresentEx, hook_present_ex);
  662. }
  663. if (present_addr) {
  664. RealPresent = (present_t)present_addr;
  665. DetourAttach((PVOID *)&RealPresent, hook_present);
  666. }
  667. const LONG error = DetourTransactionCommit();
  668. const bool success = error == NO_ERROR;
  669. if (success) {
  670. if (RealPresentSwap)
  671. hlog("Hooked IDirect3DSwapChain9::Present");
  672. if (RealPresentEx)
  673. hlog("Hooked IDirect3DDevice9Ex::PresentEx");
  674. if (RealPresent)
  675. hlog("Hooked IDirect3DDevice9::Present");
  676. hlog("Hooked D3D9");
  677. } else {
  678. RealPresentSwap = nullptr;
  679. RealPresentEx = nullptr;
  680. RealPresent = nullptr;
  681. hlog("Failed to attach Detours hook: %ld", error);
  682. }
  683. return success;
  684. }