d3d9-capture.cpp 22 KB

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