d3d9-capture.cpp 21 KB

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