From 6cbfd84c695b658c6187a97a64fc641f55317266 Mon Sep 17 00:00:00 2001 From: "nitsuja-@hotmail.com" Date: Mon, 11 Jul 2011 04:56:14 +0000 Subject: [PATCH] added fallback implementation of IDirectDrawSurface::GetDC, since some newer video cards have apparently dropped support for it on some common surface formats --- src/wintasee/hooks/ddrawhooks.cpp | 89 +++++++++++++++++++++++++------ src/wintasee/hooks/gdihooks.cpp | 4 +- 2 files changed, 76 insertions(+), 17 deletions(-) diff --git a/src/wintasee/hooks/ddrawhooks.cpp b/src/wintasee/hooks/ddrawhooks.cpp index a395254..37186af 100644 --- a/src/wintasee/hooks/ddrawhooks.cpp +++ b/src/wintasee/hooks/ddrawhooks.cpp @@ -45,6 +45,12 @@ static RECT a_bltsaved_buf = {0}; static RECT c_bltsaved_buf = {0}; static DDBLTFX e_bltsaved_buf = {0}; +struct ManualHDCInfo +{ + HBITMAP oldBitmap; +}; +std::map manuallyCreatedSurfaceDCs; + void RescaleRect(RECT& rect, RECT from, RECT to); void ConfineRect(RECT& rect, RECT bounds) @@ -726,8 +732,8 @@ struct MyDirectDrawSurface //cmdprintf("DEBUGPAUSE: " __FUNCTION__); if(!(lockFlags & (DDLOCK_WRITEONLY|DDLOCK_DISCARDCONTENTS))) { - DDSURFACEDESCN ddsdBB = { sizeof(DDSURFACEDESCN) }; - GetSurfaceDesc(pBackbuffer, &ddsdBB); + //DDSURFACEDESCN ddsdBB = { sizeof(DDSURFACEDESCN), DDSD_WIDTH | DDSD_HEIGHT }; + //GetSurfaceDesc(pBackbuffer, &ddsdBB); RECT rect = {0, 0, 1, 1 }; //RECT rect = {0, 0, ddsdBB.dwWidth, ddsdBB.dwHeight }; //POINT pt = {0,0}; @@ -797,29 +803,82 @@ struct MyDirectDrawSurface return rv; } - - static HRESULT(STDMETHODCALLTYPE *GetDC)(DIRECTDRAWSURFACEN* pThis, HDC* lphDC); static HRESULT STDMETHODCALLTYPE MyGetDC(DIRECTDRAWSURFACEN* pThis, HDC* lphDC) { + if(!lphDC) + return DDERR_INVALIDPARAMS; + + // normal case HRESULT rv = GetDC(pThis, lphDC); ddrawdebugprintf(__FUNCTION__ " called. returned 0x%X, got 0x%X\n", rv, lphDC?*lphDC:0); - return rv; + + if(FAILED(rv)) + { + // fallback for video cards that have bugs with GetDC on a DirectDraw surface + DDSURFACEDESCN desc = { sizeof(DDSURFACEDESCN), DDSD_HEIGHT }; + if(FAILED(GetSurfaceDesc(pThis,&desc))) + { *lphDC = NULL; return DDERR_CANTCREATEDC; } + HDC hdcWnd = ::GetDC(gamehwnd); + HDC hdc = ::CreateCompatibleDC(hdcWnd); + if(!hdc || FAILED(Lock(pThis,NULL,&desc,DDLOCK_WAIT|DDLOCK_READONLY,NULL))) + { *lphDC = NULL; ::ReleaseDC(gamehwnd,hdcWnd); return DDERR_CANTCREATEDC; } + int width = desc.lPitch * 8 / desc.ddpfPixelFormat.dwRGBBitCount; + int height = desc.dwHeight; + HBITMAP hbmp = ::CreateCompatibleBitmap(hdcWnd,width,height); + ::ReleaseDC(gamehwnd,hdcWnd); + struct { BITMAPINFO bmi; + RGBQUAD remainingColors [255]; + } fbmi = {sizeof(BITMAPINFOHEADER)}; + BITMAPINFO& bmi = *(BITMAPINFO*)&fbmi; + ::GetDIBits(hdc, hbmp, 0, 0, 0, &bmi, DIB_RGB_COLORS); + bmi.bmiHeader.biHeight = -height; + bmi.bmiHeader.biCompression = BI_RGB; + ::SetDIBits(hdc,hbmp,0,height,desc.lpSurface,&bmi,0); + Unlock(pThis,NULL); + ManualHDCInfo info; + info.oldBitmap = (HBITMAP)::SelectObject(hdc, hbmp); + manuallyCreatedSurfaceDCs[hdc] = info; + *lphDC = hdc; + } + return DD_OK; } - static HRESULT(STDMETHODCALLTYPE *ReleaseDC)(DIRECTDRAWSURFACEN* pThis, HDC hDC); - static HRESULT STDMETHODCALLTYPE MyReleaseDC(DIRECTDRAWSURFACEN* pThis, HDC hDC) + static HRESULT(STDMETHODCALLTYPE *ReleaseDC)(DIRECTDRAWSURFACEN* pThis, HDC hdc); + static HRESULT STDMETHODCALLTYPE MyReleaseDC(DIRECTDRAWSURFACEN* pThis, HDC hdc) { ddrawdebugprintf(__FUNCTION__ " called.\n"); + std::map::iterator found = manuallyCreatedSurfaceDCs.find(hdc); + if(found == manuallyCreatedSurfaceDCs.end()) + { + // normal case + HRESULT rv = ReleaseDC(pThis, hdc); + if(SUCCEEDED(rv)) + videoMemoryBackupDirty[pThis] = TRUE; + return rv; + } - HRESULT rv = ReleaseDC(pThis, hDC); - - //// backup the surface pixels if it's in video memory, for savestates. - //BackupVideoMemory(pThis); - - videoMemoryBackupDirty[pThis] = TRUE; - - return rv; + // fallback for video cards that have bugs with GetDC on a DirectDraw surface + ManualHDCInfo info = found->second; + manuallyCreatedSurfaceDCs.erase(found); + DDSURFACEDESCN desc = { sizeof(DDSURFACEDESCN), DDSD_HEIGHT }; + GetSurfaceDesc(pThis, &desc); + Lock(pThis,NULL,&desc,DDLOCK_WAIT|DDLOCK_WRITEONLY,NULL); + int width = desc.lPitch * 8 / desc.ddpfPixelFormat.dwRGBBitCount; + int height = desc.dwHeight; + HBITMAP hbmp = (HBITMAP)SelectObject(hdc, info.oldBitmap); + struct { BITMAPINFO bmi; + RGBQUAD remainingColors [255]; + } fbmi = {sizeof(BITMAPINFOHEADER)}; + BITMAPINFO& bmi = *(BITMAPINFO*)&fbmi; + ::GetDIBits(hdc, hbmp, 0, 0, 0, &bmi, DIB_RGB_COLORS); + bmi.bmiHeader.biHeight = -height; + bmi.bmiHeader.biCompression = BI_RGB; + ::GetDIBits(hdc,hbmp,0,height,desc.lpSurface,&bmi,0); + Unlock(pThis,NULL); + ::DeleteObject((HGDIOBJ)hbmp); + ::DeleteDC(hdc); + return DD_OK; } static HRESULT(STDMETHODCALLTYPE *GetFlipStatus)(DIRECTDRAWSURFACEN* pThis, DWORD flags); diff --git a/src/wintasee/hooks/gdihooks.cpp b/src/wintasee/hooks/gdihooks.cpp index f49606d..1adbdc5 100644 --- a/src/wintasee/hooks/gdihooks.cpp +++ b/src/wintasee/hooks/gdihooks.cpp @@ -100,10 +100,10 @@ static void FrameBoundaryDIBitsToAVI(const void* bits, const BITMAPINFO& bmi) } } -union FULLBITMAPINFO +struct FULLBITMAPINFO { BITMAPINFO bmi; - RGBQUAD remainingColors [255 + 3*sizeof(DWORD)]; + RGBQUAD remainingColors [255]; }; //#define UNSELECT_BEFORE_HDC_CAPTURE // TODO: if this doesn't slow down AVI capture of GDI games and it doesn't break anything when savestates are loaded during AVI capture of GDI games, then enable it