1 #include <windows.h>
2 #include <psapi.h>
3 #include <inttypes.h>
4 #include "graphics-hook.h"
5 #include "../graphics-hook-ver.h"
6 #include "../obfuscate.h"
7
8 #define DEBUG_OUTPUT
9
10 #ifdef DEBUG_OUTPUT
11 #define DbgOut(x) OutputDebugStringA(x)
12 #else
13 #define DbgOut(x)
14 #endif
15
16 struct thread_data {
17 CRITICAL_SECTION mutexes[NUM_BUFFERS];
18 CRITICAL_SECTION data_mutex;
19 void *volatile cur_data;
20 uint8_t *shmem_textures[2];
21 HANDLE copy_thread;
22 HANDLE copy_event;
23 HANDLE stop_event;
24 volatile int cur_tex;
25 unsigned int pitch;
26 unsigned int cy;
27 volatile bool locked_textures[NUM_BUFFERS];
28 };
29
30 ipc_pipe_client_t pipe = {0};
31 HANDLE signal_restart = NULL;
32 HANDLE signal_stop = NULL;
33 HANDLE signal_ready = NULL;
34 HANDLE signal_exit = NULL;
35 static HANDLE signal_init = NULL;
36 HANDLE tex_mutexes[2] = {NULL, NULL};
37 static HANDLE filemap_hook_info = NULL;
38
39 static HINSTANCE dll_inst = NULL;
40 static volatile bool stop_loop = false;
41 static HANDLE dup_hook_mutex = NULL;
42 static HANDLE capture_thread = NULL;
43 char system_path[MAX_PATH] = {0};
44 char process_name[MAX_PATH] = {0};
45 wchar_t keepalive_name[64] = {0};
46 HWND dummy_window = NULL;
47
48 static unsigned int shmem_id_counter = 0;
49 static void *shmem_info = NULL;
50 static HANDLE shmem_file_handle = 0;
51
52 static struct thread_data thread_data = {0};
53
54 volatile bool active = false;
55 struct hook_info *global_hook_info = NULL;
56
wait_for_dll_main_finish(HANDLE thread_handle)57 static inline void wait_for_dll_main_finish(HANDLE thread_handle)
58 {
59 if (thread_handle) {
60 WaitForSingleObject(thread_handle, 100);
61 CloseHandle(thread_handle);
62 }
63 }
64
init_pipe(void)65 bool init_pipe(void)
66 {
67 char new_name[64];
68 sprintf(new_name, "%s%lu", PIPE_NAME, GetCurrentProcessId());
69
70 const bool success = ipc_pipe_client_open(&pipe, new_name);
71 if (!success) {
72 DbgOut("[OBS] Failed to open pipe\n");
73 }
74
75 return success;
76 }
77
init_event(const wchar_t * name,DWORD pid)78 static HANDLE init_event(const wchar_t *name, DWORD pid)
79 {
80 HANDLE handle = create_event_plus_id(name, pid);
81 if (!handle)
82 hlog("Failed to get event '%s': %lu", name, GetLastError());
83 return handle;
84 }
85
init_mutex(const wchar_t * name,DWORD pid)86 static HANDLE init_mutex(const wchar_t *name, DWORD pid)
87 {
88 HANDLE handle = create_mutex_plus_id(name, pid);
89 if (!handle)
90 hlog("Failed to open mutex '%s': %lu", name, GetLastError());
91 return handle;
92 }
93
init_signals(void)94 static inline bool init_signals(void)
95 {
96 DWORD pid = GetCurrentProcessId();
97
98 signal_restart = init_event(EVENT_CAPTURE_RESTART, pid);
99 if (!signal_restart) {
100 return false;
101 }
102
103 signal_stop = init_event(EVENT_CAPTURE_STOP, pid);
104 if (!signal_stop) {
105 return false;
106 }
107
108 signal_ready = init_event(EVENT_HOOK_READY, pid);
109 if (!signal_ready) {
110 return false;
111 }
112
113 signal_exit = init_event(EVENT_HOOK_EXIT, pid);
114 if (!signal_exit) {
115 return false;
116 }
117
118 signal_init = init_event(EVENT_HOOK_INIT, pid);
119 if (!signal_init) {
120 return false;
121 }
122
123 return true;
124 }
125
init_mutexes(void)126 static inline bool init_mutexes(void)
127 {
128 DWORD pid = GetCurrentProcessId();
129
130 tex_mutexes[0] = init_mutex(MUTEX_TEXTURE1, pid);
131 if (!tex_mutexes[0]) {
132 return false;
133 }
134
135 tex_mutexes[1] = init_mutex(MUTEX_TEXTURE2, pid);
136 if (!tex_mutexes[1]) {
137 return false;
138 }
139
140 return true;
141 }
142
init_system_path(void)143 static inline bool init_system_path(void)
144 {
145 UINT ret = GetSystemDirectoryA(system_path, MAX_PATH);
146 if (!ret) {
147 hlog("Failed to get windows system path: %lu", GetLastError());
148 return false;
149 }
150
151 return true;
152 }
153
log_current_process(void)154 static inline void log_current_process(void)
155 {
156 DWORD len = GetModuleBaseNameA(GetCurrentProcess(), NULL, process_name,
157 MAX_PATH);
158 if (len > 0) {
159 process_name[len] = 0;
160 hlog("graphics-hook.dll loaded against process: %s",
161 process_name);
162 } else {
163 hlog("graphics-hook.dll loaded");
164 }
165
166 hlog("(half life scientist) everything.. seems to be in order");
167 }
168
init_hook_info(void)169 static inline bool init_hook_info(void)
170 {
171 filemap_hook_info = create_hook_info(GetCurrentProcessId());
172 if (!filemap_hook_info) {
173 hlog("Failed to create hook info file mapping: %lu",
174 GetLastError());
175 return false;
176 }
177
178 global_hook_info = MapViewOfFile(filemap_hook_info, FILE_MAP_ALL_ACCESS,
179 0, 0, sizeof(struct hook_info));
180 if (!global_hook_info) {
181 hlog("Failed to map the hook info file mapping: %lu",
182 GetLastError());
183 return false;
184 }
185
186 return true;
187 }
188
189 #define DEF_FLAGS (WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS)
190
dummy_window_thread(LPVOID * unused)191 static DWORD WINAPI dummy_window_thread(LPVOID *unused)
192 {
193 static const wchar_t dummy_window_class[] = L"temp_d3d_window_4039785";
194 WNDCLASSW wc;
195 MSG msg;
196
197 memset(&wc, 0, sizeof(wc));
198 wc.style = CS_OWNDC;
199 wc.hInstance = dll_inst;
200 wc.lpfnWndProc = (WNDPROC)DefWindowProc;
201 wc.lpszClassName = dummy_window_class;
202
203 if (!RegisterClass(&wc)) {
204 hlog("Failed to create temp D3D window class: %lu",
205 GetLastError());
206 return 0;
207 }
208
209 dummy_window = CreateWindowExW(0, dummy_window_class, L"Temp Window",
210 DEF_FLAGS, 0, 0, 1, 1, NULL, NULL,
211 dll_inst, NULL);
212 if (!dummy_window) {
213 hlog("Failed to create temp D3D window: %lu", GetLastError());
214 return 0;
215 }
216
217 while (GetMessage(&msg, NULL, 0, 0)) {
218 TranslateMessage(&msg);
219 DispatchMessage(&msg);
220 }
221
222 (void)unused;
223 return 0;
224 }
225
init_dummy_window_thread(void)226 static inline void init_dummy_window_thread(void)
227 {
228 HANDLE thread =
229 CreateThread(NULL, 0, dummy_window_thread, NULL, 0, NULL);
230 if (!thread) {
231 hlog("Failed to create temp D3D window thread: %lu",
232 GetLastError());
233 return;
234 }
235
236 CloseHandle(thread);
237 }
238
init_hook(HANDLE thread_handle)239 static inline bool init_hook(HANDLE thread_handle)
240 {
241 wait_for_dll_main_finish(thread_handle);
242
243 _snwprintf(keepalive_name, sizeof(keepalive_name) / sizeof(wchar_t),
244 L"%s%lu", WINDOW_HOOK_KEEPALIVE, GetCurrentProcessId());
245
246 init_pipe();
247
248 init_dummy_window_thread();
249 log_current_process();
250
251 SetEvent(signal_restart);
252 return true;
253 }
254
close_handle(HANDLE * handle)255 static inline void close_handle(HANDLE *handle)
256 {
257 if (*handle) {
258 CloseHandle(*handle);
259 *handle = NULL;
260 }
261 }
262
free_hook(void)263 static void free_hook(void)
264 {
265 if (filemap_hook_info) {
266 CloseHandle(filemap_hook_info);
267 filemap_hook_info = NULL;
268 }
269 if (global_hook_info) {
270 UnmapViewOfFile(global_hook_info);
271 global_hook_info = NULL;
272 }
273
274 close_handle(&tex_mutexes[1]);
275 close_handle(&tex_mutexes[0]);
276 close_handle(&signal_exit);
277 close_handle(&signal_ready);
278 close_handle(&signal_stop);
279 close_handle(&signal_restart);
280 close_handle(&dup_hook_mutex);
281 ipc_pipe_client_free(&pipe);
282 }
283
d3d8_hookable(void)284 static inline bool d3d8_hookable(void)
285 {
286 return !!global_hook_info->offsets.d3d8.present;
287 }
288
ddraw_hookable(void)289 static inline bool ddraw_hookable(void)
290 {
291 return !!global_hook_info->offsets.ddraw.surface_create &&
292 !!global_hook_info->offsets.ddraw.surface_restore &&
293 !!global_hook_info->offsets.ddraw.surface_release &&
294 !!global_hook_info->offsets.ddraw.surface_unlock &&
295 !!global_hook_info->offsets.ddraw.surface_blt &&
296 !!global_hook_info->offsets.ddraw.surface_flip &&
297 !!global_hook_info->offsets.ddraw.surface_set_palette &&
298 !!global_hook_info->offsets.ddraw.palette_set_entries;
299 }
300
d3d9_hookable(void)301 static inline bool d3d9_hookable(void)
302 {
303 return !!global_hook_info->offsets.d3d9.present &&
304 !!global_hook_info->offsets.d3d9.present_ex &&
305 !!global_hook_info->offsets.d3d9.present_swap;
306 }
307
dxgi_hookable(void)308 static inline bool dxgi_hookable(void)
309 {
310 return !!global_hook_info->offsets.dxgi.present &&
311 !!global_hook_info->offsets.dxgi.resize;
312 }
313
attempt_hook(void)314 static inline bool attempt_hook(void)
315 {
316 //static bool ddraw_hooked = false;
317 static bool d3d8_hooked = false;
318 static bool d3d9_hooked = false;
319 static bool d3d12_hooked = false;
320 static bool dxgi_hooked = false;
321 static bool gl_hooked = false;
322 #if COMPILE_VULKAN_HOOK
323 static bool vulkan_hooked = false;
324 if (!vulkan_hooked) {
325 vulkan_hooked = hook_vulkan();
326 if (vulkan_hooked) {
327 return true;
328 }
329 }
330 #endif //COMPILE_VULKAN_HOOK
331
332 #if COMPILE_D3D12_HOOK
333 if (!d3d12_hooked) {
334 d3d12_hooked = hook_d3d12();
335 }
336 #endif
337
338 if (!d3d9_hooked) {
339 if (!d3d9_hookable()) {
340 DbgOut("[OBS] no D3D9 hook address found!\n");
341 d3d9_hooked = true;
342 } else {
343 d3d9_hooked = hook_d3d9();
344 if (d3d9_hooked) {
345 return true;
346 }
347 }
348 }
349
350 if (!dxgi_hooked) {
351 if (!dxgi_hookable()) {
352 DbgOut("[OBS] no DXGI hook address found!\n");
353 dxgi_hooked = true;
354 } else {
355 dxgi_hooked = hook_dxgi();
356 if (dxgi_hooked) {
357 return true;
358 }
359 }
360 }
361
362 if (!gl_hooked) {
363 gl_hooked = hook_gl();
364 if (gl_hooked) {
365 return true;
366 }
367 /*} else {
368 rehook_gl();*/
369 }
370
371 if (!d3d8_hooked) {
372 if (!d3d8_hookable()) {
373 d3d8_hooked = true;
374 } else {
375 d3d8_hooked = hook_d3d8();
376 if (d3d8_hooked) {
377 return true;
378 }
379 }
380 }
381
382 /*if (!ddraw_hooked) {
383 if (!ddraw_hookable()) {
384 ddraw_hooked = true;
385 } else {
386 ddraw_hooked = hook_ddraw();
387 if (ddraw_hooked) {
388 return true;
389 }
390 }
391 }*/
392
393 #if HOOK_VERBOSE_LOGGING
394 DbgOut("[OBS] Attempt hook: D3D8=");
395 DbgOut(d3d8_hooked ? "1" : "0");
396 DbgOut(", D3D9=");
397 DbgOut(d3d9_hooked ? "1" : "0");
398 DbgOut(", D3D12=");
399 DbgOut(d3d12_hooked ? "1" : "0");
400 DbgOut(", DXGI=");
401 DbgOut(dxgi_hooked ? "1" : "0");
402 DbgOut(", GL=");
403 DbgOut(gl_hooked ? "1" : "0");
404 #if COMPILE_VULKAN_HOOK
405 DbgOut(", VK=");
406 DbgOut(vulkan_hooked ? "1" : "0");
407 #endif
408 DbgOut("\n");
409 #endif
410
411 return false;
412 }
413
capture_loop(void)414 static inline void capture_loop(void)
415 {
416 WaitForSingleObject(signal_init, INFINITE);
417
418 while (!attempt_hook())
419 Sleep(40);
420
421 for (size_t n = 0; !stop_loop; n++) {
422 /* this causes it to check every 4 seconds, but still with
423 * a small sleep interval in case the thread needs to stop */
424 if (n % 100 == 0)
425 attempt_hook();
426 Sleep(40);
427 }
428 }
429
main_capture_thread(HANDLE thread_handle)430 static DWORD WINAPI main_capture_thread(HANDLE thread_handle)
431 {
432 if (!init_hook(thread_handle)) {
433 DbgOut("[OBS] Failed to init hook\n");
434 free_hook();
435 return 0;
436 }
437
438 capture_loop();
439 return 0;
440 }
441
hlogv(const char * format,va_list args)442 static inline void hlogv(const char *format, va_list args)
443 {
444 char message[1024] = "";
445 int num = _vsprintf_p(message, 1024, format, args);
446 if (num > 0) {
447 if (!ipc_pipe_client_write(&pipe, message, (size_t)num + 1)) {
448 ipc_pipe_client_free(&pipe);
449 }
450 DbgOut("[OBS] ");
451 DbgOut(message);
452 DbgOut("\n");
453 }
454 }
455
hlog(const char * format,...)456 void hlog(const char *format, ...)
457 {
458 va_list args;
459
460 va_start(args, format);
461 hlogv(format, args);
462 va_end(args);
463 }
464
hlog_hr(const char * text,HRESULT hr)465 void hlog_hr(const char *text, HRESULT hr)
466 {
467 LPSTR buffer = NULL;
468
469 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM |
470 FORMAT_MESSAGE_ALLOCATE_BUFFER |
471 FORMAT_MESSAGE_IGNORE_INSERTS,
472 NULL, hr, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
473 (LPSTR)&buffer, 0, NULL);
474
475 if (buffer) {
476 hlog("%s (0x%08lX): %s", text, hr, buffer);
477 LocalFree(buffer);
478 } else {
479 hlog("%s (0x%08lX)", text, hr);
480 }
481 }
482
get_clockfreq(void)483 static inline uint64_t get_clockfreq(void)
484 {
485 static bool have_clockfreq = false;
486 static LARGE_INTEGER clock_freq;
487
488 if (!have_clockfreq) {
489 QueryPerformanceFrequency(&clock_freq);
490 have_clockfreq = true;
491 }
492
493 return clock_freq.QuadPart;
494 }
495
os_gettime_ns(void)496 uint64_t os_gettime_ns(void)
497 {
498 LARGE_INTEGER current_time;
499 double time_val;
500
501 QueryPerformanceCounter(¤t_time);
502 time_val = (double)current_time.QuadPart;
503 time_val *= 1000000000.0;
504 time_val /= (double)get_clockfreq();
505
506 return (uint64_t)time_val;
507 }
508
try_lock_shmem_tex(int id)509 static inline int try_lock_shmem_tex(int id)
510 {
511 int next = id == 0 ? 1 : 0;
512 DWORD wait_result = WAIT_FAILED;
513
514 wait_result = WaitForSingleObject(tex_mutexes[id], 0);
515 if (wait_result == WAIT_OBJECT_0 || wait_result == WAIT_ABANDONED) {
516 return id;
517 }
518
519 wait_result = WaitForSingleObject(tex_mutexes[next], 0);
520 if (wait_result == WAIT_OBJECT_0 || wait_result == WAIT_ABANDONED) {
521 return next;
522 }
523
524 return -1;
525 }
526
unlock_shmem_tex(int id)527 static inline void unlock_shmem_tex(int id)
528 {
529 if (id != -1) {
530 ReleaseMutex(tex_mutexes[id]);
531 }
532 }
533
init_shared_info(size_t size,HWND window)534 static inline bool init_shared_info(size_t size, HWND window)
535 {
536 wchar_t name[64];
537 HWND top = GetAncestor(window, GA_ROOT);
538
539 swprintf(name, 64, SHMEM_TEXTURE "_%" PRIu64 "_%u",
540 (uint64_t)(uintptr_t)top, ++shmem_id_counter);
541
542 shmem_file_handle = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
543 PAGE_READWRITE, 0, (DWORD)size,
544 name);
545 if (!shmem_file_handle) {
546 hlog("init_shared_info: Failed to create shared memory: %d",
547 GetLastError());
548 return false;
549 }
550
551 shmem_info = MapViewOfFile(shmem_file_handle, FILE_MAP_ALL_ACCESS, 0, 0,
552 size);
553 if (!shmem_info) {
554 hlog("init_shared_info: Failed to map shared memory: %d",
555 GetLastError());
556 return false;
557 }
558
559 return true;
560 }
561
capture_init_shtex(struct shtex_data ** data,HWND window,uint32_t cx,uint32_t cy,uint32_t format,bool flip,uintptr_t handle)562 bool capture_init_shtex(struct shtex_data **data, HWND window, uint32_t cx,
563 uint32_t cy, uint32_t format, bool flip,
564 uintptr_t handle)
565 {
566 if (!init_shared_info(sizeof(struct shtex_data), window)) {
567 hlog("capture_init_shtex: Failed to initialize memory");
568 return false;
569 }
570
571 *data = shmem_info;
572 (*data)->tex_handle = (uint32_t)handle;
573
574 global_hook_info->hook_ver_major = HOOK_VER_MAJOR;
575 global_hook_info->hook_ver_minor = HOOK_VER_MINOR;
576 global_hook_info->window = (uint32_t)(uintptr_t)window;
577 global_hook_info->type = CAPTURE_TYPE_TEXTURE;
578 global_hook_info->format = format;
579 global_hook_info->flip = flip;
580 global_hook_info->map_id = shmem_id_counter;
581 global_hook_info->map_size = sizeof(struct shtex_data);
582 global_hook_info->cx = cx;
583 global_hook_info->cy = cy;
584 global_hook_info->UNUSED_base_cx = cx;
585 global_hook_info->UNUSED_base_cy = cy;
586
587 if (!SetEvent(signal_ready)) {
588 hlog("capture_init_shtex: Failed to signal ready: %d",
589 GetLastError());
590 return false;
591 }
592
593 active = true;
594 return true;
595 }
596
copy_thread(LPVOID unused)597 static DWORD CALLBACK copy_thread(LPVOID unused)
598 {
599 uint32_t pitch = thread_data.pitch;
600 uint32_t cy = thread_data.cy;
601 HANDLE events[2] = {NULL, NULL};
602 int shmem_id = 0;
603
604 if (!duplicate_handle(&events[0], thread_data.copy_event)) {
605 hlog_hr("copy_thread: Failed to duplicate copy event: %d",
606 GetLastError());
607 return 0;
608 }
609
610 if (!duplicate_handle(&events[1], thread_data.stop_event)) {
611 hlog_hr("copy_thread: Failed to duplicate stop event: %d",
612 GetLastError());
613 goto finish;
614 }
615
616 for (;;) {
617 int copy_tex;
618 void *cur_data;
619
620 DWORD ret = WaitForMultipleObjects(2, events, false, INFINITE);
621 if (ret != WAIT_OBJECT_0) {
622 break;
623 }
624
625 EnterCriticalSection(&thread_data.data_mutex);
626 copy_tex = thread_data.cur_tex;
627 cur_data = thread_data.cur_data;
628 LeaveCriticalSection(&thread_data.data_mutex);
629
630 if (copy_tex < NUM_BUFFERS && !!cur_data) {
631 EnterCriticalSection(&thread_data.mutexes[copy_tex]);
632
633 int lock_id = try_lock_shmem_tex(shmem_id);
634 if (lock_id != -1) {
635 memcpy(thread_data.shmem_textures[lock_id],
636 cur_data, (size_t)pitch * (size_t)cy);
637
638 unlock_shmem_tex(lock_id);
639 ((struct shmem_data *)shmem_info)->last_tex =
640 lock_id;
641
642 shmem_id = lock_id == 0 ? 1 : 0;
643 }
644
645 LeaveCriticalSection(&thread_data.mutexes[copy_tex]);
646 }
647 }
648
649 finish:
650 for (size_t i = 0; i < 2; i++) {
651 if (events[i]) {
652 CloseHandle(events[i]);
653 }
654 }
655
656 (void)unused;
657 return 0;
658 }
659
shmem_copy_data(size_t idx,void * volatile data)660 void shmem_copy_data(size_t idx, void *volatile data)
661 {
662 EnterCriticalSection(&thread_data.data_mutex);
663 thread_data.cur_tex = (int)idx;
664 thread_data.cur_data = data;
665 thread_data.locked_textures[idx] = true;
666 LeaveCriticalSection(&thread_data.data_mutex);
667
668 SetEvent(thread_data.copy_event);
669 }
670
shmem_texture_data_lock(int idx)671 bool shmem_texture_data_lock(int idx)
672 {
673 bool locked;
674
675 EnterCriticalSection(&thread_data.data_mutex);
676 locked = thread_data.locked_textures[idx];
677 LeaveCriticalSection(&thread_data.data_mutex);
678
679 if (locked) {
680 EnterCriticalSection(&thread_data.mutexes[idx]);
681 return true;
682 }
683
684 return false;
685 }
686
shmem_texture_data_unlock(int idx)687 void shmem_texture_data_unlock(int idx)
688 {
689 EnterCriticalSection(&thread_data.data_mutex);
690 thread_data.locked_textures[idx] = false;
691 LeaveCriticalSection(&thread_data.data_mutex);
692
693 LeaveCriticalSection(&thread_data.mutexes[idx]);
694 }
695
init_shmem_thread(uint32_t pitch,uint32_t cy)696 static inline bool init_shmem_thread(uint32_t pitch, uint32_t cy)
697 {
698 struct shmem_data *data = shmem_info;
699
700 thread_data.pitch = pitch;
701 thread_data.cy = cy;
702 thread_data.shmem_textures[0] = (uint8_t *)data + data->tex1_offset;
703 thread_data.shmem_textures[1] = (uint8_t *)data + data->tex2_offset;
704
705 thread_data.copy_event = CreateEvent(NULL, false, false, NULL);
706 if (!thread_data.copy_event) {
707 hlog("init_shmem_thread: Failed to create copy event: %d",
708 GetLastError());
709 return false;
710 }
711
712 thread_data.stop_event = CreateEvent(NULL, true, false, NULL);
713 if (!thread_data.stop_event) {
714 hlog("init_shmem_thread: Failed to create stop event: %d",
715 GetLastError());
716 return false;
717 }
718
719 for (size_t i = 0; i < NUM_BUFFERS; i++) {
720 InitializeCriticalSection(&thread_data.mutexes[i]);
721 }
722
723 InitializeCriticalSection(&thread_data.data_mutex);
724
725 thread_data.copy_thread =
726 CreateThread(NULL, 0, copy_thread, NULL, 0, NULL);
727 if (!thread_data.copy_thread) {
728 hlog("init_shmem_thread: Failed to create thread: %d",
729 GetLastError());
730 return false;
731 }
732 return true;
733 }
734
735 #ifndef ALIGN
736 #define ALIGN(bytes, align) (((bytes) + ((align)-1)) & ~((align)-1))
737 #endif
738
capture_init_shmem(struct shmem_data ** data,HWND window,uint32_t cx,uint32_t cy,uint32_t pitch,uint32_t format,bool flip)739 bool capture_init_shmem(struct shmem_data **data, HWND window, uint32_t cx,
740 uint32_t cy, uint32_t pitch, uint32_t format, bool flip)
741 {
742 uint32_t tex_size = cy * pitch;
743 uint32_t aligned_header = ALIGN(sizeof(struct shmem_data), 32);
744 uint32_t aligned_tex = ALIGN(tex_size, 32);
745 uint32_t total_size = aligned_header + aligned_tex * 2 + 32;
746 uintptr_t align_pos;
747
748 if (!init_shared_info(total_size, window)) {
749 hlog("capture_init_shmem: Failed to initialize memory");
750 return false;
751 }
752
753 *data = shmem_info;
754
755 /* to ensure fast copy rate, align texture data to 256bit addresses */
756 align_pos = (uintptr_t)shmem_info;
757 align_pos += aligned_header;
758 align_pos &= ~(32 - 1);
759 align_pos -= (uintptr_t)shmem_info;
760
761 if (align_pos < sizeof(struct shmem_data))
762 align_pos += 32;
763
764 (*data)->last_tex = -1;
765 (*data)->tex1_offset = (uint32_t)align_pos;
766 (*data)->tex2_offset = (*data)->tex1_offset + aligned_tex;
767
768 global_hook_info->hook_ver_major = HOOK_VER_MAJOR;
769 global_hook_info->hook_ver_minor = HOOK_VER_MINOR;
770 global_hook_info->window = (uint32_t)(uintptr_t)window;
771 global_hook_info->type = CAPTURE_TYPE_MEMORY;
772 global_hook_info->format = format;
773 global_hook_info->flip = flip;
774 global_hook_info->map_id = shmem_id_counter;
775 global_hook_info->map_size = total_size;
776 global_hook_info->pitch = pitch;
777 global_hook_info->cx = cx;
778 global_hook_info->cy = cy;
779 global_hook_info->UNUSED_base_cx = cx;
780 global_hook_info->UNUSED_base_cy = cy;
781
782 if (!init_shmem_thread(pitch, cy)) {
783 return false;
784 }
785
786 if (!SetEvent(signal_ready)) {
787 hlog("capture_init_shmem: Failed to signal ready: %d",
788 GetLastError());
789 return false;
790 }
791
792 active = true;
793 return true;
794 }
795
thread_data_free(void)796 static inline void thread_data_free(void)
797 {
798 if (thread_data.copy_thread) {
799 DWORD ret;
800
801 SetEvent(thread_data.stop_event);
802 ret = WaitForSingleObject(thread_data.copy_thread, 500);
803 if (ret != WAIT_OBJECT_0)
804 TerminateThread(thread_data.copy_thread, (DWORD)-1);
805
806 CloseHandle(thread_data.copy_thread);
807 }
808 if (thread_data.stop_event)
809 CloseHandle(thread_data.stop_event);
810 if (thread_data.copy_event)
811 CloseHandle(thread_data.copy_event);
812 for (size_t i = 0; i < NUM_BUFFERS; i++)
813 DeleteCriticalSection(&thread_data.mutexes[i]);
814
815 DeleteCriticalSection(&thread_data.data_mutex);
816
817 memset(&thread_data, 0, sizeof(thread_data));
818 }
819
capture_free(void)820 void capture_free(void)
821 {
822 thread_data_free();
823
824 if (shmem_info) {
825 UnmapViewOfFile(shmem_info);
826 shmem_info = NULL;
827 }
828
829 close_handle(&shmem_file_handle);
830
831 SetEvent(signal_restart);
832 active = false;
833 }
834
835 #define HOOK_NAME L"graphics_hook_dup_mutex"
836
open_mutex_plus_id(const wchar_t * name,DWORD id)837 static inline HANDLE open_mutex_plus_id(const wchar_t *name, DWORD id)
838 {
839 wchar_t new_name[64];
840 _snwprintf(new_name, 64, L"%s%lu", name, id);
841 return open_mutex(new_name);
842 }
843
init_dll(void)844 static bool init_dll(void)
845 {
846 DWORD pid = GetCurrentProcessId();
847 HANDLE h;
848
849 h = open_mutex_plus_id(HOOK_NAME, pid);
850 if (h) {
851 CloseHandle(h);
852 return false;
853 }
854
855 dup_hook_mutex = create_mutex_plus_id(HOOK_NAME, pid);
856 return !!dup_hook_mutex;
857 }
858
DllMain(HINSTANCE hinst,DWORD reason,LPVOID unused1)859 BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, LPVOID unused1)
860 {
861 if (reason == DLL_PROCESS_ATTACH) {
862 wchar_t name[MAX_PATH];
863
864 dll_inst = hinst;
865
866 if (!init_dll()) {
867 DbgOut("[OBS] Duplicate hook library");
868 return false;
869 }
870
871 HANDLE cur_thread;
872 bool success = DuplicateHandle(GetCurrentProcess(),
873 GetCurrentThread(),
874 GetCurrentProcess(), &cur_thread,
875 SYNCHRONIZE, false, 0);
876
877 if (!success)
878 DbgOut("[OBS] Failed to get current thread handle");
879
880 if (!init_signals()) {
881 return false;
882 }
883 if (!init_system_path()) {
884 return false;
885 }
886 if (!init_hook_info()) {
887 return false;
888 }
889 if (!init_mutexes()) {
890 return false;
891 }
892
893 /* this prevents the library from being automatically unloaded
894 * by the next FreeLibrary call */
895 GetModuleFileNameW(hinst, name, MAX_PATH);
896 LoadLibraryW(name);
897
898 capture_thread = CreateThread(
899 NULL, 0, (LPTHREAD_START_ROUTINE)main_capture_thread,
900 (LPVOID)cur_thread, 0, 0);
901 if (!capture_thread) {
902 CloseHandle(cur_thread);
903 return false;
904 }
905
906 } else if (reason == DLL_PROCESS_DETACH) {
907 if (!dup_hook_mutex) {
908 return true;
909 }
910
911 if (capture_thread) {
912 stop_loop = true;
913 WaitForSingleObject(capture_thread, 300);
914 CloseHandle(capture_thread);
915 }
916
917 free_hook();
918 }
919
920 (void)unused1;
921 return true;
922 }
923
924 __declspec(dllexport) LRESULT CALLBACK
dummy_debug_proc(int code,WPARAM wparam,LPARAM lparam)925 dummy_debug_proc(int code, WPARAM wparam, LPARAM lparam)
926 {
927 static bool hooking = true;
928 MSG *msg = (MSG *)lparam;
929
930 if (hooking && msg->message == (WM_USER + 432)) {
931 HMODULE user32 = GetModuleHandleW(L"USER32");
932 BOOL(WINAPI * unhook_windows_hook_ex)(HHOOK) = NULL;
933
934 unhook_windows_hook_ex = get_obfuscated_func(
935 user32, "VojeleY`bdgxvM`hhDz", 0x7F55F80C9EE3A213ULL);
936
937 if (unhook_windows_hook_ex)
938 unhook_windows_hook_ex((HHOOK)msg->lParam);
939 hooking = false;
940 }
941
942 return CallNextHookEx(0, code, wparam, lparam);
943 }
944