d3d9-capture.cpp 22 KB

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