d3d9-capture.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  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. 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", hr);
  238. return false;
  239. }
  240. return true;
  241. }
  242. static bool d3d9_shtex_init(uint32_t cx, uint32_t cy, HWND window)
  243. {
  244. data.using_shtex = true;
  245. if (!shex_init_d3d11()) {
  246. return false;
  247. }
  248. if (!d3d9_shtex_init_shtex()) {
  249. return false;
  250. }
  251. if (!d3d9_shtex_init_copytex()) {
  252. return false;
  253. }
  254. if (!capture_init_shtex(&data.shtex_info, window, cx, cy,
  255. data.cx, data.cy, data.dxgi_format, false,
  256. (uintptr_t)data.handle)) {
  257. return false;
  258. }
  259. hlog("d3d9 shared texture capture successful");
  260. return true;
  261. }
  262. static bool d3d9_shmem_init_buffers(size_t buffer)
  263. {
  264. HRESULT hr;
  265. hr = data.device->CreateOffscreenPlainSurface(data.cx, data.cy,
  266. data.d3d9_format, D3DPOOL_SYSTEMMEM,
  267. &data.copy_surfaces[buffer], nullptr);
  268. if (FAILED(hr)) {
  269. hlog_hr("d3d9_shmem_init_buffers: Failed to create surface",
  270. hr);
  271. return false;
  272. }
  273. if (buffer == 0) {
  274. D3DLOCKED_RECT rect;
  275. hr = data.copy_surfaces[buffer]->LockRect(&rect, nullptr,
  276. D3DLOCK_READONLY);
  277. if (FAILED(hr)) {
  278. hlog_hr("d3d9_shmem_init_buffers: Failed to lock "
  279. "buffer", hr);
  280. return false;
  281. }
  282. data.pitch = rect.Pitch;
  283. data.copy_surfaces[buffer]->UnlockRect();
  284. }
  285. hr = data.device->CreateRenderTarget(data.cx, data.cy,
  286. data.d3d9_format, D3DMULTISAMPLE_NONE, 0, false,
  287. &data.render_targets[buffer], nullptr);
  288. if (FAILED(hr)) {
  289. hlog_hr("d3d9_shmem_init_buffers: Failed to create render "
  290. "target", hr);
  291. return false;
  292. }
  293. hr = data.device->CreateQuery(D3DQUERYTYPE_EVENT,
  294. &data.queries[buffer]);
  295. if (FAILED(hr)) {
  296. hlog_hr("d3d9_shmem_init_buffers: Failed to create query", hr);
  297. return false;
  298. }
  299. return true;
  300. }
  301. static bool d3d9_shmem_init(uint32_t cx, uint32_t cy, HWND window)
  302. {
  303. data.using_shtex = false;
  304. for (size_t i = 0; i < NUM_BUFFERS; i++) {
  305. if (!d3d9_shmem_init_buffers(i)) {
  306. return false;
  307. }
  308. }
  309. if (!capture_init_shmem(&data.shmem_info, window, cx, cy,
  310. data.cx, data.cy, data.pitch, data.dxgi_format,
  311. false)) {
  312. return false;
  313. }
  314. hlog("d3d9 memory capture successful");
  315. return true;
  316. }
  317. static bool d3d9_get_swap_desc(D3DPRESENT_PARAMETERS &pp)
  318. {
  319. IDirect3DSwapChain9 *swap = nullptr;
  320. HRESULT hr;
  321. hr = data.device->GetSwapChain(0, &swap);
  322. if (FAILED(hr)) {
  323. hlog_hr("d3d9_get_swap_desc: Failed to get swap chain", hr);
  324. return false;
  325. }
  326. hr = swap->GetPresentParameters(&pp);
  327. swap->Release();
  328. if (FAILED(hr)) {
  329. hlog_hr("d3d9_get_swap_desc: Failed to get "
  330. "presentation parameters", hr);
  331. return false;
  332. }
  333. return true;
  334. }
  335. static bool d3d9_init_format_backbuffer(uint32_t &cx, uint32_t &cy,
  336. HWND &window)
  337. {
  338. IDirect3DSurface9 *back_buffer = nullptr;
  339. D3DPRESENT_PARAMETERS pp;
  340. D3DSURFACE_DESC desc;
  341. HRESULT hr;
  342. if (!d3d9_get_swap_desc(pp)) {
  343. return false;
  344. }
  345. hr = data.device->GetRenderTarget(0, &back_buffer);
  346. if (FAILED(hr)) {
  347. return false;
  348. }
  349. hr = back_buffer->GetDesc(&desc);
  350. back_buffer->Release();
  351. if (FAILED(hr)) {
  352. hlog_hr("d3d9_init_format_backbuffer: Failed to get "
  353. "backbuffer descriptor", hr);
  354. return false;
  355. }
  356. data.d3d9_format = desc.Format;
  357. data.dxgi_format = d3d9_to_dxgi_format(desc.Format);
  358. data.using_scale = global_hook_info->use_scale;
  359. window = pp.hDeviceWindow;
  360. cx = desc.Width;
  361. cy = desc.Height;
  362. if (data.using_scale) {
  363. data.cx = global_hook_info->cx;
  364. data.cy = global_hook_info->cy;
  365. } else {
  366. data.cx = desc.Width;
  367. data.cy = desc.Height;
  368. }
  369. return true;
  370. }
  371. static bool d3d9_init_format_swapchain(uint32_t &cx, uint32_t &cy, HWND &window)
  372. {
  373. D3DPRESENT_PARAMETERS pp;
  374. if (!d3d9_get_swap_desc(pp)) {
  375. return false;
  376. }
  377. data.dxgi_format = d3d9_to_dxgi_format(pp.BackBufferFormat);
  378. data.d3d9_format = pp.BackBufferFormat;
  379. data.using_scale = global_hook_info->use_scale;
  380. window = pp.hDeviceWindow;
  381. cx = pp.BackBufferWidth;
  382. cy = pp.BackBufferHeight;
  383. if (data.using_scale) {
  384. data.cx = global_hook_info->cx;
  385. data.cy = global_hook_info->cy;
  386. } else {
  387. data.cx = pp.BackBufferWidth;
  388. data.cy = pp.BackBufferHeight;
  389. }
  390. return true;
  391. }
  392. static void d3d9_init(IDirect3DDevice9 *device)
  393. {
  394. IDirect3DDevice9Ex *d3d9ex = nullptr;
  395. bool has_d3d9ex_bool_offset =
  396. global_hook_info->offsets.d3d9.d3d9_clsoff &&
  397. global_hook_info->offsets.d3d9.is_d3d9ex_clsoff;
  398. bool success;
  399. uint32_t cx = 0;
  400. uint32_t cy = 0;
  401. HWND window = nullptr;
  402. HRESULT hr;
  403. data.d3d9 = get_system_module("d3d9.dll");
  404. data.device = device;
  405. hr = device->QueryInterface(__uuidof(IDirect3DDevice9Ex),
  406. (void**)&d3d9ex);
  407. if (SUCCEEDED(hr)) {
  408. d3d9ex->Release();
  409. data.patch = -1;
  410. } else if (!has_d3d9ex_bool_offset) {
  411. data.patch = get_d3d9_patch(data.d3d9);
  412. } else {
  413. data.patch = -1;
  414. }
  415. if (!d3d9_init_format_backbuffer(cx, cy, window)) {
  416. if (!d3d9_init_format_swapchain(cx, cy, window)) {
  417. return;
  418. }
  419. }
  420. if (global_hook_info->force_shmem ||
  421. (!d3d9ex && data.patch == -1 && !has_d3d9ex_bool_offset)) {
  422. success = d3d9_shmem_init(cx, cy, window);
  423. } else {
  424. success = d3d9_shtex_init(cx, cy, window);
  425. }
  426. if (!success)
  427. d3d9_free();
  428. }
  429. static inline HRESULT get_backbuffer(IDirect3DDevice9 *device,
  430. IDirect3DSurface9 **surface)
  431. {
  432. static bool use_backbuffer = false;
  433. static bool checked_exceptions = false;
  434. if (!checked_exceptions) {
  435. if (_strcmpi(get_process_name(), "hotd_ng.exe") == 0)
  436. use_backbuffer = true;
  437. checked_exceptions = true;
  438. }
  439. if (use_backbuffer) {
  440. return device->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO,
  441. surface);
  442. } else {
  443. return device->GetRenderTarget(0, surface);
  444. }
  445. }
  446. static inline void d3d9_shtex_capture(IDirect3DSurface9 *backbuffer)
  447. {
  448. D3DTEXTUREFILTERTYPE filter;
  449. HRESULT hr;
  450. filter = data.using_scale ? D3DTEXF_LINEAR : D3DTEXF_NONE;
  451. hr = data.device->StretchRect(backbuffer, nullptr, data.d3d9_copytex,
  452. nullptr, filter);
  453. if (FAILED(hr))
  454. hlog_hr("d3d9_shtex_capture: StretchRect failed", hr);
  455. }
  456. static inline void d3d9_shmem_capture_queue_copy()
  457. {
  458. for (int i = 0; i < NUM_BUFFERS; i++) {
  459. IDirect3DSurface9 *target = data.copy_surfaces[i];
  460. D3DLOCKED_RECT rect;
  461. HRESULT hr;
  462. if (!data.issued_queries[i]) {
  463. continue;
  464. }
  465. if (data.queries[i]->GetData(0, 0, 0) != S_OK) {
  466. continue;
  467. }
  468. data.issued_queries[i] = false;
  469. hr = target->LockRect(&rect, nullptr, D3DLOCK_READONLY);
  470. if (SUCCEEDED(hr)) {
  471. data.texture_mapped[i] = true;
  472. shmem_copy_data(i, rect.pBits);
  473. }
  474. break;
  475. }
  476. }
  477. static inline void d3d9_shmem_capture(IDirect3DSurface9 *backbuffer)
  478. {
  479. D3DTEXTUREFILTERTYPE filter;
  480. IDirect3DSurface9 *copy;
  481. int next_tex;
  482. HRESULT hr;
  483. d3d9_shmem_capture_queue_copy();
  484. next_tex = (data.cur_tex == NUM_BUFFERS - 1) ? 0 : data.cur_tex + 1;
  485. filter = data.using_scale ? D3DTEXF_LINEAR : D3DTEXF_NONE;
  486. copy = data.render_targets[data.cur_tex];
  487. hr = data.device->StretchRect(backbuffer, nullptr, copy, nullptr,
  488. filter);
  489. if (FAILED(hr)) {
  490. hlog_hr("d3d9_shmem_capture: StretchRect failed", hr);
  491. return;
  492. }
  493. if (data.copy_wait < NUM_BUFFERS - 1) {
  494. data.copy_wait++;
  495. } else {
  496. IDirect3DSurface9 *src = data.render_targets[next_tex];
  497. IDirect3DSurface9 *dst = data.copy_surfaces[next_tex];
  498. if (shmem_texture_data_lock(next_tex)) {
  499. dst->UnlockRect();
  500. data.texture_mapped[next_tex] = false;
  501. shmem_texture_data_unlock(next_tex);
  502. }
  503. hr = data.device->GetRenderTargetData(src, dst);
  504. if (FAILED(hr)) {
  505. hlog_hr("d3d9_shmem_capture: GetRenderTargetData "
  506. "failed", hr);
  507. }
  508. data.queries[next_tex]->Issue(D3DISSUE_END);
  509. data.issued_queries[next_tex] = true;
  510. }
  511. data.cur_tex = next_tex;
  512. }
  513. static void d3d9_capture(IDirect3DDevice9 *device,
  514. IDirect3DSurface9 *backbuffer)
  515. {
  516. if (capture_should_stop()) {
  517. d3d9_free();
  518. }
  519. if (capture_should_init()) {
  520. d3d9_init(device);
  521. }
  522. if (capture_ready()) {
  523. if (data.device != device) {
  524. d3d9_free();
  525. return;
  526. }
  527. if (data.using_shtex)
  528. d3d9_shtex_capture(backbuffer);
  529. else
  530. d3d9_shmem_capture(backbuffer);
  531. }
  532. }
  533. /* this is used just in case Present calls PresentEx or vise versa. */
  534. static int present_recurse = 0;
  535. static inline void present_begin(IDirect3DDevice9 *device,
  536. IDirect3DSurface9 *&backbuffer)
  537. {
  538. HRESULT hr;
  539. if (!present_recurse) {
  540. hr = get_backbuffer(device, &backbuffer);
  541. if (FAILED(hr)) {
  542. hlog_hr("d3d9_shmem_capture: Failed to get "
  543. "backbuffer", hr);
  544. }
  545. if (!global_hook_info->capture_overlay) {
  546. d3d9_capture(device, backbuffer);
  547. }
  548. }
  549. present_recurse++;
  550. }
  551. static inline void present_end(IDirect3DDevice9 *device,
  552. IDirect3DSurface9 *backbuffer)
  553. {
  554. present_recurse--;
  555. if (!present_recurse) {
  556. if (global_hook_info->capture_overlay) {
  557. if (!present_recurse)
  558. d3d9_capture(device, backbuffer);
  559. }
  560. if (backbuffer)
  561. backbuffer->Release();
  562. }
  563. }
  564. static bool hooked_reset = false;
  565. static void setup_reset_hooks(IDirect3DDevice9 *device);
  566. static HRESULT STDMETHODCALLTYPE hook_present(IDirect3DDevice9 *device,
  567. CONST RECT *src_rect, CONST RECT *dst_rect,
  568. HWND override_window, CONST RGNDATA *dirty_region)
  569. {
  570. IDirect3DSurface9 *backbuffer = nullptr;
  571. HRESULT hr;
  572. if (!hooked_reset)
  573. setup_reset_hooks(device);
  574. present_begin(device, backbuffer);
  575. unhook(&present);
  576. present_t call = (present_t)present.call_addr;
  577. hr = call(device, src_rect, dst_rect, override_window, dirty_region);
  578. rehook(&present);
  579. present_end(device, backbuffer);
  580. return hr;
  581. }
  582. static HRESULT STDMETHODCALLTYPE hook_present_ex(IDirect3DDevice9 *device,
  583. CONST RECT *src_rect, CONST RECT *dst_rect,
  584. HWND override_window, CONST RGNDATA *dirty_region, DWORD flags)
  585. {
  586. IDirect3DSurface9 *backbuffer = nullptr;
  587. HRESULT hr;
  588. if (!hooked_reset)
  589. setup_reset_hooks(device);
  590. present_begin(device, backbuffer);
  591. unhook(&present_ex);
  592. present_ex_t call = (present_ex_t)present_ex.call_addr;
  593. hr = call(device, src_rect, dst_rect, override_window, dirty_region,
  594. flags);
  595. rehook(&present_ex);
  596. present_end(device, backbuffer);
  597. return hr;
  598. }
  599. static HRESULT STDMETHODCALLTYPE hook_present_swap(IDirect3DSwapChain9 *swap,
  600. CONST RECT *src_rect, CONST RECT *dst_rect,
  601. HWND override_window, CONST RGNDATA *dirty_region, DWORD flags)
  602. {
  603. IDirect3DSurface9 *backbuffer = nullptr;
  604. IDirect3DDevice9 *device = nullptr;
  605. HRESULT hr;
  606. if (!present_recurse) {
  607. hr = swap->GetDevice(&device);
  608. if (SUCCEEDED(hr)) {
  609. device->Release();
  610. }
  611. }
  612. if (device) {
  613. if (!hooked_reset)
  614. setup_reset_hooks(device);
  615. present_begin(device, backbuffer);
  616. }
  617. unhook(&present_swap);
  618. present_swap_t call = (present_swap_t)present_swap.call_addr;
  619. hr = call(swap, src_rect, dst_rect, override_window, dirty_region,
  620. flags);
  621. rehook(&present_swap);
  622. if (device)
  623. present_end(device, backbuffer);
  624. return hr;
  625. }
  626. static HRESULT STDMETHODCALLTYPE hook_reset(IDirect3DDevice9 *device,
  627. D3DPRESENT_PARAMETERS *params)
  628. {
  629. HRESULT hr;
  630. if (capture_active())
  631. d3d9_free();
  632. unhook(&reset);
  633. reset_t call = (reset_t)reset.call_addr;
  634. hr = call(device, params);
  635. rehook(&reset);
  636. return hr;
  637. }
  638. static HRESULT STDMETHODCALLTYPE hook_reset_ex(IDirect3DDevice9 *device,
  639. D3DPRESENT_PARAMETERS *params, D3DDISPLAYMODEEX *dmex)
  640. {
  641. HRESULT hr;
  642. if (capture_active())
  643. d3d9_free();
  644. unhook(&reset_ex);
  645. reset_ex_t call = (reset_ex_t)reset_ex.call_addr;
  646. hr = call(device, params, dmex);
  647. rehook(&reset_ex);
  648. return hr;
  649. }
  650. static void setup_reset_hooks(IDirect3DDevice9 *device)
  651. {
  652. IDirect3DDevice9Ex *d3d9ex = nullptr;
  653. uintptr_t *vtable = *(uintptr_t**)device;
  654. HRESULT hr;
  655. hook_init(&reset, (void*)vtable[16], (void*)hook_reset,
  656. "IDirect3DDevice9::Reset");
  657. rehook(&reset);
  658. hr = device->QueryInterface(__uuidof(IDirect3DDevice9Ex),
  659. (void**)&d3d9ex);
  660. if (SUCCEEDED(hr)) {
  661. hook_init(&reset_ex, (void*)vtable[132], (void*)hook_reset_ex,
  662. "IDirect3DDevice9Ex::ResetEx");
  663. rehook(&reset_ex);
  664. d3d9ex->Release();
  665. }
  666. hooked_reset = true;
  667. }
  668. typedef HRESULT (WINAPI *d3d9create_ex_t)(UINT, IDirect3D9Ex**);
  669. static bool manually_get_d3d9_addrs(HMODULE d3d9_module,
  670. void **present_addr,
  671. void **present_ex_addr,
  672. void **present_swap_addr)
  673. {
  674. d3d9create_ex_t create_ex;
  675. D3DPRESENT_PARAMETERS pp;
  676. HRESULT hr;
  677. IDirect3DDevice9Ex *device;
  678. IDirect3D9Ex *d3d9ex;
  679. hlog("D3D9 values invalid, manually obtaining");
  680. create_ex = (d3d9create_ex_t)GetProcAddress(d3d9_module,
  681. "Direct3DCreate9Ex");
  682. if (!create_ex) {
  683. hlog("Failed to load Direct3DCreate9Ex");
  684. return false;
  685. }
  686. if (FAILED(create_ex(D3D_SDK_VERSION, &d3d9ex))) {
  687. hlog("Failed to create D3D9 context");
  688. return false;
  689. }
  690. memset(&pp, 0, sizeof(pp));
  691. pp.Windowed = 1;
  692. pp.SwapEffect = D3DSWAPEFFECT_FLIP;
  693. pp.BackBufferFormat = D3DFMT_A8R8G8B8;
  694. pp.BackBufferCount = 1;
  695. pp.hDeviceWindow = (HWND)dummy_window;
  696. pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
  697. hr = d3d9ex->CreateDeviceEx(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
  698. dummy_window,
  699. D3DCREATE_HARDWARE_VERTEXPROCESSING |
  700. D3DCREATE_NOWINDOWCHANGES, &pp, NULL, &device);
  701. d3d9ex->Release();
  702. if (SUCCEEDED(hr)) {
  703. uintptr_t *vtable = *(uintptr_t**)device;
  704. IDirect3DSwapChain9 *swap;
  705. *present_addr = (void*)vtable[17];
  706. *present_ex_addr = (void*)vtable[121];
  707. hr = device->GetSwapChain(0, &swap);
  708. if (SUCCEEDED(hr)) {
  709. vtable = *(uintptr_t**)swap;
  710. *present_swap_addr = (void*)vtable[3];
  711. swap->Release();
  712. }
  713. device->Release();
  714. } else {
  715. hlog("Failed to create D3D9 device");
  716. return false;
  717. }
  718. return true;
  719. }
  720. bool hook_d3d9(void)
  721. {
  722. HMODULE d3d9_module = get_system_module("d3d9.dll");
  723. uint32_t d3d9_size;
  724. void *present_addr = nullptr;
  725. void *present_ex_addr = nullptr;
  726. void *present_swap_addr = nullptr;
  727. if (!d3d9_module) {
  728. return false;
  729. }
  730. d3d9_size = module_size(d3d9_module);
  731. if (global_hook_info->offsets.d3d9.present < d3d9_size &&
  732. global_hook_info->offsets.d3d9.present_ex < d3d9_size &&
  733. global_hook_info->offsets.d3d9.present_swap < d3d9_size) {
  734. present_addr = get_offset_addr(d3d9_module,
  735. global_hook_info->offsets.d3d9.present);
  736. present_ex_addr = get_offset_addr(d3d9_module,
  737. global_hook_info->offsets.d3d9.present_ex);
  738. present_swap_addr = get_offset_addr(d3d9_module,
  739. global_hook_info->offsets.d3d9.present_swap);
  740. } else {
  741. if (!dummy_window) {
  742. return false;
  743. }
  744. if (!manually_get_d3d9_addrs(d3d9_module,
  745. &present_addr,
  746. &present_ex_addr,
  747. &present_swap_addr)) {
  748. hlog("Failed to get D3D9 values");
  749. return true;
  750. }
  751. }
  752. if (!present_addr && !present_ex_addr && !present_swap_addr) {
  753. hlog("Invalid D3D9 values");
  754. return true;
  755. }
  756. if (present_swap_addr) {
  757. hook_init(&present_swap, present_swap_addr,
  758. (void*)hook_present_swap,
  759. "IDirect3DSwapChain9::Present");
  760. rehook(&present_swap);
  761. }
  762. if (present_ex_addr) {
  763. hook_init(&present_ex, present_ex_addr,
  764. (void*)hook_present_ex,
  765. "IDirect3DDevice9Ex::PresentEx");
  766. rehook(&present_ex);
  767. }
  768. if (present_addr) {
  769. hook_init(&present, present_addr,
  770. (void*)hook_present,
  771. "IDirect3DDevice9::Present");
  772. rehook(&present);
  773. }
  774. hlog("Hooked D3D9");
  775. return true;
  776. }