From 6a241ca0aefe02959c1dfdd083746bfda6039952 Mon Sep 17 00:00:00 2001 From: FunkyFr3sh Date: Sun, 7 Nov 2021 22:42:55 +0100 Subject: [PATCH] add OBS game capture support for bnet lobby --- inc/IDirectDrawSurface.h | 4 ++ inc/ddsurface.h | 2 +- inc/wndproc.h | 1 + src/IDirectDraw/IDirectDrawSurface.c | 12 +++++ src/ddsurface.c | 78 ++++++++++++++++++++++++++-- src/render_d3d9.c | 26 ++++++++-- src/render_gdi.c | 16 +++++- src/render_ogl.c | 25 +++++++-- src/winapi_hooks.c | 2 + src/wndproc.c | 8 +++ 10 files changed, 160 insertions(+), 14 deletions(-) diff --git a/inc/IDirectDrawSurface.h b/inc/IDirectDrawSurface.h index 97aa39f..1cb624d 100644 --- a/inc/IDirectDrawSurface.h +++ b/inc/IDirectDrawSurface.h @@ -30,6 +30,10 @@ typedef struct IDirectDrawSurfaceImpl DWORD l_pitch; DWORD lx_pitch; + void* bnet_surface; + HDC bnet_dc; + HBITMAP bnet_bitmap; + PBITMAPINFO bmi; HBITMAP bitmap; HDC hdc; diff --git a/inc/ddsurface.h b/inc/ddsurface.h index f641118..b069885 100644 --- a/inc/ddsurface.h +++ b/inc/ddsurface.h @@ -33,6 +33,6 @@ HRESULT dds_Unlock(IDirectDrawSurfaceImpl* This, LPRECT lpRect); HRESULT dds_GetDDInterface(IDirectDrawSurfaceImpl* This, LPVOID* lplpDD); void* dds_GetBuffer(IDirectDrawSurfaceImpl* This); HRESULT dd_CreateSurface(IDirectDrawImpl* This, LPDDSURFACEDESC lpDDSurfaceDesc, IDirectDrawSurfaceImpl** lpDDSurface, IUnknown FAR* unkOuter); - +void dds_RedrawBnet(IDirectDrawSurfaceImpl* This); #endif diff --git a/inc/wndproc.h b/inc/wndproc.h index 6d2a199..d74dff7 100644 --- a/inc/wndproc.h +++ b/inc/wndproc.h @@ -9,6 +9,7 @@ #define WM_DISPLAYCHANGE_DDRAW WM_APP+116 #define IDT_TIMER_LEAVE_BNET 541287654 +#define IDT_TIMER_REDRAW_BNET 541287655 LRESULT CALLBACK fake_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); diff --git a/src/IDirectDraw/IDirectDrawSurface.c b/src/IDirectDraw/IDirectDrawSurface.c index de4b209..b86f2c4 100644 --- a/src/IDirectDraw/IDirectDrawSurface.c +++ b/src/IDirectDraw/IDirectDrawSurface.c @@ -90,9 +90,21 @@ ULONG __stdcall IDirectDrawSurface__Release(IDirectDrawSurfaceImpl* This) HeapFree(GetProcessHeap(), 0, This->surface); } + if (This->bnet_bitmap) + { + DeleteObject(This->bnet_bitmap); + } + else if (This->bnet_surface) + { + HeapFree(GetProcessHeap(), 0, This->bnet_surface); + } + if (This->hdc) DeleteDC(This->hdc); + if (This->bnet_dc) + DeleteDC(This->bnet_dc); + if (This->bmi) HeapFree(GetProcessHeap(), 0, This->bmi); diff --git a/src/ddsurface.c b/src/ddsurface.c index 8a8ceef..bb75231 100644 --- a/src/ddsurface.c +++ b/src/ddsurface.c @@ -1190,28 +1190,39 @@ HRESULT dds_SetPalette(IDirectDrawSurfaceImpl* This, IDirectDrawPaletteImpl* lpD return DD_OK; } -HRESULT dds_Unlock(IDirectDrawSurfaceImpl* This, LPRECT lpRect) +void dds_RedrawBnet(IDirectDrawSurfaceImpl* This) { /* Hack for Warcraft II BNE and Diablo */ HWND hwnd = g_ddraw->bnet_active ? FindWindowEx(HWND_DESKTOP, NULL, "SDlgDialog", NULL) : NULL; - if (hwnd && (This->caps & DDSCAPS_PRIMARYSURFACE)) + if (hwnd) { HDC primary_dc; dds_GetDC(This, &primary_dc); + if (g_ddraw->primary->palette) + { + SetDIBColorTable(g_ddraw->primary->bnet_dc, 0, 256, g_ddraw->primary->palette->data_rgb); + } + /* GdiTransparentBlt idea taken from Aqrit's war2 ddraw */ RGBQUAD quad; GetDIBColorTable(primary_dc, 0xFE, 1, &quad); COLORREF color = RGB(quad.rgbRed, quad.rgbGreen, quad.rgbBlue); BOOL erase = FALSE; + HWND hwnd_bnet[20]; + int i = 0; + + memset(&hwnd_bnet[0], 0, sizeof(hwnd_bnet)); do { RECT rc; if (fake_GetWindowRect(hwnd, &rc)) { + hwnd_bnet[i++] = hwnd; + if (rc.bottom - rc.top == 479) erase = TRUE; @@ -1236,6 +1247,36 @@ HRESULT dds_Unlock(IDirectDrawSurfaceImpl* This, LPRECT lpRect) } while ((hwnd = FindWindowEx(HWND_DESKTOP, hwnd, "SDlgDialog", NULL))); + /* hack for windows 8/10 fullscreen exclusive mode */ + EnterCriticalSection(&g_ddraw->cs); + + for (i = sizeof(hwnd_bnet) / sizeof(hwnd_bnet[0]); i--; ) + { + if (hwnd_bnet[i]) + { + RECT rc; + if (fake_GetWindowRect(hwnd_bnet[i], &rc)) + { + HDC dc = GetDC(hwnd_bnet[i]); + + BitBlt( + g_ddraw->primary->bnet_dc, + rc.left, + rc.top, + rc.right - rc.left, + rc.bottom - rc.top, + dc, + 0, + 0, + SRCCOPY); + + ReleaseDC(hwnd_bnet[i], dc); + } + } + } + + LeaveCriticalSection(&g_ddraw->cs); + if (erase) { BOOL x = g_ddraw->ticks_limiter.use_blt_or_flip; @@ -1245,10 +1286,21 @@ HRESULT dds_Unlock(IDirectDrawSurfaceImpl* This, LPRECT lpRect) g_ddraw->ticks_limiter.use_blt_or_flip = x; } + + if (g_ddraw->render.run) + ReleaseSemaphore(g_ddraw->render.sem, 1, NULL); + } +} + +HRESULT dds_Unlock(IDirectDrawSurfaceImpl* This, LPRECT lpRect) +{ + if ((This->caps & DDSCAPS_PRIMARYSURFACE)) + { + dds_RedrawBnet(This); } /* Hack for Star Trek Armada */ - hwnd = g_ddraw->armadahack ? FindWindowEx(HWND_DESKTOP, NULL, "#32770", NULL) : NULL; + HWND hwnd = g_ddraw->armadahack ? FindWindowEx(HWND_DESKTOP, NULL, "#32770", NULL) : NULL; if (hwnd && (This->caps & DDSCAPS_PRIMARYSURFACE)) { @@ -1471,6 +1523,26 @@ HRESULT dd_CreateSurface( { g_ddraw->primary = dst_surface; FakePrimarySurface = dst_surface->surface; + + dst_surface->bnet_dc = CreateCompatibleDC(g_ddraw->render.hdc); + + dst_surface->bnet_bitmap = + CreateDIBSection( + dst_surface->bnet_dc, + dst_surface->bmi, + DIB_RGB_COLORS, + (void**)&dst_surface->bnet_surface, NULL, 0); + + if (!dst_surface->bnet_bitmap) + { + dst_surface->bnet_surface = + HeapAlloc( + GetProcessHeap(), + HEAP_ZERO_MEMORY, + dst_surface->l_pitch * (dst_surface->height + 200) * dst_surface->lx_pitch); + } + + SelectObject(dst_surface->bnet_dc, dst_surface->bnet_bitmap); } SelectObject(dst_surface->hdc, dst_surface->bitmap); diff --git a/src/render_d3d9.c b/src/render_d3d9.c index f640fb0..c6e3b95 100644 --- a/src/render_d3d9.c +++ b/src/render_d3d9.c @@ -442,6 +442,27 @@ DWORD WINAPI d3d9_render_main(void) } } } + + if (g_ddraw->bnet_active) + { + RECT rc = { 0,0,g_ddraw->width,g_ddraw->height }; + + if (SUCCEEDED(IDirect3DTexture9_LockRect(g_d3d9.surface_tex[tex_index], 0, &lock_rc, &rc, 0))) + { + unsigned char* src = (unsigned char*)g_ddraw->primary->bnet_surface; + unsigned char* dst = (unsigned char*)lock_rc.pBits; + + for (int i = 0; i < g_ddraw->height; i++) + { + memcpy(dst, src, g_ddraw->primary->l_pitch); + + src += g_ddraw->primary->l_pitch; + dst += lock_rc.Pitch; + } + + IDirect3DTexture9_UnlockRect(g_d3d9.surface_tex[tex_index], 0); + } + } } LeaveCriticalSection(&g_ddraw->cs); @@ -456,11 +477,6 @@ DWORD WINAPI d3d9_render_main(void) IDirect3DDevice9_DrawPrimitive(g_d3d9.device, D3DPT_TRIANGLESTRIP, 0, 2); IDirect3DDevice9_EndScene(g_d3d9.device); - if (g_ddraw->bnet_active) - { - IDirect3DDevice9_Clear(g_d3d9.device, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0); - } - if (FAILED(IDirect3DDevice9_Present(g_d3d9.device, NULL, NULL, NULL, NULL))) { DWORD_PTR result; diff --git a/src/render_gdi.c b/src/render_gdi.c index 74528f3..e2e8ef0 100644 --- a/src/render_gdi.c +++ b/src/render_gdi.c @@ -97,8 +97,20 @@ DWORD WINAPI gdi_render_main(void) if (g_ddraw->bnet_active) { - RECT rc = { 0, 0, g_ddraw->render.width, g_ddraw->render.height }; - FillRect(g_ddraw->render.hdc, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH)); + StretchDIBits( + g_ddraw->render.hdc, + g_ddraw->render.viewport.x, + g_ddraw->render.viewport.y, + g_ddraw->render.viewport.width, + g_ddraw->render.viewport.height, + 0, + 0, + g_ddraw->width, + g_ddraw->height, + g_ddraw->primary->bnet_surface, + g_ddraw->primary->bmi, + DIB_RGB_COLORS, + SRCCOPY); } else if (upscale_hack) { diff --git a/src/render_ogl.c b/src/render_ogl.c index 87c6b1e..1c5336c 100644 --- a/src/render_ogl.c +++ b/src/render_ogl.c @@ -701,6 +701,28 @@ static void ogl_render() } } } + + if (g_ddraw->bnet_active) + { + glBindTexture(GL_TEXTURE_2D, g_ogl.surface_tex_ids[tex_index]); + + if (g_ogl.adjust_alignment) + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + glTexSubImage2D( + GL_TEXTURE_2D, + 0, + 0, + 0, + g_ddraw->width, + g_ddraw->height, + g_ogl.surface_format, + g_ogl.surface_type, + g_ddraw->primary->bnet_surface); + + if (g_ogl.adjust_alignment) + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + } } LeaveCriticalSection(&g_ddraw->cs); @@ -823,9 +845,6 @@ static void ogl_render() glEnd(); } - if (g_ddraw->bnet_active) - glClear(GL_COLOR_BUFFER_BIT); - SwapBuffers(g_ddraw->render.hdc); #if _DEBUG diff --git a/src/winapi_hooks.c b/src/winapi_hooks.c index 3f96827..8f3c9c0 100644 --- a/src/winapi_hooks.c +++ b/src/winapi_hooks.c @@ -591,6 +591,7 @@ BOOL WINAPI fake_DestroyWindow(HWND hWnd) if (!FindWindowEx(HWND_DESKTOP, NULL, "SDlgDialog", NULL)) { + KillTimer(g_ddraw->hwnd, IDT_TIMER_REDRAW_BNET); g_ddraw->bnet_active = FALSE; SetFocus(g_ddraw->hwnd); mouse_lock(); @@ -680,6 +681,7 @@ HWND WINAPI fake_CreateWindowExA( g_ddraw->resizable = FALSE; g_ddraw->bnet_active = TRUE; + SetTimer(g_ddraw->hwnd, IDT_TIMER_REDRAW_BNET, 200, (TIMERPROC)NULL); mouse_unlock(); } diff --git a/src/wndproc.c b/src/wndproc.c index e001662..d69cbb5 100644 --- a/src/wndproc.c +++ b/src/wndproc.c @@ -4,6 +4,7 @@ #include "dllmain.h" #include "dd.h" #include "hook.h" +#include "ddsurface.h" #include "mouse.h" #include "render_d3d9.h" #include "config.h" @@ -162,6 +163,13 @@ LRESULT CALLBACK fake_WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam return 0; } + case IDT_TIMER_REDRAW_BNET: + { + if (g_ddraw->primary) + dds_RedrawBnet(g_ddraw->primary); + + return 0; + } } break; }