d3d8-capture.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. #include <dxgi.h>
  2. #include "../d3d8-api/d3d8.h"
  3. #include "graphics-hook.h"
  4. #include <detours.h>
  5. typedef HRESULT(STDMETHODCALLTYPE *reset_t)(IDirect3DDevice8 *,
  6. D3DPRESENT_PARAMETERS *);
  7. typedef HRESULT(STDMETHODCALLTYPE *present_t)(IDirect3DDevice8 *, CONST RECT *,
  8. CONST RECT *, HWND,
  9. CONST RGNDATA *);
  10. reset_t RealReset = NULL;
  11. present_t RealPresent = NULL;
  12. struct d3d8_data {
  13. HMODULE d3d8;
  14. uint32_t cx;
  15. uint32_t cy;
  16. D3DFORMAT d3d8_format;
  17. DXGI_FORMAT dxgi_format;
  18. struct shmem_data *shmem_info;
  19. HWND window;
  20. uint32_t pitch;
  21. IDirect3DSurface8 *copy_surfaces[NUM_BUFFERS];
  22. bool texture_ready[NUM_BUFFERS];
  23. bool surface_locked[NUM_BUFFERS];
  24. int cur_surface;
  25. int copy_wait;
  26. };
  27. static d3d8_data data = {};
  28. static DXGI_FORMAT d3d8_to_dxgi_format(D3DFORMAT format)
  29. {
  30. switch ((unsigned long)format) {
  31. case D3DFMT_X1R5G5B5:
  32. case D3DFMT_A1R5G5B5:
  33. return DXGI_FORMAT_B5G5R5A1_UNORM;
  34. case D3DFMT_R5G6B5:
  35. return DXGI_FORMAT_B5G6R5_UNORM;
  36. case D3DFMT_A8R8G8B8:
  37. return DXGI_FORMAT_B8G8R8A8_UNORM;
  38. case D3DFMT_X8R8G8B8:
  39. return DXGI_FORMAT_B8G8R8X8_UNORM;
  40. }
  41. return DXGI_FORMAT_UNKNOWN;
  42. }
  43. static IDirect3DSurface8 *d3d8_get_backbuffer(IDirect3DDevice8 *device)
  44. {
  45. IDirect3DSurface8 *backbuffer;
  46. HRESULT hr;
  47. hr = device->GetRenderTarget(&backbuffer);
  48. if (FAILED(hr)) {
  49. hlog_hr("d3d8_get_backbuffer: Failed to get backbuffer", hr);
  50. backbuffer = nullptr;
  51. }
  52. return backbuffer;
  53. }
  54. static bool d3d8_get_window_handle(IDirect3DDevice8 *device)
  55. {
  56. D3DDEVICE_CREATION_PARAMETERS parameters;
  57. HRESULT hr;
  58. hr = device->GetCreationParameters(&parameters);
  59. if (FAILED(hr)) {
  60. hlog_hr("d3d8_get_window_handle: Failed to get "
  61. "device creation parameters",
  62. hr);
  63. return false;
  64. }
  65. data.window = parameters.hFocusWindow;
  66. return true;
  67. }
  68. static bool d3d8_init_format_backbuffer(IDirect3DDevice8 *device)
  69. {
  70. IDirect3DSurface8 *backbuffer;
  71. D3DSURFACE_DESC desc;
  72. HRESULT hr;
  73. if (!d3d8_get_window_handle(device))
  74. return false;
  75. backbuffer = d3d8_get_backbuffer(device);
  76. if (!backbuffer)
  77. return false;
  78. hr = backbuffer->GetDesc(&desc);
  79. backbuffer->Release();
  80. if (FAILED(hr)) {
  81. hlog_hr("d3d8_init_format_backbuffer: Failed to get "
  82. "backbuffer descriptor",
  83. hr);
  84. return false;
  85. }
  86. data.d3d8_format = desc.Format;
  87. data.dxgi_format = d3d8_to_dxgi_format(desc.Format);
  88. data.cx = desc.Width;
  89. data.cy = desc.Height;
  90. return true;
  91. }
  92. static bool d3d8_shmem_init_buffer(IDirect3DDevice8 *device, int idx)
  93. {
  94. HRESULT hr;
  95. hr = device->CreateImageSurface(data.cx, data.cy, data.d3d8_format,
  96. &data.copy_surfaces[idx]);
  97. if (FAILED(hr)) {
  98. hlog_hr("d3d8_shmem_init_buffer: Failed to create surface", hr);
  99. return false;
  100. }
  101. if (idx == 0) {
  102. D3DLOCKED_RECT rect;
  103. hr = data.copy_surfaces[0]->LockRect(&rect, nullptr,
  104. D3DLOCK_READONLY);
  105. if (FAILED(hr)) {
  106. hlog_hr("d3d8_shmem_init_buffer: Failed to lock buffer",
  107. hr);
  108. return false;
  109. }
  110. data.pitch = rect.Pitch;
  111. data.copy_surfaces[0]->UnlockRect();
  112. }
  113. return true;
  114. }
  115. static bool d3d8_shmem_init(IDirect3DDevice8 *device)
  116. {
  117. for (int i = 0; i < NUM_BUFFERS; i++) {
  118. if (!d3d8_shmem_init_buffer(device, i)) {
  119. return false;
  120. }
  121. }
  122. if (!capture_init_shmem(&data.shmem_info, data.window, data.cx, data.cy,
  123. data.pitch, data.dxgi_format, false)) {
  124. return false;
  125. }
  126. hlog("d3d8 memory capture successful");
  127. return true;
  128. }
  129. static void d3d8_free()
  130. {
  131. capture_free();
  132. for (size_t i = 0; i < NUM_BUFFERS; i++) {
  133. if (data.copy_surfaces[i]) {
  134. if (data.surface_locked[i])
  135. data.copy_surfaces[i]->UnlockRect();
  136. data.copy_surfaces[i]->Release();
  137. }
  138. }
  139. memset(&data, 0, sizeof(data));
  140. hlog("----------------- d3d8 capture freed -----------------");
  141. }
  142. static void d3d8_init(IDirect3DDevice8 *device)
  143. {
  144. data.d3d8 = get_system_module("d3d8.dll");
  145. if (!d3d8_init_format_backbuffer(device))
  146. return;
  147. if (!d3d8_shmem_init(device))
  148. d3d8_free();
  149. }
  150. static void d3d8_shmem_capture_copy(int idx)
  151. {
  152. D3DLOCKED_RECT rect;
  153. HRESULT hr;
  154. if (data.texture_ready[idx]) {
  155. data.texture_ready[idx] = false;
  156. IDirect3DSurface8 *target = data.copy_surfaces[idx];
  157. hr = target->LockRect(&rect, nullptr, D3DLOCK_READONLY);
  158. if (SUCCEEDED(hr)) {
  159. data.surface_locked[idx] = true;
  160. shmem_copy_data(idx, rect.pBits);
  161. }
  162. }
  163. }
  164. static void d3d8_shmem_capture(IDirect3DDevice8 *device,
  165. IDirect3DSurface8 *backbuffer)
  166. {
  167. int next_surface;
  168. HRESULT hr;
  169. next_surface = (data.cur_surface + 1) % NUM_BUFFERS;
  170. d3d8_shmem_capture_copy(next_surface);
  171. if (data.copy_wait < NUM_BUFFERS - 1) {
  172. data.copy_wait++;
  173. } else {
  174. IDirect3DSurface8 *src = backbuffer;
  175. IDirect3DSurface8 *dst = data.copy_surfaces[data.cur_surface];
  176. if (shmem_texture_data_lock(data.cur_surface)) {
  177. dst->UnlockRect();
  178. data.surface_locked[data.cur_surface] = false;
  179. shmem_texture_data_unlock(data.cur_surface);
  180. }
  181. hr = device->CopyRects(src, nullptr, 0, dst, nullptr);
  182. if (FAILED(hr)) {
  183. hlog_hr("d3d8_shmem_capture: CopyRects "
  184. "failed",
  185. hr);
  186. }
  187. data.texture_ready[data.cur_surface] = true;
  188. }
  189. data.cur_surface = next_surface;
  190. }
  191. static void d3d8_capture(IDirect3DDevice8 *device,
  192. IDirect3DSurface8 *backbuffer)
  193. {
  194. if (capture_should_stop()) {
  195. d3d8_free();
  196. }
  197. if (capture_should_init()) {
  198. d3d8_init(device);
  199. }
  200. if (capture_ready()) {
  201. d3d8_shmem_capture(device, backbuffer);
  202. }
  203. }
  204. static HRESULT STDMETHODCALLTYPE hook_reset(IDirect3DDevice8 *device,
  205. D3DPRESENT_PARAMETERS *parameters)
  206. {
  207. if (capture_active())
  208. d3d8_free();
  209. return RealReset(device, parameters);
  210. }
  211. static bool hooked_reset = false;
  212. static void setup_reset_hooks(IDirect3DDevice8 *device)
  213. {
  214. uintptr_t *vtable = *(uintptr_t **)device;
  215. DetourTransactionBegin();
  216. RealReset = (reset_t)vtable[14];
  217. DetourAttach((PVOID *)&RealReset, hook_reset);
  218. const LONG error = DetourTransactionCommit();
  219. const bool success = error == NO_ERROR;
  220. if (success) {
  221. hlog("Hooked IDirect3DDevice8::Reset");
  222. hooked_reset = true;
  223. } else {
  224. RealReset = nullptr;
  225. }
  226. }
  227. static HRESULT STDMETHODCALLTYPE hook_present(IDirect3DDevice8 *device,
  228. CONST RECT *src_rect,
  229. CONST RECT *dst_rect,
  230. HWND override_window,
  231. CONST RGNDATA *dirty_region)
  232. {
  233. IDirect3DSurface8 *backbuffer;
  234. if (!hooked_reset)
  235. setup_reset_hooks(device);
  236. backbuffer = d3d8_get_backbuffer(device);
  237. if (backbuffer) {
  238. d3d8_capture(device, backbuffer);
  239. backbuffer->Release();
  240. }
  241. return RealPresent(device, src_rect, dst_rect, override_window,
  242. dirty_region);
  243. }
  244. typedef IDirect3D8 *(WINAPI *d3d8create_t)(UINT);
  245. static bool manually_get_d3d8_present_addr(HMODULE d3d8_module,
  246. void **present_addr)
  247. {
  248. d3d8create_t create;
  249. D3DPRESENT_PARAMETERS pp;
  250. HRESULT hr;
  251. IDirect3DDevice8 *device;
  252. IDirect3D8 *d3d8;
  253. hlog("D3D8 value invalid, manually obtaining");
  254. create = (d3d8create_t)GetProcAddress(d3d8_module, "Direct3DCreate8");
  255. if (!create) {
  256. hlog("Failed to load Direct3DCreate8");
  257. return false;
  258. }
  259. d3d8 = create(D3D_SDK_VERSION);
  260. if (!d3d8) {
  261. hlog("Failed to create D3D8 context");
  262. return false;
  263. }
  264. memset(&pp, 0, sizeof(pp));
  265. pp.Windowed = true;
  266. pp.SwapEffect = D3DSWAPEFFECT_FLIP;
  267. pp.BackBufferFormat = D3DFMT_A8R8G8B8;
  268. pp.BackBufferWidth = 2;
  269. pp.BackBufferHeight = 2;
  270. pp.BackBufferCount = 1;
  271. pp.hDeviceWindow = dummy_window;
  272. hr = d3d8->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
  273. dummy_window,
  274. D3DCREATE_HARDWARE_VERTEXPROCESSING, &pp,
  275. &device);
  276. d3d8->Release();
  277. if (SUCCEEDED(hr)) {
  278. uintptr_t *vtable = *(uintptr_t **)device;
  279. *present_addr = (void *)vtable[15];
  280. device->Release();
  281. } else {
  282. hlog("Failed to create D3D8 device");
  283. return false;
  284. }
  285. return true;
  286. }
  287. bool hook_d3d8(void)
  288. {
  289. HMODULE d3d8_module = get_system_module("d3d8.dll");
  290. uint32_t d3d8_size;
  291. void *present_addr = nullptr;
  292. if (!d3d8_module) {
  293. return false;
  294. }
  295. d3d8_size = module_size(d3d8_module);
  296. if (global_hook_info->offsets.d3d8.present < d3d8_size) {
  297. present_addr = get_offset_addr(
  298. d3d8_module, global_hook_info->offsets.d3d8.present);
  299. } else {
  300. if (!dummy_window) {
  301. return false;
  302. }
  303. if (!manually_get_d3d8_present_addr(d3d8_module,
  304. &present_addr)) {
  305. hlog("Failed to get D3D8 value");
  306. return true;
  307. }
  308. }
  309. if (!present_addr) {
  310. hlog("Invalid D3D8 value");
  311. return true;
  312. }
  313. DetourTransactionBegin();
  314. RealPresent = (present_t)present_addr;
  315. DetourAttach((PVOID *)&RealPresent, hook_present);
  316. const LONG error = DetourTransactionCommit();
  317. const bool success = error == NO_ERROR;
  318. if (success) {
  319. hlog("Hooked IDirect3DDevice8::Present");
  320. hlog("Hooked D3D8");
  321. } else {
  322. RealPresent = nullptr;
  323. hlog("Failed to attach Detours hook: %ld", error);
  324. }
  325. return success;
  326. }