d3d8-capture.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include <dxgi.h>
  3. #include "../d3d8-api/d3d8.h"
  4. #include "graphics-hook.h"
  5. #include "../funchook.h"
  6. typedef HRESULT(STDMETHODCALLTYPE *reset_t)(IDirect3DDevice8*,
  7. D3DPRESENT_PARAMETERS*);
  8. typedef HRESULT(STDMETHODCALLTYPE *present_t)(IDirect3DDevice8*,
  9. CONST RECT*, CONST RECT*, HWND, CONST RGNDATA*);
  10. static struct func_hook present;
  11. static struct func_hook reset;
  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 surface_locked[NUM_BUFFERS];
  23. int cur_surface;
  24. int copy_wait;
  25. };
  26. static d3d8_data data = {};
  27. static DXGI_FORMAT d3d8_to_dxgi_format(D3DFORMAT format)
  28. {
  29. switch ((unsigned long)format) {
  30. case D3DFMT_X1R5G5B5:
  31. case D3DFMT_A1R5G5B5: return DXGI_FORMAT_B5G5R5A1_UNORM;
  32. case D3DFMT_R5G6B5: return DXGI_FORMAT_B5G6R5_UNORM;
  33. case D3DFMT_A8R8G8B8: return DXGI_FORMAT_B8G8R8A8_UNORM;
  34. case D3DFMT_X8R8G8B8: return DXGI_FORMAT_B8G8R8X8_UNORM;
  35. }
  36. return DXGI_FORMAT_UNKNOWN;
  37. }
  38. static IDirect3DSurface8 *d3d8_get_backbuffer(IDirect3DDevice8 *device)
  39. {
  40. IDirect3DSurface8 *backbuffer;
  41. HRESULT hr;
  42. hr = device->GetRenderTarget(&backbuffer);
  43. if (FAILED(hr)) {
  44. hlog_hr("d3d8_get_backbuffer: Failed to get backbuffer", hr);
  45. backbuffer = nullptr;
  46. }
  47. return backbuffer;
  48. }
  49. static bool d3d8_get_window_handle(IDirect3DDevice8 *device)
  50. {
  51. D3DDEVICE_CREATION_PARAMETERS parameters;
  52. HRESULT hr;
  53. hr = device->GetCreationParameters(&parameters);
  54. if (FAILED(hr)) {
  55. hlog_hr("d3d8_get_window_handle: Failed to get "
  56. "device creation parameters", hr);
  57. return false;
  58. }
  59. data.window = parameters.hFocusWindow;
  60. return true;
  61. }
  62. static bool d3d8_init_format_backbuffer(IDirect3DDevice8 *device)
  63. {
  64. IDirect3DSurface8 *backbuffer;
  65. D3DSURFACE_DESC desc;
  66. HRESULT hr;
  67. if (!d3d8_get_window_handle(device))
  68. return false;
  69. backbuffer = d3d8_get_backbuffer(device);
  70. if (!backbuffer)
  71. return false;
  72. hr = backbuffer->GetDesc(&desc);
  73. backbuffer->Release();
  74. if (FAILED(hr)) {
  75. hlog_hr("d3d8_init_format_backbuffer: Failed to get "
  76. "backbuffer descriptor", hr);
  77. return false;
  78. }
  79. data.d3d8_format = desc.Format;
  80. data.dxgi_format = d3d8_to_dxgi_format(desc.Format);
  81. data.cx = desc.Width;
  82. data.cy = desc.Height;
  83. return true;
  84. }
  85. static bool d3d8_shmem_init_buffer(IDirect3DDevice8 *device, int idx)
  86. {
  87. HRESULT hr;
  88. hr = device->CreateImageSurface(data.cx, data.cy,
  89. data.d3d8_format, &data.copy_surfaces[idx]);
  90. if (FAILED(hr)) {
  91. hlog_hr("d3d8_shmem_init_buffer: Failed to create surface", hr);
  92. return false;
  93. }
  94. if (idx == 0) {
  95. D3DLOCKED_RECT rect;
  96. hr = data.copy_surfaces[0]->LockRect(&rect, nullptr,
  97. D3DLOCK_READONLY);
  98. if (FAILED(hr)) {
  99. hlog_hr("d3d8_shmem_init_buffer: Failed to lock buffer", hr);
  100. return false;
  101. }
  102. data.pitch = rect.Pitch;
  103. data.copy_surfaces[0]->UnlockRect();
  104. }
  105. return true;
  106. }
  107. static bool d3d8_shmem_init(IDirect3DDevice8 *device)
  108. {
  109. for (int i = 0; i < NUM_BUFFERS; i++) {
  110. if (!d3d8_shmem_init_buffer(device, i)) {
  111. return false;
  112. }
  113. }
  114. if (!capture_init_shmem(&data.shmem_info, data.window, data.cx, data.cy,
  115. data.cx, data.cy, data.pitch, data.dxgi_format,
  116. false)) {
  117. return false;
  118. }
  119. hlog("d3d8 memory capture successfull");
  120. return true;
  121. }
  122. static void d3d8_free()
  123. {
  124. capture_free();
  125. for (size_t i = 0; i < NUM_BUFFERS; i++) {
  126. if (data.copy_surfaces[i]) {
  127. if (data.surface_locked[i])
  128. data.copy_surfaces[i]->UnlockRect();
  129. data.copy_surfaces[i]->Release();
  130. }
  131. }
  132. memset(&data, 0, sizeof(data));
  133. hlog("----------------- d3d8 capture freed -----------------");
  134. }
  135. static void d3d8_init(IDirect3DDevice8 *device)
  136. {
  137. data.d3d8 = get_system_module("d3d8.dll");
  138. if (!d3d8_init_format_backbuffer(device))
  139. return;
  140. if (!d3d8_shmem_init(device))
  141. d3d8_free();
  142. }
  143. static void d3d8_shmem_capture_copy(int idx)
  144. {
  145. IDirect3DSurface8 *target = data.copy_surfaces[idx];
  146. D3DLOCKED_RECT rect;
  147. HRESULT hr;
  148. if (data.surface_locked[idx])
  149. return;
  150. hr = target->LockRect(&rect, nullptr, D3DLOCK_READONLY);
  151. if (SUCCEEDED(hr)) {
  152. shmem_copy_data(idx, rect.pBits);
  153. }
  154. }
  155. static void d3d8_shmem_capture(IDirect3DDevice8 *device,
  156. IDirect3DSurface8 *backbuffer)
  157. {
  158. int cur_surface;
  159. int next_surface;
  160. HRESULT hr;
  161. cur_surface = data.cur_surface;
  162. next_surface = (cur_surface == NUM_BUFFERS - 1) ? 0 : cur_surface + 1;
  163. if (data.copy_wait < NUM_BUFFERS - 1) {
  164. data.copy_wait++;
  165. } else {
  166. IDirect3DSurface8 *src = backbuffer;
  167. IDirect3DSurface8 *dst = data.copy_surfaces[cur_surface];
  168. if (shmem_texture_data_lock(next_surface)) {
  169. dst->UnlockRect();
  170. data.surface_locked[next_surface] = false;
  171. shmem_texture_data_unlock(next_surface);
  172. }
  173. hr = device->CopyRects(src, nullptr, 0, dst, nullptr);
  174. if (SUCCEEDED(hr)) {
  175. d3d8_shmem_capture_copy(cur_surface);
  176. }
  177. }
  178. data.cur_surface = next_surface;
  179. }
  180. static void d3d8_capture(IDirect3DDevice8 *device,
  181. IDirect3DSurface8 *backbuffer)
  182. {
  183. if (capture_should_stop()) {
  184. d3d8_free();
  185. }
  186. if (capture_should_init()) {
  187. d3d8_init(device);
  188. }
  189. if (capture_ready()) {
  190. d3d8_shmem_capture(device, backbuffer);
  191. }
  192. }
  193. static HRESULT STDMETHODCALLTYPE hook_reset(IDirect3DDevice8 *device,
  194. D3DPRESENT_PARAMETERS *parameters)
  195. {
  196. HRESULT hr;
  197. if (capture_active())
  198. d3d8_free();
  199. unhook(&reset);
  200. reset_t call = (reset_t)reset.call_addr;
  201. hr = call(device, parameters);
  202. rehook(&reset);
  203. return hr;
  204. }
  205. static HRESULT STDMETHODCALLTYPE hook_present(IDirect3DDevice8 *device,
  206. CONST RECT *src_rect, CONST RECT *dst_rect,
  207. HWND override_window, CONST RGNDATA *dirty_region)
  208. {
  209. IDirect3DSurface8 *backbuffer;
  210. HRESULT hr;
  211. backbuffer = d3d8_get_backbuffer(device);
  212. if (backbuffer) {
  213. d3d8_capture(device, backbuffer);
  214. backbuffer->Release();
  215. }
  216. unhook(&present);
  217. present_t call = (present_t)present.call_addr;
  218. hr = call(device, src_rect, dst_rect, override_window, dirty_region);
  219. rehook(&present);
  220. return hr;
  221. }
  222. bool hook_d3d8(void)
  223. {
  224. HMODULE d3d8_module = get_system_module("d3d8.dll");
  225. void *present_addr;
  226. void *reset_addr;
  227. if (!d3d8_module) {
  228. return false;
  229. }
  230. present_addr = get_offset_addr(d3d8_module,
  231. global_hook_info->offsets.d3d8.present);
  232. reset_addr = get_offset_addr(d3d8_module,
  233. global_hook_info->offsets.d3d8.reset);
  234. hook_init(&present, present_addr, (void*)hook_present,
  235. "IDirect3DDevice8::Present");
  236. hook_init(&reset, reset_addr, (void*)hook_reset,
  237. "IDirect3DDevice8::Reset");
  238. rehook(&present);
  239. rehook(&reset);
  240. hlog("Hooked D3D8");
  241. return true;
  242. }