d3d9-capture.cpp 20 KB

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