d3d8-capture.cpp 8.4 KB

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