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