commit 2a5e699c6e632e5794cc4f0e1d994e1e360de268 Author: narzoul Date: Fri Dec 25 01:57:44 2015 +0100 Initial commit diff --git a/DDrawCompat/CompatDirectDraw.cpp b/DDrawCompat/CompatDirectDraw.cpp new file mode 100644 index 0000000..c2e2f35 --- /dev/null +++ b/DDrawCompat/CompatDirectDraw.cpp @@ -0,0 +1,140 @@ +#include "CompatDirectDraw.h" +#include "CompatDirectDrawSurface.h" +#include "CompatPrimarySurface.h" + +namespace +{ + template + HRESULT PASCAL enumDisplayModesCallback( + TSurfaceDesc* lpDDSurfaceDesc, + LPVOID lpContext) + { + if (lpDDSurfaceDesc) + { + *static_cast(lpContext) = lpDDSurfaceDesc->ddpfPixelFormat; + } + return DDENUMRET_CANCEL; + } +} + +template +void CompatDirectDraw::setCompatVtable(Vtable& vtable) +{ + vtable.CreateSurface = &CreateSurface; + vtable.RestoreDisplayMode = &RestoreDisplayMode; + vtable.SetDisplayMode = &SetDisplayMode; +} + +template +HRESULT STDMETHODCALLTYPE CompatDirectDraw::CreateSurface( + TDirectDraw* This, + TSurfaceDesc* lpDDSurfaceDesc, + TSurface** lplpDDSurface, + IUnknown* pUnkOuter) +{ + HRESULT result = DD_OK; + + const bool isPrimary = lpDDSurfaceDesc && + (lpDDSurfaceDesc->dwFlags & DDSD_CAPS) && + (lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE); + + if (isPrimary) + { + result = CompatDirectDrawSurface::createCompatPrimarySurface( + *This, *lpDDSurfaceDesc, *lplpDDSurface); + } + else + { + if (CompatPrimarySurface::displayMode.pixelFormat.dwSize != 0 && + !(lpDDSurfaceDesc->dwFlags & DDSD_PIXELFORMAT)) + { + lpDDSurfaceDesc->dwFlags |= DDSD_PIXELFORMAT; + lpDDSurfaceDesc->ddpfPixelFormat = CompatPrimarySurface::displayMode.pixelFormat; + } + result = s_origVtable.CreateSurface(This, lpDDSurfaceDesc, lplpDDSurface, pUnkOuter); + } + + if (SUCCEEDED(result)) + { + CompatDirectDrawSurface::fixSurfacePtrs(**lplpDDSurface); + if (isPrimary) + { + CompatDirectDrawSurface::updateSurfaceParams(); + } + } + + return result; +} + +template +HRESULT STDMETHODCALLTYPE CompatDirectDraw::RestoreDisplayMode(TDirectDraw* This) +{ + HRESULT result = s_origVtable.RestoreDisplayMode(This); + if (SUCCEEDED(result)) + { + CompatPrimarySurface::displayMode = CompatPrimarySurface::getDisplayMode(*This); + } + return result; +} + +template +template +static HRESULT STDMETHODCALLTYPE CompatDirectDraw::SetDisplayMode( + TDirectDraw* This, + DWORD dwWidth, + DWORD dwHeight, + DWORD dwBPP, + Params... params) +{ + TSurfaceDesc desc = {}; + desc.dwSize = sizeof(desc); + desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; + desc.dwWidth = dwWidth; + desc.dwHeight = dwHeight; + desc.ddpfPixelFormat.dwSize = sizeof(desc.ddpfPixelFormat); + desc.ddpfPixelFormat.dwFlags = DDPF_RGB; + desc.ddpfPixelFormat.dwRGBBitCount = dwBPP; + + switch (dwBPP) + { + case 1: desc.ddpfPixelFormat.dwFlags |= DDPF_PALETTEINDEXED1; break; + case 2: desc.ddpfPixelFormat.dwFlags |= DDPF_PALETTEINDEXED2; break; + case 4: desc.ddpfPixelFormat.dwFlags |= DDPF_PALETTEINDEXED4; break; + case 8: desc.ddpfPixelFormat.dwFlags |= DDPF_PALETTEINDEXED8; break; + } + + DDPIXELFORMAT pf = {}; + if (dwBPP > 8) + { + if (FAILED(s_origVtable.EnumDisplayModes(This, 0, &desc, &pf, &enumDisplayModesCallback)) || + 0 == pf.dwSize) + { + Compat::Log() << "Failed to find the requested display mode: " << + dwWidth << "x" << dwHeight << "x" << dwBPP; + return DDERR_INVALIDMODE; + } + } + else + { + pf = desc.ddpfPixelFormat; + } + + HRESULT result = s_origVtable.SetDisplayMode(This, dwWidth, dwHeight, 32, params...); + if (SUCCEEDED(result)) + { + CompatPrimarySurface::displayMode.width = dwWidth; + CompatPrimarySurface::displayMode.height = dwHeight; + CompatPrimarySurface::displayMode.pixelFormat = pf; + } + else + { + Compat::Log() << "Failed to set the display mode to " << dwWidth << "x" << dwHeight << "x32"; + } + + return result; +} + +template CompatDirectDraw; +template CompatDirectDraw; +template CompatDirectDraw; +template CompatDirectDraw; diff --git a/DDrawCompat/CompatDirectDraw.h b/DDrawCompat/CompatDirectDraw.h new file mode 100644 index 0000000..e5d9bad --- /dev/null +++ b/DDrawCompat/CompatDirectDraw.h @@ -0,0 +1,31 @@ +#pragma once + +#include "CompatVtable.h" +#include "DDrawTypes.h" +#include "DirectDrawVtblVisitor.h" + +template +class CompatDirectDraw : public CompatVtable, TDirectDraw> +{ +public: + typedef typename Types::TCreatedSurface TSurface; + typedef typename Types::TSurfaceDesc TSurfaceDesc; + + static void setCompatVtable(Vtable& vtable); + + static HRESULT STDMETHODCALLTYPE CreateSurface( + TDirectDraw* This, + TSurfaceDesc* lpDDSurfaceDesc, + TSurface** lplpDDSurface, + IUnknown* pUnkOuter); + + static HRESULT STDMETHODCALLTYPE RestoreDisplayMode(TDirectDraw* This); + + template + static HRESULT STDMETHODCALLTYPE SetDisplayMode( + TDirectDraw* This, + DWORD dwWidth, + DWORD dwHeight, + DWORD dwBPP, + Params... params); +}; diff --git a/DDrawCompat/CompatDirectDrawPalette.cpp b/DDrawCompat/CompatDirectDrawPalette.cpp new file mode 100644 index 0000000..fa0d087 --- /dev/null +++ b/DDrawCompat/CompatDirectDrawPalette.cpp @@ -0,0 +1,23 @@ +#include "CompatDirectDrawPalette.h" +#include "CompatPrimarySurface.h" +#include "RealPrimarySurface.h" + +void CompatDirectDrawPalette::setCompatVtable(IDirectDrawPaletteVtbl& vtable) +{ + vtable.SetEntries = &SetEntries; +} + +HRESULT STDMETHODCALLTYPE CompatDirectDrawPalette::SetEntries( + IDirectDrawPalette* This, + DWORD dwFlags, + DWORD dwStartingEntry, + DWORD dwCount, + LPPALETTEENTRY lpEntries) +{ + HRESULT result = s_origVtable.SetEntries(This, dwFlags, dwStartingEntry, dwCount, lpEntries); + if (This == CompatPrimarySurface::palette && SUCCEEDED(result)) + { + RealPrimarySurface::updatePalette(); + } + return result; +} diff --git a/DDrawCompat/CompatDirectDrawPalette.h b/DDrawCompat/CompatDirectDrawPalette.h new file mode 100644 index 0000000..e2e02ce --- /dev/null +++ b/DDrawCompat/CompatDirectDrawPalette.h @@ -0,0 +1,17 @@ +#pragma once + +#include "CompatVtable.h" +#include "DirectDrawPaletteVtblVisitor.h" + +class CompatDirectDrawPalette : public CompatVtable +{ +public: + static void setCompatVtable(IDirectDrawPaletteVtbl& vtable); + + static HRESULT STDMETHODCALLTYPE SetEntries( + IDirectDrawPalette* This, + DWORD dwFlags, + DWORD dwStartingEntry, + DWORD dwCount, + LPPALETTEENTRY lpEntries); +}; diff --git a/DDrawCompat/CompatDirectDrawSurface.cpp b/DDrawCompat/CompatDirectDrawSurface.cpp new file mode 100644 index 0000000..7d849d7 --- /dev/null +++ b/DDrawCompat/CompatDirectDrawSurface.cpp @@ -0,0 +1,685 @@ +#include +#include +#include + +#include "CompatDirectDraw.h" +#include "CompatDirectDrawSurface.h" +#include "CompatGdiSurface.h" +#include "CompatPrimarySurface.h" +#include "DDrawProcs.h" +#include "IReleaseNotifier.h" +#include "RealPrimarySurface.h" + +namespace +{ + struct SimilarSurface + { + DWORD width; + DWORD height; + DDPIXELFORMAT pixelFormat; + IDirectDrawSurface7* front; + IDirectDrawSurface7* back; + }; + + SimilarSurface getSimilarSurface(const DDSURFACEDESC2& desc); + bool mirrorBlt(IDirectDrawSurface7& dst, IDirectDrawSurface7& src, RECT srcRect, DWORD mirrorFx); + + IDirectDraw7* g_mirrorDirectDraw = nullptr; + + IDirectDraw7* createMirrorDirectDraw() + { + IDirectDraw7* dd = nullptr; + CALL_ORIG_DDRAW(DirectDrawCreateEx, nullptr, reinterpret_cast(&dd), IID_IDirectDraw7, nullptr); + if (!dd || + FAILED(CompatDirectDraw::s_origVtable.SetCooperativeLevel(dd, nullptr, DDSCL_NORMAL))) + { + Compat::Log() << "Failed to create a helper DirectDraw object for mirroring"; + } + return dd; + } + + void fixSurfacePtr(IDirectDrawSurface7& surface, DDSURFACEDESC2& desc) + { + if ((desc.dwFlags & DDSD_CAPS) && (desc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY)) + { + return; + } + + DDBLTFX fx = {}; + fx.dwSize = sizeof(fx); + surface.lpVtbl->Blt(&surface, nullptr, nullptr, nullptr, DDBLT_COLORFILL | DDBLT_WAIT, &fx); + } + + HRESULT WINAPI enumSurfacesCallback( + LPDIRECTDRAWSURFACE7 lpDDSurface, + LPDDSURFACEDESC2 lpDDSurfaceDesc, + LPVOID lpContext) + { + auto visitedSurfaces = *static_cast*>(lpContext); + + if (visitedSurfaces.find(lpDDSurface) == visitedSurfaces.end()) + { + visitedSurfaces.insert(lpDDSurface); + fixSurfacePtr(*lpDDSurface, *lpDDSurfaceDesc); + lpDDSurface->lpVtbl->EnumAttachedSurfaces(lpDDSurface, &visitedSurfaces, &enumSurfacesCallback); + } + + lpDDSurface->lpVtbl->Release(lpDDSurface); + return DDENUMRET_OK; + } + + void fixSurfacePtrs(IDirectDrawSurface7& surface) + { + DDSURFACEDESC2 desc = {}; + desc.dwSize = sizeof(desc); + surface.lpVtbl->GetSurfaceDesc(&surface, &desc); + + fixSurfacePtr(surface, desc); + std::set visitedSurfaces{ &surface }; + surface.lpVtbl->EnumAttachedSurfaces(&surface, &visitedSurfaces, &enumSurfacesCallback); + } + + IDirectDrawSurface7* getMirroredSurface(IDirectDrawSurface7& surface, RECT* srcRect, DWORD mirrorFx) + { + auto& origVtable = CompatDirectDrawSurface::s_origVtable; + + DDSURFACEDESC2 desc = {}; + desc.dwSize = sizeof(desc); + HRESULT result = origVtable.GetSurfaceDesc(&surface, &desc); + if (FAILED(result)) + { + LOG_ONCE("Failed to get surface description for mirroring: " << result); + return nullptr; + } + + if (srcRect) + { + desc.dwWidth = srcRect->right - srcRect->left; + desc.dwHeight = srcRect->bottom - srcRect->top; + } + + SimilarSurface similarSurface = getSimilarSurface(desc); + if (!similarSurface.front) + { + return nullptr; + } + + RECT rect = { 0, 0, static_cast(desc.dwWidth), static_cast(desc.dwHeight) }; + if ((mirrorFx & DDBLTFX_MIRRORLEFTRIGHT) && (mirrorFx & DDBLTFX_MIRRORUPDOWN)) + { + if (!mirrorBlt(*similarSurface.back, surface, srcRect ? *srcRect : rect, DDBLTFX_MIRRORLEFTRIGHT) || + !mirrorBlt(*similarSurface.front, *similarSurface.back, rect, DDBLTFX_MIRRORUPDOWN)) + { + return nullptr; + } + } + else if (!mirrorBlt(*similarSurface.front, surface, srcRect ? *srcRect : rect, mirrorFx)) + { + return nullptr; + } + + origVtable.AddRef(similarSurface.front); + return similarSurface.front; + } + + template + TSurface* getMirroredSurface(TSurface& surface, RECT* rect, DWORD mirrorFx) + { + auto& origVtable = CompatDirectDrawSurface::s_origVtable; + + IDirectDrawSurface7* surface7 = nullptr; + origVtable.QueryInterface(&surface, IID_IDirectDrawSurface7, reinterpret_cast(&surface7)); + IDirectDrawSurface7* mirroredSurface7 = getMirroredSurface(*surface7, rect, mirrorFx); + surface7->lpVtbl->Release(surface7); + + if (!mirroredSurface7) + { + return nullptr; + } + + auto& origVtable7 = CompatDirectDrawSurface::s_origVtable; + TSurface* mirroredSurface = nullptr; + origVtable7.QueryInterface(mirroredSurface7, + CompatDirectDrawSurface::s_iid, reinterpret_cast(&mirroredSurface)); + origVtable7.Release(mirroredSurface7); + return mirroredSurface; + } + + SimilarSurface getSimilarSurface(const DDSURFACEDESC2& desc) + { + static std::vector similarSurfacesVidMem; + static std::vector similarSurfacesSysMem; + + std::vector& similarSurfaces = + (desc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) ? similarSurfacesSysMem : similarSurfacesVidMem; + + DDPIXELFORMAT pf = desc.ddpfPixelFormat; + if (!(pf.dwFlags & DDPF_FOURCC)) + { + pf.dwFourCC = 0; + } + if (!(pf.dwFlags & (DDPF_ALPHAPIXELS | DDPF_ZPIXELS))) + { + pf.dwRGBAlphaBitMask = 0; + } + auto it = std::find_if(similarSurfaces.begin(), similarSurfaces.end(), + [&](SimilarSurface& s) { return 0 == memcmp(&s.pixelFormat, &pf, sizeof(pf)); }); + + auto& origVtable = CompatDirectDrawSurface::s_origVtable; + SimilarSurface similarSurface = {}; + if (it != similarSurfaces.end()) + { + if (DDERR_SURFACELOST == origVtable.IsLost(it->front) || + DDERR_SURFACELOST == origVtable.IsLost(it->back)) + { + origVtable.Release(it->front); + origVtable.Release(it->back); + similarSurfaces.erase(it); + it = similarSurfaces.end(); + } + else + { + similarSurface = *it; + } + } + + if (similarSurface.width >= desc.dwWidth && similarSurface.height >= desc.dwHeight) + { + return similarSurface; + } + + DDSURFACEDESC2 similarDesc = {}; + similarDesc.dwSize = sizeof(similarDesc); + similarDesc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS; + similarDesc.dwWidth = max(similarSurface.width, desc.dwWidth); + similarDesc.dwHeight = max(similarSurface.height, desc.dwHeight); + similarDesc.ddpfPixelFormat = pf; + similarDesc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + if (desc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) + { + similarDesc.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY; + } + + HRESULT result = CompatDirectDraw::s_origVtable.CreateSurface( + g_mirrorDirectDraw, &similarDesc, &similarSurface.front, nullptr); + if (FAILED(result)) + { + LOG_ONCE("Failed to create a front surface for mirroring"); + similarSurface.front = nullptr; + return similarSurface; + } + + result = CompatDirectDraw::s_origVtable.CreateSurface( + g_mirrorDirectDraw, &similarDesc, &similarSurface.back, nullptr); + if (FAILED(result)) + { + LOG_ONCE("Failed to create a back surface for mirroring"); + origVtable.Release(similarSurface.front); + similarSurface.front = nullptr; + return similarSurface; + } + + similarSurface.width = similarDesc.dwWidth; + similarSurface.height = similarDesc.dwHeight; + similarSurface.pixelFormat = pf; + + if (it != similarSurfaces.end()) + { + origVtable.Release(it->front); + origVtable.Release(it->back); + *it = similarSurface; + } + else + { + similarSurfaces.push_back(similarSurface); + } + + return similarSurface; + } + + bool mirrorBlt(IDirectDrawSurface7& dst, IDirectDrawSurface7& src, RECT srcRect, DWORD mirrorFx) + { + if (DDBLTFX_MIRRORLEFTRIGHT == mirrorFx) + { + LONG width = srcRect.right - srcRect.left; + srcRect.left = srcRect.right - 1; + for (LONG x = 0; x < width; ++x) + { + HRESULT result = CompatDirectDrawSurface::s_origVtable.BltFast( + &dst, x, 0, &src, &srcRect, DDBLTFAST_WAIT); + if (FAILED(result)) + { + LOG_ONCE("Failed BltFast for mirroring: " << result); + return false; + } + --srcRect.left; + --srcRect.right; + } + } + else + { + LONG height = srcRect.bottom - srcRect.top; + srcRect.top = srcRect.bottom - 1; + for (LONG y = 0; y < height; ++y) + { + HRESULT result = CompatDirectDrawSurface::s_origVtable.BltFast( + &dst, 0, y, &src, &srcRect, DDBLTFAST_WAIT); + if (FAILED(result)) + { + LOG_ONCE("Failed BltFast for mirroring: " << result); + return false; + } + --srcRect.top; + --srcRect.bottom; + } + } + + return true; + } +} + +template +void CompatDirectDrawSurface::setCompatVtable(Vtable& vtable) +{ + vtable.Blt = &Blt; + vtable.BltFast = &BltFast; + vtable.Flip = &Flip; + vtable.GetCaps = &GetCaps; + vtable.GetSurfaceDesc = &GetSurfaceDesc; + vtable.IsLost = &IsLost; + vtable.Lock = &Lock; + vtable.QueryInterface = &QueryInterface; + vtable.ReleaseDC = &ReleaseDC; + vtable.Restore = &Restore; + vtable.SetClipper = &SetClipper; + vtable.SetPalette = &SetPalette; + vtable.Unlock = &Unlock; +} + +template +template +HRESULT CompatDirectDrawSurface::createCompatPrimarySurface( + TDirectDraw& dd, + TSurfaceDesc compatDesc, + TSurface*& compatSurface) +{ + if (0 == CompatPrimarySurface::displayMode.pixelFormat.dwSize) + { + CompatPrimarySurface::displayMode = CompatPrimarySurface::getDisplayMode(dd); + } + + HRESULT result = RealPrimarySurface::create(dd); + if (FAILED(result)) + { + return result; + } + + CompatPrimarySurface::width = CompatPrimarySurface::displayMode.width; + CompatPrimarySurface::height = CompatPrimarySurface::displayMode.height; + CompatPrimarySurface::pixelFormat = CompatPrimarySurface::displayMode.pixelFormat; + + compatDesc.dwFlags |= DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; + compatDesc.dwWidth = CompatPrimarySurface::width; + compatDesc.dwHeight = CompatPrimarySurface::height; + compatDesc.ddsCaps.dwCaps ^= DDSCAPS_PRIMARYSURFACE; + compatDesc.ddsCaps.dwCaps |= DDSCAPS_OFFSCREENPLAIN; + compatDesc.ddpfPixelFormat = CompatPrimarySurface::pixelFormat; + + result = CompatDirectDraw::s_origVtable.CreateSurface( + &dd, &compatDesc, &compatSurface, nullptr); + if (FAILED(result)) + { + Compat::Log() << "Failed to create the compat primary surface!"; + RealPrimarySurface::release(); + return result; + } + + g_mirrorDirectDraw = createMirrorDirectDraw(); + + s_compatPrimarySurface = compatSurface; + initCompatPrimarySurface(); + return DD_OK; +} + +template +void CompatDirectDrawSurface::fixSurfacePtrs(TSurface& surface) +{ + IDirectDrawSurface7* surface7 = nullptr; + surface.lpVtbl->QueryInterface(&surface, IID_IDirectDrawSurface7, reinterpret_cast(&surface7)); + ::fixSurfacePtrs(*surface7); + surface7->lpVtbl->Release(surface7); +} + +template +void CompatDirectDrawSurface::initPrimarySurfacePtr(const GUID& guid, IUnknown& surface) +{ + if (SUCCEEDED(surface.lpVtbl->QueryInterface( + &surface, guid, reinterpret_cast(&s_compatPrimarySurface)))) + { + s_compatPrimarySurface->lpVtbl->Release(s_compatPrimarySurface); + } +} + +template +HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::Blt( + TSurface* This, + LPRECT lpDestRect, + TSurface* lpDDSrcSurface, + LPRECT lpSrcRect, + DWORD dwFlags, + LPDDBLTFX lpDDBltFx) +{ + if ((This == s_compatPrimarySurface || lpDDSrcSurface == s_compatPrimarySurface) && + RealPrimarySurface::isLost()) + { + return DDERR_SURFACELOST; + } + + HRESULT result = DD_OK; + TSurface* mirroredSrcSurface = nullptr; + + if (lpDDSrcSurface && (dwFlags & DDBLT_DDFX) && lpDDBltFx && + (lpDDBltFx->dwDDFX & (DDBLTFX_MIRRORLEFTRIGHT | DDBLTFX_MIRRORUPDOWN))) + { + mirroredSrcSurface = getMirroredSurface(*lpDDSrcSurface, lpSrcRect, lpDDBltFx->dwDDFX); + } + + if (mirroredSrcSurface) + { + DWORD flags = dwFlags; + DDBLTFX fx = *lpDDBltFx; + fx.dwDDFX &= ~(DDBLTFX_MIRRORLEFTRIGHT | DDBLTFX_MIRRORUPDOWN); + if (0 == fx.dwDDFX) + { + flags ^= DDBLT_DDFX; + } + if (flags & DDBLT_KEYSRC) + { + DDCOLORKEY srcColorKey = {}; + s_origVtable.GetColorKey(lpDDSrcSurface, DDCKEY_SRCBLT, &srcColorKey); + s_origVtable.SetColorKey(mirroredSrcSurface, DDCKEY_SRCBLT, &srcColorKey); + } + + if (lpSrcRect) + { + RECT srcRect = { 0, 0, lpSrcRect->right - lpSrcRect->left, lpSrcRect->bottom - lpSrcRect->top }; + result = s_origVtable.Blt(This, lpDestRect, mirroredSrcSurface, &srcRect, flags, &fx); + } + else + { + result = s_origVtable.Blt(This, lpDestRect, mirroredSrcSurface, nullptr, flags, &fx); + } + + s_origVtable.Release(mirroredSrcSurface); + } + else + { + result = s_origVtable.Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); + } + + if (This == s_compatPrimarySurface && SUCCEEDED(result)) + { + RealPrimarySurface::update(); + } + + return result; +} + +template +HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::BltFast( + TSurface* This, + DWORD dwX, + DWORD dwY, + TSurface* lpDDSrcSurface, + LPRECT lpSrcRect, + DWORD dwTrans) +{ + if ((This == s_compatPrimarySurface || lpDDSrcSurface == s_compatPrimarySurface) && + RealPrimarySurface::isLost()) + { + return DDERR_SURFACELOST; + } + + HRESULT result = s_origVtable.BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); + if (This == s_compatPrimarySurface && SUCCEEDED(result)) + { + RealPrimarySurface::update(); + } + return result; +} + +template +HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::Flip( + TSurface* This, + TSurface* lpDDSurfaceTargetOverride, + DWORD dwFlags) +{ + HRESULT result = s_origVtable.Flip(This, lpDDSurfaceTargetOverride, dwFlags); + if (This == s_compatPrimarySurface && SUCCEEDED(result)) + { + result = RealPrimarySurface::flip(dwFlags); + updateSurfaceParams(); + } + return result; +} + +template +HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::GetCaps( + TSurface* This, + TDdsCaps* lpDDSCaps) +{ + HRESULT result = s_origVtable.GetCaps(This, lpDDSCaps); + if (This == s_compatPrimarySurface && SUCCEEDED(result)) + { + restorePrimaryCaps(*lpDDSCaps); + } + return result; +} + +template +HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::GetSurfaceDesc( + TSurface* This, + TSurfaceDesc* lpDDSurfaceDesc) +{ + HRESULT result = s_origVtable.GetSurfaceDesc(This, lpDDSurfaceDesc); + if (This == s_compatPrimarySurface && SUCCEEDED(result)) + { + restorePrimaryCaps(lpDDSurfaceDesc->ddsCaps); + } + return result; +} + +template +HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::IsLost(TSurface* This) +{ + HRESULT result = s_origVtable.IsLost(This); + if (This == s_compatPrimarySurface && SUCCEEDED(result)) + { + result = RealPrimarySurface::isLost() ? DDERR_SURFACELOST : DD_OK; + } + return result; +} + +template +HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::Lock( + TSurface* This, + LPRECT lpDestRect, + TSurfaceDesc* lpDDSurfaceDesc, + DWORD dwFlags, + HANDLE hEvent) +{ + if (This == s_compatPrimarySurface && RealPrimarySurface::isLost()) + { + return DDERR_SURFACELOST; + } + return s_origVtable.Lock(This, lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); +} + +template +HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::QueryInterface( + TSurface* This, + REFIID riid, + LPVOID* obp) +{ + if (This == s_compatPrimarySurface && riid == IID_IDirectDrawGammaControl) + { + return RealPrimarySurface::getSurface()->lpVtbl->QueryInterface( + RealPrimarySurface::getSurface(), riid, obp); + } + return s_origVtable.QueryInterface(This, riid, obp); +} + +template +HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::ReleaseDC(TSurface* This, HDC hDC) +{ + if (This == s_compatPrimarySurface && RealPrimarySurface::isLost()) + { + return DDERR_SURFACELOST; + } + + HRESULT result = s_origVtable.ReleaseDC(This, hDC); + if (This == s_compatPrimarySurface && SUCCEEDED(result)) + { + RealPrimarySurface::update(); + } + return result; +} + +template +HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::Restore(TSurface* This) +{ + const bool wasLost = DDERR_SURFACELOST == s_origVtable.IsLost(This); + HRESULT result = s_origVtable.Restore(This); + if (SUCCEEDED(result)) + { + if (wasLost) + { + fixSurfacePtrs(*This); + } + if (This == s_compatPrimarySurface) + { + result = RealPrimarySurface::restore(); + if (wasLost) + { + updateSurfaceParams(); + } + } + } + return result; +} + +template +HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::SetClipper( + TSurface* This, + LPDIRECTDRAWCLIPPER lpDDClipper) +{ + HRESULT result = s_origVtable.SetClipper(This, lpDDClipper); + if (This == s_compatPrimarySurface && SUCCEEDED(result)) + { + RealPrimarySurface::setClipper(lpDDClipper); + } + return result; +} + +template +HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::SetPalette( + TSurface* This, + LPDIRECTDRAWPALETTE lpDDPalette) +{ + HRESULT result = s_origVtable.SetPalette(This, lpDDPalette); + if (This == s_compatPrimarySurface && SUCCEEDED(result)) + { + CompatPrimarySurface::palette = lpDDPalette; + RealPrimarySurface::setPalette(lpDDPalette); + } + return result; +} + +template +HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface::Unlock(TSurface* This, TUnlockParam lpRect) +{ + HRESULT result = s_origVtable.Unlock(This, lpRect); + if (This == s_compatPrimarySurface && SUCCEEDED(result)) + { + RealPrimarySurface::update(); + } + return result; +} + +template +void CompatDirectDrawSurface::initCompatPrimarySurface() +{ + Compat::LogEnter("CompatDirectDrawSurface::initCompatPrimarySurface"); + + IUnknown& unk = reinterpret_cast(*s_compatPrimarySurface); + CompatDirectDrawSurface::initPrimarySurfacePtr(IID_IDirectDrawSurface, unk); + CompatDirectDrawSurface::initPrimarySurfacePtr(IID_IDirectDrawSurface2, unk); + CompatDirectDrawSurface::initPrimarySurfacePtr(IID_IDirectDrawSurface3, unk); + CompatDirectDrawSurface::initPrimarySurfacePtr(IID_IDirectDrawSurface4, unk); + CompatDirectDrawSurface::initPrimarySurfacePtr(IID_IDirectDrawSurface7, unk); + + if (SUCCEEDED(s_origVtable.QueryInterface( + s_compatPrimarySurface, + IID_IDirectDrawSurface7, + reinterpret_cast(&CompatPrimarySurface::surface)))) + { + IReleaseNotifier* releaseNotifier = &CompatPrimarySurface::releaseNotifier; + CompatPrimarySurface::surface->lpVtbl->SetPrivateData(CompatPrimarySurface::surface, + IID_IReleaseNotifier, releaseNotifier, sizeof(releaseNotifier), DDSPD_IUNKNOWNPOINTER); + CompatPrimarySurface::surface->lpVtbl->Release(CompatPrimarySurface::surface); + } + + Compat::LogLeave("CompatDirectDrawSurface::initCompatPrimarySurface"); +} + +template +void CompatDirectDrawSurface::restorePrimaryCaps(TDdsCaps& caps) +{ + caps.dwCaps ^= DDSCAPS_OFFSCREENPLAIN; + caps.dwCaps |= DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE; +} + +template +void CompatDirectDrawSurface::updateSurfaceParams() +{ + TSurfaceDesc desc = {}; + desc.dwSize = sizeof(desc); + if (SUCCEEDED(s_origVtable.Lock(s_compatPrimarySurface, nullptr, &desc, DDLOCK_WAIT, nullptr))) + { + s_origVtable.Unlock(s_compatPrimarySurface, nullptr); + CompatPrimarySurface::pitch = desc.lPitch; + CompatPrimarySurface::surfacePtr = desc.lpSurface; + } +} + +template +TSurface* CompatDirectDrawSurface::s_compatPrimarySurface = nullptr; + +template <> const IID& CompatDirectDrawSurface::s_iid = IID_IDirectDrawSurface; +template <> const IID& CompatDirectDrawSurface::s_iid = IID_IDirectDrawSurface2; +template <> const IID& CompatDirectDrawSurface::s_iid = IID_IDirectDrawSurface3; +template <> const IID& CompatDirectDrawSurface::s_iid = IID_IDirectDrawSurface4; +template <> const IID& CompatDirectDrawSurface::s_iid = IID_IDirectDrawSurface7; + +template CompatDirectDrawSurface; +template CompatDirectDrawSurface; +template CompatDirectDrawSurface; +template CompatDirectDrawSurface; +template CompatDirectDrawSurface; + +template HRESULT CompatDirectDrawSurface::createCompatPrimarySurface( + IDirectDraw& dd, + TSurfaceDesc compatDesc, + IDirectDrawSurface*& compatSurface); +template HRESULT CompatDirectDrawSurface::createCompatPrimarySurface( + IDirectDraw2& dd, + TSurfaceDesc compatDesc, + IDirectDrawSurface*& compatSurface); +template HRESULT CompatDirectDrawSurface::createCompatPrimarySurface( + IDirectDraw4& dd, + TSurfaceDesc compatDesc, + IDirectDrawSurface4*& compatSurface); +template HRESULT CompatDirectDrawSurface::createCompatPrimarySurface( + IDirectDraw7& dd, + TSurfaceDesc compatDesc, + IDirectDrawSurface7*& compatSurface); diff --git a/DDrawCompat/CompatDirectDrawSurface.h b/DDrawCompat/CompatDirectDrawSurface.h new file mode 100644 index 0000000..93707cb --- /dev/null +++ b/DDrawCompat/CompatDirectDrawSurface.h @@ -0,0 +1,72 @@ +#pragma once + +#include "CompatVtable.h" +#include "DDrawTypes.h" +#include "DirectDrawSurfaceVtblVisitor.h" + +template +class CompatDirectDrawSurface : public CompatVtable, TSurface> +{ +public: + typedef typename Types::TSurfaceDesc TSurfaceDesc; + typedef typename Types::TDdsCaps TDdsCaps; + typedef typename Types::TUnlockParam TUnlockParam; + + static void setCompatVtable(Vtable& vtable); + + template + static HRESULT createCompatPrimarySurface( + TDirectDraw& dd, + TSurfaceDesc compatDesc, + TSurface*& compatSurface); + + static void fixSurfacePtrs(TSurface& surface); + static void initPrimarySurfacePtr(const GUID& guid, IUnknown& surface); + static void updateSurfaceParams(); + + static HRESULT STDMETHODCALLTYPE Blt( + TSurface* This, + LPRECT lpDestRect, + TSurface* lpDDSrcSurface, + LPRECT lpSrcRect, + DWORD dwFlags, + LPDDBLTFX lpDDBltFx); + + static HRESULT STDMETHODCALLTYPE BltFast( + TSurface* This, + DWORD dwX, + DWORD dwY, + TSurface* lpDDSrcSurface, + LPRECT lpSrcRect, + DWORD dwTrans); + + static HRESULT STDMETHODCALLTYPE Flip( + TSurface* This, + TSurface* lpDDSurfaceTargetOverride, + DWORD dwFlags); + + static HRESULT STDMETHODCALLTYPE GetCaps(TSurface* This, TDdsCaps* lpDDSCaps); + static HRESULT STDMETHODCALLTYPE GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc); + static HRESULT STDMETHODCALLTYPE IsLost(TSurface* This); + + static HRESULT STDMETHODCALLTYPE Lock( + TSurface* This, + LPRECT lpDestRect, + TSurfaceDesc* lpDDSurfaceDesc, + DWORD dwFlags, + HANDLE hEvent); + + static HRESULT STDMETHODCALLTYPE QueryInterface(TSurface* This, REFIID riid, LPVOID* obp); + static HRESULT STDMETHODCALLTYPE ReleaseDC(TSurface* This, HDC hDC); + static HRESULT STDMETHODCALLTYPE Restore(TSurface* This); + static HRESULT STDMETHODCALLTYPE SetClipper(TSurface* This, LPDIRECTDRAWCLIPPER lpDDClipper); + static HRESULT STDMETHODCALLTYPE SetPalette(TSurface* This, LPDIRECTDRAWPALETTE lpDDPalette); + static HRESULT STDMETHODCALLTYPE Unlock(TSurface* This, TUnlockParam lpRect); + + static const IID& s_iid; + +private: + static void initCompatPrimarySurface(); + static void restorePrimaryCaps(TDdsCaps& caps); + static TSurface* s_compatPrimarySurface; +}; diff --git a/DDrawCompat/CompatGdiSurface.cpp b/DDrawCompat/CompatGdiSurface.cpp new file mode 100644 index 0000000..eb2d23c --- /dev/null +++ b/DDrawCompat/CompatGdiSurface.cpp @@ -0,0 +1,537 @@ +#define CINTERFACE +#define WIN32_LEAN_AND_MEAN + +#include + +#include +#include +#include + +#include "CompatDirectDraw.h" +#include "CompatDirectDrawSurface.h" +#include "CompatGdiSurface.h" +#include "CompatPrimarySurface.h" +#include "DDrawLog.h" +#include "DDrawProcs.h" +#include "RealPrimarySurface.h" + +namespace +{ + struct CaretData + { + HWND hwnd; + long left; + long top; + long width; + long height; + bool isDrawn; + }; + + struct GdiSurface + { + IDirectDrawSurface7* surface; + HDC origDc; + }; + + struct ExcludeClipRectsData + { + HDC compatDc; + POINT clientOrigin; + HWND rootWnd; + }; + + bool g_suppressGdiHooks = false; + + class HookRecursionGuard + { + public: + HookRecursionGuard() + { + g_suppressGdiHooks = true; + } + + ~HookRecursionGuard() + { + g_suppressGdiHooks = false; + } + }; + + auto g_origGetDc = &GetDC; + auto g_origGetDcEx = &GetDCEx; + auto g_origGetWindowDc = &GetWindowDC; + auto g_origReleaseDc = &ReleaseDC; + auto g_origBeginPaint = &BeginPaint; + auto g_origEndPaint = &EndPaint; + + auto g_origCreateCaret = &CreateCaret; + auto g_origShowCaret = &ShowCaret; + auto g_origHideCaret = &HideCaret; + + IDirectDraw7* g_directDraw = nullptr; + std::unordered_map g_dcToSurface; + + CaretData g_caret = {}; + + POINT getClientOrigin(HWND hwnd); + HDC getCompatDc(HWND hwnd, HDC origDc, const POINT& origin); + HDC releaseCompatDc(HDC hdc); + + LRESULT CALLBACK callWndRetProc(int nCode, WPARAM wParam, LPARAM lParam) + { + if (HC_ACTION == nCode) + { + auto ret = reinterpret_cast(lParam); + if (WM_WINDOWPOSCHANGED == ret->message) + { + InvalidateRect(nullptr, nullptr, TRUE); + } + else if (WM_ERASEBKGND == ret->message && ret->lResult) + { + HDC origDc = reinterpret_cast(ret->wParam); + if (g_dcToSurface.find(origDc) == g_dcToSurface.end()) + { + HWND hwnd = WindowFromDC(origDc); + POINT origin = {}; + ClientToScreen(hwnd, &origin); + + HDC compatDc = getCompatDc(hwnd, origDc, origin); + if (compatDc != origDc) + { + SendMessage(hwnd, WM_ERASEBKGND, reinterpret_cast(compatDc), 0); + releaseCompatDc(compatDc); + } + } + } + } + + return CallNextHookEx(nullptr, nCode, wParam, lParam); + } + + IDirectDraw7* createDirectDraw() + { + IDirectDraw7* dd = nullptr; + CALL_ORIG_DDRAW(DirectDrawCreateEx, nullptr, reinterpret_cast(&dd), IID_IDirectDraw7, nullptr); + if (!dd) + { + Compat::Log() << "Failed to create a DirectDraw interface for GDI"; + return nullptr; + } + + if (FAILED(CompatDirectDraw::s_origVtable.SetCooperativeLevel(dd, nullptr, DDSCL_NORMAL))) + { + Compat::Log() << "Failed to set the cooperative level on the DirectDraw interface for GDI"; + dd->lpVtbl->Release(dd); + return nullptr; + } + + return dd; + } + + IDirectDrawSurface7* createGdiSurface() + { + DDSURFACEDESC2 desc = {}; + desc.dwSize = sizeof(desc); + desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS | DDSD_PITCH | DDSD_LPSURFACE; + desc.dwWidth = CompatPrimarySurface::width; + desc.dwHeight = CompatPrimarySurface::height; + desc.ddpfPixelFormat = CompatPrimarySurface::pixelFormat; + desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; + desc.lPitch = CompatPrimarySurface::pitch; + desc.lpSurface = CompatPrimarySurface::surfacePtr; + + IDirectDrawSurface7* surface = nullptr; + HRESULT result = CompatDirectDraw::s_origVtable.CreateSurface( + g_directDraw, &desc, &surface, nullptr); + if (FAILED(result)) + { + LOG_ONCE("Failed to create a GDI surface: " << result); + return nullptr; + } + + if (CompatPrimarySurface::palette) + { + CompatDirectDrawSurface::s_origVtable.SetPalette( + surface, CompatPrimarySurface::palette); + } + + return surface; + } + + BOOL CALLBACK excludeClipRectsForOverlappingWindows(HWND hwnd, LPARAM lParam) + { + auto excludeClipRectsData = reinterpret_cast(lParam); + if (hwnd == excludeClipRectsData->rootWnd) + { + return FALSE; + } + + RECT rect = {}; + GetWindowRect(hwnd, &rect); + OffsetRect(&rect, -excludeClipRectsData->clientOrigin.x, -excludeClipRectsData->clientOrigin.y); + ExcludeClipRect(excludeClipRectsData->compatDc, rect.left, rect.top, rect.right, rect.bottom); + return TRUE; + } + + POINT getClientOrigin(HWND hwnd) + { + POINT origin = {}; + if (hwnd) + { + ClientToScreen(hwnd, &origin); + } + return origin; + } + + HDC getCompatDc(HWND hwnd, HDC origDc, const POINT& origin) + { + if (!CompatPrimarySurface::surfacePtr || !origDc || !RealPrimarySurface::isFullScreen() || g_suppressGdiHooks) + { + return origDc; + } + + HookRecursionGuard recursionGuard; + + IDirectDrawSurface7* surface = createGdiSurface(); + if (!surface) + { + return origDc; + } + + HDC compatDc = nullptr; + HRESULT result = surface->lpVtbl->GetDC(surface, &compatDc); + if (FAILED(result)) + { + LOG_ONCE("Failed to create a GDI DC: " << result); + surface->lpVtbl->Release(surface); + return origDc; + } + + if (hwnd) + { + SetWindowOrgEx(compatDc, -origin.x, -origin.y, nullptr); + + HRGN clipRgn = CreateRectRgn(0, 0, 0, 0); + GetRandomRgn(origDc, clipRgn, SYSRGN); + SelectClipRgn(compatDc, clipRgn); + RECT r = {}; + GetRgnBox(clipRgn, &r); + DeleteObject(clipRgn); + + ExcludeClipRectsData excludeClipRectsData = { compatDc, origin, GetAncestor(hwnd, GA_ROOT) }; + EnumThreadWindows(GetCurrentThreadId(), &excludeClipRectsForOverlappingWindows, + reinterpret_cast(&excludeClipRectsData)); + } + + GdiSurface gdiSurface = { surface, origDc }; + g_dcToSurface[compatDc] = gdiSurface; + + // Release DD critical section acquired by IDirectDrawSurface7::GetDC to avoid deadlocks + Compat::origProcs.ReleaseDDThreadLock(); + + return compatDc; + } + + FARPROC getProcAddress(HMODULE module, const char* procName) + { + if (!module || !procName) + { + return nullptr; + } + + PIMAGE_DOS_HEADER dosHeader = reinterpret_cast(module); + if (IMAGE_DOS_SIGNATURE != dosHeader->e_magic) { + return nullptr; + } + char* moduleBase = reinterpret_cast(module); + + PIMAGE_NT_HEADERS ntHeader = reinterpret_cast( + reinterpret_cast(dosHeader) + dosHeader->e_lfanew); + if (IMAGE_NT_SIGNATURE != ntHeader->Signature) + { + return nullptr; + } + + PIMAGE_EXPORT_DIRECTORY exportDir = reinterpret_cast( + moduleBase + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + + DWORD* rvaOfNames = reinterpret_cast(moduleBase + exportDir->AddressOfNames); + + for (DWORD i = 0; i < exportDir->NumberOfNames; ++i) + { + if (0 == strcmp(procName, moduleBase + rvaOfNames[i])) + { + WORD* nameOrds = reinterpret_cast(moduleBase + exportDir->AddressOfNameOrdinals); + DWORD* rvaOfFunctions = reinterpret_cast(moduleBase + exportDir->AddressOfFunctions); + return reinterpret_cast(moduleBase + rvaOfFunctions[nameOrds[i]]); + } + } + + return nullptr; + } + + POINT getWindowOrigin(HWND hwnd) + { + POINT origin = {}; + if (hwnd) + { + RECT windowRect = {}; + GetWindowRect(hwnd, &windowRect); + origin.x = windowRect.left; + origin.y = windowRect.top; + } + return origin; + } + + template + void hookGdiFunction(const char* funcName, FuncPtr& origFuncPtr, FuncPtr newFuncPtr) + { + origFuncPtr = reinterpret_cast(getProcAddress(GetModuleHandle("user32"), funcName)); + DetourAttach(reinterpret_cast(&origFuncPtr), newFuncPtr); + } + + HDC releaseCompatDc(HDC hdc) + { + if (g_suppressGdiHooks) + { + return hdc; + } + + HookRecursionGuard recursionGuard; + + auto it = g_dcToSurface.find(hdc); + + if (it != g_dcToSurface.end()) + { + // Reacquire DD critical section that was temporarily released after IDirectDrawSurface7::GetDC + Compat::origProcs.AcquireDDThreadLock(); + + if (FAILED(it->second.surface->lpVtbl->ReleaseDC(it->second.surface, hdc))) + { + Compat::origProcs.ReleaseDDThreadLock(); + } + + it->second.surface->lpVtbl->Release(it->second.surface); + + HDC origDc = it->second.origDc; + g_dcToSurface.erase(it); + + RealPrimarySurface::update(); + return origDc; + } + + return hdc; + } + + void drawCaret() + { + HDC dc = GetDC(g_caret.hwnd); + PatBlt(dc, g_caret.left, g_caret.top, g_caret.width, g_caret.height, PATINVERT); + ReleaseDC(g_caret.hwnd, dc); + } + + LONG getCaretState(IAccessible* accessible) + { + VARIANT varChild = {}; + varChild.vt = VT_I4; + varChild.lVal = CHILDID_SELF; + VARIANT varState = {}; + accessible->lpVtbl->get_accState(accessible, varChild, &varState); + return varState.lVal; + } + + void CALLBACK caretDestroyEvent( + HWINEVENTHOOK /*hWinEventHook*/, + DWORD /*event*/, + HWND hwnd, + LONG idObject, + LONG idChild, + DWORD /*dwEventThread*/, + DWORD /*dwmsEventTime*/) + { + if (OBJID_CARET != idObject || !g_caret.isDrawn || g_caret.hwnd != hwnd) + { + return; + } + + IAccessible* accessible = nullptr; + VARIANT varChild = {}; + AccessibleObjectFromEvent(hwnd, idObject, idChild, &accessible, &varChild); + if (accessible) + { + if (STATE_SYSTEM_INVISIBLE == getCaretState(accessible)) + { + drawCaret(); + g_caret.isDrawn = false; + } + accessible->lpVtbl->Release(accessible); + } + } + + HDC WINAPI getDc(HWND hWnd) + { + Compat::origProcs.AcquireDDThreadLock(); + Compat::LogEnter("GetDC", hWnd); + + HDC compatDc = getCompatDc(hWnd, g_origGetDc(hWnd), getClientOrigin(hWnd)); + + Compat::LogLeave("GetDC", hWnd) << compatDc; + Compat::origProcs.ReleaseDDThreadLock(); + return compatDc; + } + + HDC WINAPI getDcEx(HWND hWnd, HRGN hrgnClip, DWORD flags) + { + Compat::origProcs.AcquireDDThreadLock(); + Compat::LogEnter("GetDCEx", hWnd); + + HDC compatDc = getCompatDc(hWnd, g_origGetDcEx(hWnd, hrgnClip, flags), + flags & (DCX_WINDOW | DCX_PARENTCLIP) ? getWindowOrigin(hWnd) : getClientOrigin(hWnd)); + + Compat::LogLeave("GetDCEx", hWnd) << compatDc; + Compat::origProcs.ReleaseDDThreadLock(); + return compatDc; + } + + HDC WINAPI getWindowDc(HWND hWnd) + { + Compat::origProcs.AcquireDDThreadLock(); + Compat::LogEnter("GetWindowDC", hWnd); + + HDC compatDc = getCompatDc(hWnd, g_origGetWindowDc(hWnd), getWindowOrigin(hWnd)); + + Compat::LogLeave("GetWindowDC", hWnd) << compatDc; + Compat::origProcs.ReleaseDDThreadLock(); + return compatDc; + } + + int WINAPI releaseDc(HWND hWnd, HDC hDC) + { + Compat::origProcs.AcquireDDThreadLock(); + Compat::LogEnter("ReleaseDC", hWnd, hDC); + + int result = g_origReleaseDc(hWnd, releaseCompatDc(hDC)); + + Compat::LogLeave("ReleaseDC", hWnd, hDC) << result; + Compat::origProcs.ReleaseDDThreadLock(); + return result; + } + + HDC WINAPI beginPaint(HWND hWnd, LPPAINTSTRUCT lpPaint) + { + Compat::origProcs.AcquireDDThreadLock(); + Compat::LogEnter("BeginPaint", hWnd, lpPaint); + + HDC compatDc = getCompatDc(hWnd, g_origBeginPaint(hWnd, lpPaint), getClientOrigin(hWnd)); + lpPaint->hdc = compatDc; + + Compat::LogLeave("BeginPaint", hWnd, lpPaint) << compatDc; + Compat::origProcs.ReleaseDDThreadLock(); + return compatDc; + } + + BOOL WINAPI endPaint(HWND hWnd, const PAINTSTRUCT* lpPaint) + { + Compat::origProcs.AcquireDDThreadLock(); + Compat::LogEnter("EndPaint", hWnd, lpPaint); + + BOOL result = FALSE; + if (lpPaint) + { + PAINTSTRUCT paint = *lpPaint; + paint.hdc = releaseCompatDc(lpPaint->hdc); + result = g_origEndPaint(hWnd, &paint); + } + else + { + result = g_origEndPaint(hWnd, lpPaint); + } + + Compat::LogLeave("EndPaint", hWnd, lpPaint) << result; + Compat::origProcs.ReleaseDDThreadLock(); + return result; + } + + BOOL WINAPI createCaret(HWND hWnd, HBITMAP hBitmap, int nWidth, int nHeight) + { + BOOL result = g_origCreateCaret(hWnd, hBitmap, nWidth, nHeight); + if (result) + { + if (g_caret.isDrawn) + { + drawCaret(); + g_caret.isDrawn = false; + } + g_caret.width = nWidth ? nWidth : GetSystemMetrics(SM_CXBORDER); + g_caret.height = nHeight ? nHeight : GetSystemMetrics(SM_CYBORDER); + } + return result; + } + + BOOL WINAPI showCaret(HWND hWnd) + { + BOOL result = g_origShowCaret(hWnd); + if (result && !g_caret.isDrawn) + { + IAccessible* accessible = nullptr; + AccessibleObjectFromWindow(hWnd, static_cast(OBJID_CARET), IID_IAccessible, + reinterpret_cast(&accessible)); + if (accessible) + { + if (0 == getCaretState(accessible)) + { + POINT caretPos = {}; + GetCaretPos(&caretPos); + g_caret.left = caretPos.x; + g_caret.top = caretPos.y; + g_caret.hwnd = hWnd; + drawCaret(); + g_caret.isDrawn = true; + } + accessible->lpVtbl->Release(accessible); + } + } + return result; + } + + BOOL WINAPI hideCaret(HWND hWnd) + { + BOOL result = g_origHideCaret(hWnd); + if (result && g_caret.isDrawn) + { + drawCaret(); + g_caret.isDrawn = false; + } + return result; + } +} + +void CompatGdiSurface::hookGdi() +{ + static bool alreadyHooked = false; + if (alreadyHooked) + { + return; + } + + g_directDraw = createDirectDraw(); + if (g_directDraw) + { + DetourTransactionBegin(); + hookGdiFunction("GetDC", g_origGetDc, &getDc); + hookGdiFunction("GetDCEx", g_origGetDcEx, &getDcEx); + hookGdiFunction("GetWindowDC", g_origGetWindowDc, &getWindowDc); + hookGdiFunction("ReleaseDC", g_origReleaseDc, &releaseDc); + hookGdiFunction("BeginPaint", g_origBeginPaint, &beginPaint); + hookGdiFunction("EndPaint", g_origEndPaint, &endPaint); + hookGdiFunction("CreateCaret", g_origCreateCaret, &createCaret); + hookGdiFunction("ShowCaret", g_origShowCaret, &showCaret); + hookGdiFunction("HideCaret", g_origHideCaret, &hideCaret); + DetourTransactionCommit(); + + DWORD threadId = GetCurrentThreadId(); + SetWindowsHookEx(WH_CALLWNDPROCRET, callWndRetProc, nullptr, threadId); + SetWinEventHook(EVENT_OBJECT_DESTROY, EVENT_OBJECT_DESTROY, + nullptr, &caretDestroyEvent, 0, threadId, WINEVENT_OUTOFCONTEXT); + } + alreadyHooked = true; +} diff --git a/DDrawCompat/CompatGdiSurface.h b/DDrawCompat/CompatGdiSurface.h new file mode 100644 index 0000000..b746496 --- /dev/null +++ b/DDrawCompat/CompatGdiSurface.h @@ -0,0 +1,7 @@ +#pragma once + +class CompatGdiSurface +{ +public: + static void hookGdi(); +}; diff --git a/DDrawCompat/CompatPrimarySurface.cpp b/DDrawCompat/CompatPrimarySurface.cpp new file mode 100644 index 0000000..7bd4a28 --- /dev/null +++ b/DDrawCompat/CompatPrimarySurface.cpp @@ -0,0 +1,51 @@ +#include "CompatDirectDraw.h" +#include "CompatPrimarySurface.h" +#include "IReleaseNotifier.h" +#include "RealPrimarySurface.h" + +namespace +{ + void onRelease() + { + CompatPrimarySurface::surface = nullptr; + CompatPrimarySurface::palette = nullptr; + CompatPrimarySurface::width = 0; + CompatPrimarySurface::height = 0; + ZeroMemory(&CompatPrimarySurface::pixelFormat, sizeof(CompatPrimarySurface::pixelFormat)); + CompatPrimarySurface::pitch = 0; + CompatPrimarySurface::surfacePtr = nullptr; + + RealPrimarySurface::release(); + } +} + +namespace CompatPrimarySurface +{ + template + DisplayMode getDisplayMode(TDirectDraw& dd) + { + DisplayMode dm = {}; + typename CompatDirectDraw::TSurfaceDesc desc = {}; + desc.dwSize = sizeof(desc); + CompatDirectDraw::s_origVtable.GetDisplayMode(&dd, &desc); + dm.width = desc.dwWidth; + dm.height = desc.dwHeight; + dm.pixelFormat = desc.ddpfPixelFormat; + return dm; + } + + template DisplayMode getDisplayMode(IDirectDraw& dd); + template DisplayMode getDisplayMode(IDirectDraw2& dd); + template DisplayMode getDisplayMode(IDirectDraw4& dd); + template DisplayMode getDisplayMode(IDirectDraw7& dd); + + DisplayMode displayMode = {}; + IDirectDrawSurface7* surface = nullptr; + LPDIRECTDRAWPALETTE palette = nullptr; + LONG width = 0; + LONG height = 0; + DDPIXELFORMAT pixelFormat = {}; + LONG pitch = 0; + void* surfacePtr = nullptr; + IReleaseNotifier releaseNotifier(onRelease); +} diff --git a/DDrawCompat/CompatPrimarySurface.h b/DDrawCompat/CompatPrimarySurface.h new file mode 100644 index 0000000..68f3c68 --- /dev/null +++ b/DDrawCompat/CompatPrimarySurface.h @@ -0,0 +1,30 @@ +#pragma once + +#define CINTERFACE + +#include + +class IReleaseNotifier; + +namespace CompatPrimarySurface +{ + struct DisplayMode + { + LONG width; + LONG height; + DDPIXELFORMAT pixelFormat; + }; + + template + DisplayMode getDisplayMode(TDirectDraw& dd); + + extern DisplayMode displayMode; + extern IDirectDrawSurface7* surface; + extern LPDIRECTDRAWPALETTE palette; + extern LONG width; + extern LONG height; + extern DDPIXELFORMAT pixelFormat; + extern LONG pitch; + extern void* surfacePtr; + extern IReleaseNotifier releaseNotifier; +} diff --git a/DDrawCompat/CompatVtable.cpp b/DDrawCompat/CompatVtable.cpp new file mode 100644 index 0000000..1cc4233 --- /dev/null +++ b/DDrawCompat/CompatVtable.cpp @@ -0,0 +1,6 @@ +#include "CompatVtable.h" + +namespace Compat +{ + std::map detouredMethods; +} diff --git a/DDrawCompat/CompatVtable.h b/DDrawCompat/CompatVtable.h new file mode 100644 index 0000000..f9af4e8 --- /dev/null +++ b/DDrawCompat/CompatVtable.h @@ -0,0 +1,175 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include + +#include +#include + +#include "DDrawLog.h" +#include "DDrawVtableVisitor.h" + +template +using Vtable = typename std::remove_pointer::type; + +namespace Compat +{ + struct DetouredMethodInfo + { + DetouredMethodInfo(void*& updatedOrigMethodPtr, std::map& vtablePtrToCompatVtable) + : updatedOrigMethodPtr(updatedOrigMethodPtr), vtablePtrToCompatVtable(vtablePtrToCompatVtable) + { + } + + void*& updatedOrigMethodPtr; + std::map& vtablePtrToCompatVtable; + }; + + extern std::map detouredMethods; +} + +template +class CompatVtable +{ +public: + typedef Interface Interface; + + static void hookVtable(Interface& intf) + { + static bool isInitialized = false; + if (!isInitialized) + { + isInitialized = true; + + s_vtablePtr = intf.lpVtbl; + s_origVtable = *intf.lpVtbl; + + DetourTransactionBegin(); + InitVisitor visitor; + forEach>(visitor); + DetourTransactionCommit(); + } + } + + static Vtable s_origVtable; + +private: + class InitVisitor + { + public: + template + void visit(const std::string& vtableTypeName, const std::string& funcName) + { + s_funcNames[getKey()] = vtableTypeName + "::" + funcName; + + if (!(s_compatVtable.*ptr)) + { + s_compatVtable.*ptr = s_origVtable.*ptr; + s_threadSafeVtable.*ptr = s_origVtable.*ptr; + } + else + { + s_threadSafeVtable.*ptr = getThreadSafeFuncPtr(s_compatVtable.*ptr); + hookMethod(reinterpret_cast(s_origVtable.*ptr), s_threadSafeVtable.*ptr); + } + } + + private: + template + using FuncPtr = Result(STDMETHODCALLTYPE *)(Params...); + + template + static std::vector getKey() + { + MemberDataPtr mp = ptr; + unsigned char* p = reinterpret_cast(&mp); + return std::vector(p, p + sizeof(mp)); + } + + template + static FuncPtr getThreadSafeFuncPtr(FuncPtr) + { + return &threadSafeFunc; + } + + void hookMethod(void*& origMethodPtr, void* newMethodPtr) + { + auto it = Compat::detouredMethods.find(origMethodPtr); + if (it != Compat::detouredMethods.end()) + { + origMethodPtr = it->second.updatedOrigMethodPtr; + it->second.vtablePtrToCompatVtable[s_vtablePtr] = &s_compatVtable; + } + else + { + DetourAttach(&origMethodPtr, newMethodPtr); + s_vtablePtrToCompatVtable[s_vtablePtr] = &s_compatVtable; + Compat::detouredMethods.emplace(origMethodPtr, + Compat::DetouredMethodInfo(origMethodPtr, s_vtablePtrToCompatVtable)); + } + } + + template + static Result STDMETHODCALLTYPE threadSafeFunc(IntfPtr This, Params... params) + { + Compat::origProcs.AcquireDDThreadLock(); + Compat::LogEnter(s_funcNames[getKey()].c_str(), This, params...); + + Result result; + auto it = s_vtablePtrToCompatVtable.find(This->lpVtbl); + if (it != s_vtablePtrToCompatVtable.end()) + { + Vtable& compatVtable = *static_cast*>(it->second); + result = (compatVtable.*ptr)(This, params...); + } + else + { + result = (s_origVtable.*ptr)(This, params...); + } + + Compat::origProcs.ReleaseDDThreadLock(); + Compat::LogLeave(s_funcNames[getKey()].c_str(), This, params...) << result; + return result; + } + }; + + static Vtable createCompatVtable() + { + Vtable vtable = {}; + CompatInterface::setCompatVtable(vtable); + return vtable; + } + + static Vtable& getCompatVtable() + { + static Vtable vtable(createCompatVtable()); + return vtable; + } + + static Vtable* s_vtablePtr; + static Vtable s_compatVtable; + static Vtable s_threadSafeVtable; + static std::map s_vtablePtrToCompatVtable; + static std::map, std::string> s_funcNames; +}; + +template +Vtable* CompatVtable::s_vtablePtr = nullptr; + +template +Vtable CompatVtable::s_origVtable = {}; + +template +Vtable CompatVtable::s_compatVtable(CompatInterface::getCompatVtable()); + +template +Vtable CompatVtable::s_threadSafeVtable = {}; + +template +std::map CompatVtable::s_vtablePtrToCompatVtable; + +template +std::map, std::string> CompatVtable::s_funcNames; diff --git a/DDrawCompat/Config.h b/DDrawCompat/Config.h new file mode 100644 index 0000000..ffa2da1 --- /dev/null +++ b/DDrawCompat/Config.h @@ -0,0 +1,10 @@ +#pragma once + +typedef unsigned long DWORD; + +namespace Config +{ + const DWORD minRefreshInterval = 1000 / 60; + const DWORD minRefreshIntervalAfterFlip = 1000 / 10; + const DWORD minPaletteUpdateInterval = 1000 / 60; +} diff --git a/DDrawCompat/DDrawCompat.def b/DDrawCompat/DDrawCompat.def new file mode 100644 index 0000000..bd7171c --- /dev/null +++ b/DDrawCompat/DDrawCompat.def @@ -0,0 +1,26 @@ +LIBRARY ddraw + +EXPORTS + AcquireDDThreadLock + CompleteCreateSysmemSurface + D3DParseUnknownCommand + DDGetAttachedSurfaceLcl + DDInternalLock + DDInternalUnlock + DSoundHelp + DirectDrawCreate + DirectDrawCreateClipper + DirectDrawCreateEx + DirectDrawEnumerateA + DirectDrawEnumerateExA + DirectDrawEnumerateExW + DirectDrawEnumerateW + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + GetDDSurfaceLocal + GetOLEThunkData + GetSurfaceFromDC + RegisterSpecialCase + ReleaseDDThreadLock + SetAppCompatData + DirectInputCreateA diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj new file mode 100644 index 0000000..73acdd7 --- /dev/null +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -0,0 +1,184 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {1146187A-17DE-4350-B9D1-9F9EAA934908} + Win32Proj + DDrawCompat + 8.1 + + + + DynamicLibrary + true + v140 + NotSet + + + DynamicLibrary + false + v140 + NotSet + + + DynamicLibrary + true + v140 + Unicode + + + DynamicLibrary + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + ddraw + C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\include;$(IncludePath) + C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\lib.X86;$(LibraryPath) + + + true + + + ddraw + C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\include;$(IncludePath) + C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\lib.X86;$(LibraryPath) + + + false + + + + + + Level4 + WIN32;_DEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions) + MultiThreadedDebug + + + DDrawCompat.def + dxguid.lib;detours.lib;oleacc.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + _DEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + DDrawCompat.def + + + + + + + Level4 + WIN32;NDEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions) + MultiThreaded + + + DDrawCompat.def + dxguid.lib;detours.lib;oleacc.lib;%(AdditionalDependencies) + No + + + + + Level3 + + + MaxSpeed + true + true + NDEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions) + + + Windows + true + true + true + DDrawCompat.def + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters new file mode 100644 index 0000000..dbe2d1a --- /dev/null +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -0,0 +1,110 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Source Files + + + \ No newline at end of file diff --git a/DDrawCompat/DDrawLog.cpp b/DDrawCompat/DDrawLog.cpp new file mode 100644 index 0000000..ddbec78 --- /dev/null +++ b/DDrawCompat/DDrawLog.cpp @@ -0,0 +1,39 @@ +#define WIN32_LEAN_AND_MEAN + +#include + +#include "DDrawLog.h" + +std::ostream& operator<<(std::ostream& os, LPRECT rect) +{ + if (rect) + { + os << "RECT(" << rect->left << ',' << rect->top << ',' << rect->right << ',' << rect->bottom << ')'; + } + else + { + os << "NullRect"; + } + return os; +} + +namespace Compat +{ + Log::Log() + { + SYSTEMTIME st = {}; + GetLocalTime(&st); + + char time[100]; + sprintf_s(time, "%02hu:%02hu:%02hu.%03hu ", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds); + + s_logFile << GetCurrentThreadId() << " " << time; + } + + Log::~Log() + { + s_logFile << std::endl; + } + + std::ofstream Log::s_logFile("ddraw.log"); +} diff --git a/DDrawCompat/DDrawLog.h b/DDrawCompat/DDrawLog.h new file mode 100644 index 0000000..0a4bf4c --- /dev/null +++ b/DDrawCompat/DDrawLog.h @@ -0,0 +1,104 @@ +#pragma once + +#define CINTERFACE + +#include + +struct _GUID; +struct tagRECT; + +#define LOG_ONCE(msg) \ + static bool isAlreadyLogged##__LINE__ = false; \ + if (!isAlreadyLogged##__LINE__) \ + { \ + Compat::Log() << msg; \ + isAlreadyLogged##__LINE__ = true; \ + } + +inline std::ostream& operator<<(std::ostream& os, const _GUID& guid) +{ + return os << &guid; +} + +std::ostream& operator<<(std::ostream& os, tagRECT* rect); + +namespace Compat +{ + class Log + { + public: + Log(); + ~Log(); + + template + Log& operator<<(const T& t) + { + s_logFile << t; + return *this; + } + + protected: + template + Log(const char* prefix, const char* funcName, Params... params) : Log() + { + s_logFile << prefix << ' ' << funcName << '('; + toList(params...); + s_logFile << ')'; + } + + private: + void toList() + { + } + + template + void toList(Param param) + { + s_logFile << param; + } + + template + void toList(Param firstParam, Params... remainingParams) + { + s_logFile << firstParam << ", "; + toList(remainingParams...); + } + + static std::ofstream s_logFile; + }; + +#ifdef _DEBUG + class LogEnter : private Log + { + public: + template + LogEnter(const char* funcName, Params... params) : Log("-->", funcName, params...) + { + } + }; + + class LogLeave : private Log + { + public: + template + LogLeave(const char* funcName, Params... params) : Log("<--", funcName, params...) + { + } + + template + void operator<<(const Result& result) + { + static_cast(*this) << " = " << result; + } + }; +#else + class LogEnter + { + public: + template LogEnter(const char*, Params...) {} + template void operator<<(const Result&) {} + }; + + typedef LogEnter LogLeave; +#endif +} diff --git a/DDrawCompat/DDrawProcs.cpp b/DDrawCompat/DDrawProcs.cpp new file mode 100644 index 0000000..beeb103 --- /dev/null +++ b/DDrawCompat/DDrawProcs.cpp @@ -0,0 +1,6 @@ +#include "DDrawProcs.h" + +namespace Compat +{ + DDrawProcs origProcs = {}; +} diff --git a/DDrawCompat/DDrawProcs.h b/DDrawCompat/DDrawProcs.h new file mode 100644 index 0000000..3c7f026 --- /dev/null +++ b/DDrawCompat/DDrawProcs.h @@ -0,0 +1,55 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN + +#include + +#define VISIT_UNMODIFIED_DDRAW_PROCS(visit) \ + visit(AcquireDDThreadLock) \ + visit(CompleteCreateSysmemSurface) \ + visit(D3DParseUnknownCommand) \ + visit(DDGetAttachedSurfaceLcl) \ + visit(DDInternalLock) \ + visit(DDInternalUnlock) \ + visit(DSoundHelp) \ + visit(DirectDrawCreateClipper) \ + visit(DirectDrawEnumerateA) \ + visit(DirectDrawEnumerateExA) \ + visit(DirectDrawEnumerateExW) \ + visit(DirectDrawEnumerateW) \ + visit(DllCanUnloadNow) \ + visit(DllGetClassObject) \ + visit(GetDDSurfaceLocal) \ + visit(GetOLEThunkData) \ + visit(GetSurfaceFromDC) \ + visit(RegisterSpecialCase) \ + visit(ReleaseDDThreadLock) \ + visit(SetAppCompatData) + +#define VISIT_MODIFIED_DDRAW_PROCS(visit) \ + visit(DirectDrawCreate) \ + visit(DirectDrawCreateEx) + +#define VISIT_ALL_DDRAW_PROCS(visit) \ + VISIT_UNMODIFIED_DDRAW_PROCS(visit) \ + VISIT_MODIFIED_DDRAW_PROCS(visit) + +#define ADD_FARPROC_MEMBER(memberName) FARPROC memberName; + +namespace Compat +{ + struct DDrawProcs + { + VISIT_ALL_DDRAW_PROCS(ADD_FARPROC_MEMBER); + FARPROC DirectInputCreateA; + }; + + extern DDrawProcs origProcs; +} + +#undef ADD_FARPROC_MEMBER + +#define CALL_ORIG_DDRAW(procName, ...) \ + (Compat::origProcs.procName ? \ + reinterpret_cast(Compat::origProcs.procName)(__VA_ARGS__) : \ + E_FAIL) diff --git a/DDrawCompat/DDrawTypes.h b/DDrawCompat/DDrawTypes.h new file mode 100644 index 0000000..12217d1 --- /dev/null +++ b/DDrawCompat/DDrawTypes.h @@ -0,0 +1,79 @@ +#pragma once + +#define CINTERFACE + +#include + +struct DDrawTypes +{ + typedef IDirectDrawSurface TSurface; + typedef IDirectDrawSurface TCreatedSurface; + typedef DDSURFACEDESC TSurfaceDesc; + typedef DDSCAPS TDdsCaps; + typedef LPDDENUMSURFACESCALLBACK TEnumSurfacesCallbackPtr; + typedef LPVOID TUnlockParam; +}; + +struct DDrawTypes2 +{ + typedef IDirectDrawSurface2 TSurface; + typedef IDirectDrawSurface TCreatedSurface; + typedef DDSURFACEDESC TSurfaceDesc; + typedef DDSCAPS TDdsCaps; + typedef LPDDENUMSURFACESCALLBACK TEnumSurfacesCallbackPtr; + typedef LPVOID TUnlockParam; +}; + +struct DDrawTypes3 +{ + typedef IDirectDrawSurface3 TSurface; + typedef IDirectDrawSurface3 TCreatedSurface; + typedef DDSURFACEDESC TSurfaceDesc; + typedef DDSCAPS TDdsCaps; + typedef LPDDENUMSURFACESCALLBACK TEnumSurfacesCallbackPtr; + typedef LPVOID TUnlockParam; +}; + +struct DDrawTypes4 +{ + typedef IDirectDrawSurface4 TSurface; + typedef IDirectDrawSurface4 TCreatedSurface; + typedef DDSURFACEDESC2 TSurfaceDesc; + typedef DDSCAPS2 TDdsCaps; + typedef LPDDENUMSURFACESCALLBACK2 TEnumSurfacesCallbackPtr; + typedef LPRECT TUnlockParam; +}; + +struct DDrawTypes7 +{ + typedef IDirectDrawSurface7 TSurface; + typedef IDirectDrawSurface7 TCreatedSurface; + typedef DDSURFACEDESC2 TSurfaceDesc; + typedef DDSCAPS2 TDdsCaps; + typedef LPDDENUMSURFACESCALLBACK7 TEnumSurfacesCallbackPtr; + typedef LPRECT TUnlockParam; +}; + +template +struct Types; + +#define DD_CONCAT(x, y, ...) x##y + +#define DD_TYPES(Interface, ...) \ + template <> \ + struct Types : DD_CONCAT(DDrawTypes, __VA_ARGS__) \ + {} + +DD_TYPES(IDirectDraw); +DD_TYPES(IDirectDraw, 2); +DD_TYPES(IDirectDraw, 4); +DD_TYPES(IDirectDraw, 7); + +DD_TYPES(IDirectDrawSurface); +DD_TYPES(IDirectDrawSurface, 2); +DD_TYPES(IDirectDrawSurface, 3); +DD_TYPES(IDirectDrawSurface, 4); +DD_TYPES(IDirectDrawSurface, 7); + +#undef DD_TYPES +#undef DD_CONCAT diff --git a/DDrawCompat/DDrawVtableVisitor.h b/DDrawCompat/DDrawVtableVisitor.h new file mode 100644 index 0000000..8822d5c --- /dev/null +++ b/DDrawCompat/DDrawVtableVisitor.h @@ -0,0 +1,41 @@ +#pragma once + +#define CINTERFACE + +#include +#include + +template +struct DDrawVtableForEach; + +template +void forEach(Visitor& visitor) +{ + DDrawVtableForEach::forEach(visitor); +} + +template +std::string getTypeName() +{ + std::string typeName(typeid(T).name()); + if (0 == typeName.find("struct ")) + { + typeName = typeName.substr(typeName.find(" ") + 1); + } + return typeName; +} + +#define DD_VISIT(member) \ + visitor.visit(getTypeName(), #member) + +template <> +struct DDrawVtableForEach +{ + template + static void forEach(Visitor& visitor) + { + DD_VISIT(QueryInterface); + DD_VISIT(AddRef); + DD_VISIT(Release); + } +}; diff --git a/DDrawCompat/DirectDrawPaletteVtblVisitor.h b/DDrawCompat/DirectDrawPaletteVtblVisitor.h new file mode 100644 index 0000000..1ac1376 --- /dev/null +++ b/DDrawCompat/DirectDrawPaletteVtblVisitor.h @@ -0,0 +1,18 @@ +#pragma once + +#include "DDrawVtableVisitor.h" + +template <> +struct DDrawVtableForEach +{ + template + static void forEach(Visitor& visitor) + { + DDrawVtableForEach::forEach(visitor); + + DD_VISIT(GetCaps); + DD_VISIT(GetEntries); + DD_VISIT(Initialize); + DD_VISIT(SetEntries); + } +}; diff --git a/DDrawCompat/DirectDrawSurfaceVtblVisitor.h b/DDrawCompat/DirectDrawSurfaceVtblVisitor.h new file mode 100644 index 0000000..40f15de --- /dev/null +++ b/DDrawCompat/DirectDrawSurfaceVtblVisitor.h @@ -0,0 +1,104 @@ +#pragma once + +#include "DDrawVtableVisitor.h" + +template <> +struct DDrawVtableForEach +{ + template + static void forEach(Visitor& visitor) + { + DDrawVtableForEach::forEach(visitor); + + DD_VISIT(AddAttachedSurface); + DD_VISIT(AddOverlayDirtyRect); + DD_VISIT(Blt); + DD_VISIT(BltBatch); + DD_VISIT(BltFast); + DD_VISIT(DeleteAttachedSurface); + DD_VISIT(EnumAttachedSurfaces); + DD_VISIT(EnumOverlayZOrders); + DD_VISIT(Flip); + DD_VISIT(GetAttachedSurface); + DD_VISIT(GetBltStatus); + DD_VISIT(GetCaps); + DD_VISIT(GetClipper); + DD_VISIT(GetColorKey); + DD_VISIT(GetDC); + DD_VISIT(GetFlipStatus); + DD_VISIT(GetOverlayPosition); + DD_VISIT(GetPalette); + DD_VISIT(GetPixelFormat); + DD_VISIT(GetSurfaceDesc); + DD_VISIT(Initialize); + DD_VISIT(IsLost); + DD_VISIT(Lock); + DD_VISIT(ReleaseDC); + DD_VISIT(Restore); + DD_VISIT(SetClipper); + DD_VISIT(SetColorKey); + DD_VISIT(SetOverlayPosition); + DD_VISIT(SetPalette); + DD_VISIT(Unlock); + DD_VISIT(UpdateOverlay); + DD_VISIT(UpdateOverlayDisplay); + DD_VISIT(UpdateOverlayZOrder); + } +}; + +template <> +struct DDrawVtableForEach +{ + template + static void forEach(Visitor& visitor) + { + DDrawVtableForEach::forEach(visitor); + + DD_VISIT(GetDDInterface); + DD_VISIT(PageLock); + DD_VISIT(PageUnlock); + } +}; + +template <> +struct DDrawVtableForEach +{ + template + static void forEach(Visitor& visitor) + { + DDrawVtableForEach::forEach(visitor); + + DD_VISIT(SetSurfaceDesc); + } +}; + +template <> +struct DDrawVtableForEach +{ + template + static void forEach(Visitor& visitor) + { + DDrawVtableForEach::forEach(visitor); + + DD_VISIT(SetPrivateData); + DD_VISIT(GetPrivateData); + DD_VISIT(FreePrivateData); + DD_VISIT(GetUniquenessValue); + DD_VISIT(ChangeUniquenessValue); + } +}; + +template <> +struct DDrawVtableForEach +{ + template + static void forEach(Visitor& visitor) + { + DDrawVtableForEach::forEach(visitor); + + DD_VISIT(SetPriority); + DD_VISIT(GetPriority); + DD_VISIT(SetLOD); + DD_VISIT(GetLOD); + } +}; diff --git a/DDrawCompat/DirectDrawVtblVisitor.h b/DDrawCompat/DirectDrawVtblVisitor.h new file mode 100644 index 0000000..e1d19f9 --- /dev/null +++ b/DDrawCompat/DirectDrawVtblVisitor.h @@ -0,0 +1,74 @@ +#pragma once + +#include "DDrawVtableVisitor.h" + +template <> +struct DDrawVtableForEach +{ + template + static void forEach(Visitor& visitor) + { + DDrawVtableForEach::forEach(visitor); + + DD_VISIT(Compact); + DD_VISIT(CreateClipper); + DD_VISIT(CreatePalette); + DD_VISIT(CreateSurface); + DD_VISIT(DuplicateSurface); + DD_VISIT(EnumDisplayModes); + DD_VISIT(EnumSurfaces); + DD_VISIT(FlipToGDISurface); + DD_VISIT(GetCaps); + DD_VISIT(GetDisplayMode); + DD_VISIT(GetFourCCCodes); + DD_VISIT(GetGDISurface); + DD_VISIT(GetMonitorFrequency); + DD_VISIT(GetScanLine); + DD_VISIT(GetVerticalBlankStatus); + DD_VISIT(Initialize); + DD_VISIT(RestoreDisplayMode); + DD_VISIT(SetCooperativeLevel); + DD_VISIT(SetDisplayMode); + DD_VISIT(WaitForVerticalBlank); + } +}; + +template <> +struct DDrawVtableForEach +{ + template + static void forEach(Visitor& visitor) + { + DDrawVtableForEach::forEach(visitor); + + DD_VISIT(GetAvailableVidMem); + } +}; + +template <> +struct DDrawVtableForEach +{ + template + static void forEach(Visitor& visitor) + { + DDrawVtableForEach::forEach(visitor); + + DD_VISIT(GetSurfaceFromDC); + DD_VISIT(RestoreAllSurfaces); + DD_VISIT(TestCooperativeLevel); + DD_VISIT(GetDeviceIdentifier); + } +}; + +template <> +struct DDrawVtableForEach +{ + template + static void forEach(Visitor& visitor) + { + DDrawVtableForEach::forEach(visitor); + + DD_VISIT(StartModeTest); + DD_VISIT(EvaluateMode); + } +}; diff --git a/DDrawCompat/DllMain.cpp b/DDrawCompat/DllMain.cpp new file mode 100644 index 0000000..5854170 --- /dev/null +++ b/DDrawCompat/DllMain.cpp @@ -0,0 +1,207 @@ +#include + +#include "CompatDirectDraw.h" +#include "CompatDirectDrawSurface.h" +#include "CompatDirectDrawPalette.h" +#include "CompatGdiSurface.h" +#include "CompatVtable.h" +#include "DDrawProcs.h" + +struct IDirectInput; + +namespace +{ + HMODULE g_origDDrawModule = nullptr; + HMODULE g_origDInputModule = nullptr; + + template + void hookVtable(const GUID& guid, IUnknown& unk) + { + typename CompatInterface::Interface* intf = nullptr; + if (SUCCEEDED(unk.lpVtbl->QueryInterface(&unk, guid, reinterpret_cast(&intf)))) + { + CompatInterface::hookVtable(*intf); + intf->lpVtbl->Release(intf); + } + } + + void hookDirectDraw(IDirectDraw& dd) + { + IUnknown& ddUnk = reinterpret_cast(dd); + hookVtable>(IID_IDirectDraw, ddUnk); + hookVtable>(IID_IDirectDraw2, ddUnk); + hookVtable>(IID_IDirectDraw4, ddUnk); + hookVtable>(IID_IDirectDraw7, ddUnk); + } + + void hookDirectDrawSurface(IDirectDraw& dd) + { + DDSURFACEDESC desc = {}; + desc.dwSize = sizeof(desc); + desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; + desc.dwWidth = 1; + desc.dwHeight = 1; + desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + + IDirectDrawSurface* surface = nullptr; + if (SUCCEEDED(dd.lpVtbl->CreateSurface(&dd, &desc, &surface, nullptr))) + { + IUnknown& surfaceUnk = reinterpret_cast(*surface); + hookVtable>(IID_IDirectDrawSurface, surfaceUnk); + hookVtable>(IID_IDirectDrawSurface2, surfaceUnk); + hookVtable>(IID_IDirectDrawSurface3, surfaceUnk); + hookVtable>(IID_IDirectDrawSurface4, surfaceUnk); + hookVtable>(IID_IDirectDrawSurface7, surfaceUnk); + surface->lpVtbl->Release(surface); + } + } + + void hookDirectDrawPalette(IDirectDraw& dd) + { + PALETTEENTRY paletteEntries[2] = {}; + IDirectDrawPalette* palette = nullptr; + if (SUCCEEDED(dd.lpVtbl->CreatePalette(&dd, DDPCAPS_1BIT, paletteEntries, &palette, nullptr))) + { + CompatDirectDrawPalette::hookVtable(*palette); + palette->lpVtbl->Release(palette); + } + } + + void installHooks() + { + static bool isAlreadyInstalled = false; + if (!isAlreadyInstalled) + { + Compat::Log() << "Installing DirectDraw hooks"; + IDirectDraw* dd = nullptr; + if (SUCCEEDED(CALL_ORIG_DDRAW(DirectDrawCreate, nullptr, &dd, nullptr))) + { + dd->lpVtbl->SetCooperativeLevel(dd, nullptr, DDSCL_NORMAL); + + hookDirectDraw(*dd); + hookDirectDrawSurface(*dd); + hookDirectDrawPalette(*dd); + + Compat::Log() << "Installing GDI hooks"; + CompatGdiSurface::hookGdi(); + + dd->lpVtbl->Release(dd); + } + Compat::Log() << "Finished installing hooks"; + isAlreadyInstalled = true; + } + } + + bool loadLibrary(const std::string& systemDirectory, const std::string& dllName, HMODULE& module) + { + const std::string systemDllPath = systemDirectory + '\\' + dllName; + + module = LoadLibrary(systemDllPath.c_str()); + if (!module) + { + Compat::Log() << "Failed to load system " << dllName << " from " << systemDllPath; + return false; + } + + return true; + } + + void suppressEmulatedDirectDraw(GUID*& guid) + { + if (reinterpret_cast(DDCREATE_EMULATIONONLY) == guid) + { + LOG_ONCE("Warning: suppressed a request to create an emulated DirectDraw object"); + guid = nullptr; + } + } +} + +#define LOAD_ORIGINAL_DDRAW_PROC(procName) \ + Compat::origProcs.procName = GetProcAddress(g_origDDrawModule, #procName); + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID /*lpvReserved*/) +{ + if (fdwReason == DLL_PROCESS_ATTACH) + { + char currentDllPath[MAX_PATH] = {}; + GetModuleFileName(hinstDLL, currentDllPath, MAX_PATH); + Compat::Log() << "Loading DDrawCompat from " << currentDllPath; + + char systemDirectory[MAX_PATH] = {}; + GetSystemDirectory(systemDirectory, MAX_PATH); + + std::string systemDDrawDllPath = std::string(systemDirectory) + "\\ddraw.dll"; + if (0 == _stricmp(currentDllPath, systemDDrawDllPath.c_str())) + { + Compat::Log() << "DDrawCompat cannot be installed as the system ddraw.dll"; + return FALSE; + } + + if (!loadLibrary(systemDirectory, "ddraw.dll", g_origDDrawModule) || + !loadLibrary(systemDirectory, "dinput.dll", g_origDInputModule)) + { + return FALSE; + } + + VISIT_ALL_DDRAW_PROCS(LOAD_ORIGINAL_DDRAW_PROC); + Compat::origProcs.DirectInputCreateA = GetProcAddress(g_origDInputModule, "DirectInputCreateA"); + + SetProcessAffinityMask(GetCurrentProcess(), 1); + + if (Compat::origProcs.SetAppCompatData) + { + typedef HRESULT WINAPI SetAppCompatDataFunc(DWORD, DWORD); + auto setAppCompatData = reinterpret_cast(Compat::origProcs.SetAppCompatData); + const DWORD disableMaxWindowedMode = 12; + setAppCompatData(disableMaxWindowedMode, 0); + } + + Compat::Log() << "DDrawCompat loaded successfully"; + } + else if (fdwReason == DLL_PROCESS_DETACH) + { + FreeLibrary(g_origDInputModule); + FreeLibrary(g_origDDrawModule); + } + + return TRUE; +} + +extern "C" HRESULT WINAPI DirectDrawCreate( + GUID* lpGUID, + LPDIRECTDRAW* lplpDD, + IUnknown* pUnkOuter) +{ + Compat::LogEnter(__func__, lpGUID, lplpDD, pUnkOuter); + installHooks(); + suppressEmulatedDirectDraw(lpGUID); + HRESULT result = CALL_ORIG_DDRAW(DirectDrawCreate, lpGUID, lplpDD, pUnkOuter); + Compat::LogLeave(__func__, lpGUID, lplpDD, pUnkOuter) << result; + return result; +} + +extern "C" HRESULT WINAPI DirectDrawCreateEx( + GUID* lpGUID, + LPVOID* lplpDD, + REFIID iid, + IUnknown* pUnkOuter) +{ + Compat::LogEnter(__func__, lpGUID, lplpDD, iid, pUnkOuter); + installHooks(); + suppressEmulatedDirectDraw(lpGUID); + HRESULT result = CALL_ORIG_DDRAW(DirectDrawCreateEx, lpGUID, lplpDD, iid, pUnkOuter); + Compat::LogLeave(__func__, lpGUID, lplpDD, iid, pUnkOuter) << result; + return result; +} + +extern "C" HRESULT WINAPI DirectInputCreateA( + HINSTANCE hinst, + DWORD dwVersion, + IDirectInput** lplpDirectInput, + LPUNKNOWN punkOuter) +{ + Compat::LogEnter(__func__, hinst, dwVersion, lplpDirectInput, punkOuter); + HRESULT result = CALL_ORIG_DDRAW(DirectInputCreateA, hinst, dwVersion, lplpDirectInput, punkOuter); + Compat::LogLeave(__func__, hinst, dwVersion, lplpDirectInput, punkOuter) << result; + return result; +} diff --git a/DDrawCompat/IReleaseNotifier.cpp b/DDrawCompat/IReleaseNotifier.cpp new file mode 100644 index 0000000..12b0576 --- /dev/null +++ b/DDrawCompat/IReleaseNotifier.cpp @@ -0,0 +1,7 @@ +#include "IReleaseNotifier.h" + +#include + +// {7810158A-CB51-448A-8706-443A7DF6D4ED} +DEFINE_GUID(IID_IReleaseNotifier, + 0x7810158a, 0xcb51, 0x448a, 0x87, 0x6, 0x44, 0x3a, 0x7d, 0xf6, 0xd4, 0xed); diff --git a/DDrawCompat/IReleaseNotifier.h b/DDrawCompat/IReleaseNotifier.h new file mode 100644 index 0000000..d20acf2 --- /dev/null +++ b/DDrawCompat/IReleaseNotifier.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include + +// {7810158A-CB51-448A-8706-443A7DF6D4ED} +DEFINE_GUID(IID_IReleaseNotifier, + 0x7810158a, 0xcb51, 0x448a, 0x87, 0x6, 0x44, 0x3a, 0x7d, 0xf6, 0xd4, 0xed); + +class IReleaseNotifier +{ +public: + IReleaseNotifier(const std::function& notifyHandler) + : m_notifyHandler(notifyHandler) + { + } + + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, LPVOID*) + { + return E_NOINTERFACE; + } + + virtual ULONG STDMETHODCALLTYPE AddRef() + { + return 2; + } + + virtual ULONG STDMETHODCALLTYPE Release() + { + m_notifyHandler(); + return 0; + } + +private: + std::function m_notifyHandler; +}; diff --git a/DDrawCompat/RealPrimarySurface.cpp b/DDrawCompat/RealPrimarySurface.cpp new file mode 100644 index 0000000..ba6291f --- /dev/null +++ b/DDrawCompat/RealPrimarySurface.cpp @@ -0,0 +1,376 @@ +#include "CompatDirectDraw.h" +#include "CompatDirectDrawSurface.h" +#include "CompatGdiSurface.h" +#include "CompatPrimarySurface.h" +#include "Config.h" +#include "DDrawProcs.h" +#include "DDrawTypes.h" +#include "IReleaseNotifier.h" +#include "RealPrimarySurface.h" + +namespace +{ + void onRelease(); + void updateNow(); + + IDirectDrawSurface7* g_frontBuffer = nullptr; + IDirectDrawSurface7* g_backBuffer = nullptr; + IDirectDrawSurface7* g_paletteConverterSurface = nullptr; + IReleaseNotifier g_releaseNotifier(onRelease); + + HANDLE g_updateThread = nullptr; + HANDLE g_updateEvent = nullptr; + bool g_isFlipEvent = false; + LARGE_INTEGER g_lastUpdateTime = {}; + LARGE_INTEGER g_qpcFrequency = {}; + + bool compatBlt(IDirectDrawSurface7* dest) + { + Compat::LogEnter("RealPrimarySurface::compatBlt", dest); + + bool result = false; + const auto& origVtable = CompatDirectDrawSurface::s_origVtable; + + if (!RealPrimarySurface::isFullScreen()) + { + IDirectDrawClipper* clipper = nullptr; + if (FAILED(origVtable.GetClipper(g_frontBuffer, &clipper))) + { + return false; + } + clipper->lpVtbl->Release(clipper); + } + + if (g_paletteConverterSurface) + { + origVtable.Blt(g_paletteConverterSurface, nullptr, CompatPrimarySurface::surface, nullptr, + DDBLT_WAIT, nullptr); + + HDC destDc = nullptr; + origVtable.GetDC(dest, &destDc); + HDC converterDc = nullptr; + origVtable.GetDC(g_paletteConverterSurface, &converterDc); + + result = TRUE == BitBlt(destDc, 0, 0, + RealPrimarySurface::s_surfaceDesc.dwWidth, RealPrimarySurface::s_surfaceDesc.dwHeight, + converterDc, 0, 0, SRCCOPY); + + origVtable.ReleaseDC(g_paletteConverterSurface, converterDc); + origVtable.ReleaseDC(dest, destDc); + } + else + { + result = SUCCEEDED(origVtable.Blt( + dest, nullptr, CompatPrimarySurface::surface, nullptr, DDBLT_WAIT, nullptr)); + } + + Compat::LogLeave("RealPrimarySurface::compatBlt", dest); + return result; + } + + template + HRESULT createPaletteConverterSurface(DirectDraw& dd) + { + if (CompatPrimarySurface::displayMode.pixelFormat.dwRGBBitCount > 8 && + RealPrimarySurface::s_surfaceDesc.ddpfPixelFormat.dwRGBBitCount > 8) + { + return DD_OK; + } + + typename Types::TSurfaceDesc desc = {}; + desc.dwSize = sizeof(desc); + desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS; + desc.dwWidth = RealPrimarySurface::s_surfaceDesc.dwWidth; + desc.dwHeight = RealPrimarySurface::s_surfaceDesc.dwHeight; + desc.ddpfPixelFormat = CompatPrimarySurface::displayMode.pixelFormat; + desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; + + typename Types::TCreatedSurface* surface = nullptr; + HRESULT result = dd.lpVtbl->CreateSurface(&dd, &desc, &surface, nullptr); + if (SUCCEEDED(result)) + { + surface->lpVtbl->QueryInterface( + surface, IID_IDirectDrawSurface7, reinterpret_cast(&g_paletteConverterSurface)); + surface->lpVtbl->Release(surface); + } + + return result; + } + + DWORD getTimeElapsedInMs(const LARGE_INTEGER& time) + { + LARGE_INTEGER currentTime = {}; + QueryPerformanceCounter(¤tTime); + return static_cast((currentTime.QuadPart - time.QuadPart) * 1000 / g_qpcFrequency.QuadPart); + } + + void onRelease() + { + g_frontBuffer = nullptr; + g_backBuffer = nullptr; + g_paletteConverterSurface->lpVtbl->Release(g_paletteConverterSurface); + g_paletteConverterSurface = nullptr; + + SetEvent(g_updateEvent); + if (WAIT_TIMEOUT == WaitForSingleObject(g_updateThread, 500)) + { + TerminateThread(g_updateThread, 0); + } + g_updateThread = nullptr; + + CloseHandle(g_updateEvent); + g_updateEvent = nullptr; + + ZeroMemory(&RealPrimarySurface::s_surfaceDesc, sizeof(RealPrimarySurface::s_surfaceDesc)); + } + + void updateNow() + { + QueryPerformanceCounter(&g_lastUpdateTime); + + if (g_backBuffer) + { + if (compatBlt(g_backBuffer)) + { + g_frontBuffer->lpVtbl->Flip(g_frontBuffer, nullptr, DDFLIP_NOVSYNC | DDFLIP_WAIT); + } + } + else + { + compatBlt(g_frontBuffer); + } + } + + DWORD WINAPI updateThreadProc(LPVOID /*lpParameter*/) + { + while (true) + { + WaitForSingleObject(g_updateEvent, INFINITE); + if (!g_frontBuffer) + { + return 0; + } + + Compat::origProcs.AcquireDDThreadLock(); + ResetEvent(g_updateEvent); + + if (!g_isFlipEvent) + { + updateNow(); + } + + DWORD timeSinceLastUpdate = getTimeElapsedInMs(g_lastUpdateTime); + DWORD minRefreshInterval = g_isFlipEvent ? + Config::minRefreshIntervalAfterFlip : Config::minRefreshInterval; + DWORD minRefreshIntervalTimeout = timeSinceLastUpdate < minRefreshInterval ? + minRefreshInterval - timeSinceLastUpdate : 0; + + g_isFlipEvent = false; + Compat::origProcs.ReleaseDDThreadLock(); + + if (minRefreshIntervalTimeout) + { + Sleep(minRefreshIntervalTimeout); + } + } + } +} + +DDSURFACEDESC2 RealPrimarySurface::s_surfaceDesc = {}; + +template +HRESULT RealPrimarySurface::create(DirectDraw& dd) +{ + typename Types::TSurfaceDesc desc = {}; + desc.dwSize = sizeof(desc); + desc.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT; + desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_COMPLEX | DDSCAPS_FLIP; + desc.dwBackBufferCount = 1; + + typename Types::TCreatedSurface* surface = nullptr; + HRESULT result = CompatDirectDraw::s_origVtable.CreateSurface( + &dd, &desc, &surface, nullptr); + + bool isFlippable = true; + if (DDERR_NOEXCLUSIVEMODE == result) + { + desc.dwFlags = DDSD_CAPS; + desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + desc.dwBackBufferCount = 0; + isFlippable = false; + result = CompatDirectDraw::s_origVtable.CreateSurface( + &dd, &desc, &surface, nullptr); + } + + if (FAILED(result)) + { + Compat::Log() << "Failed to create the real primary surface"; + return result; + } + + surface->lpVtbl->QueryInterface( + surface, IID_IDirectDrawSurface7, reinterpret_cast(&g_frontBuffer)); + surface->lpVtbl->Release(surface); + + s_surfaceDesc.dwSize = sizeof(s_surfaceDesc); + g_frontBuffer->lpVtbl->GetSurfaceDesc(g_frontBuffer, &s_surfaceDesc); + + result = createPaletteConverterSurface(dd); + if (FAILED(result)) + { + Compat::Log() << "Failed to create palette converter surface"; + g_frontBuffer->lpVtbl->Release(g_frontBuffer); + g_frontBuffer = nullptr; + return result; + } + + if (isFlippable) + { + DDSCAPS2 backBufferCaps = {}; + backBufferCaps.dwCaps = DDSCAPS_BACKBUFFER; + g_frontBuffer->lpVtbl->GetAttachedSurface(g_frontBuffer, &backBufferCaps, &g_backBuffer); + } + + QueryPerformanceFrequency(&g_qpcFrequency); + + g_updateEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr); + g_updateThread = CreateThread(nullptr, 0, &updateThreadProc, nullptr, 0, nullptr); + SetThreadPriority(g_updateThread, THREAD_PRIORITY_ABOVE_NORMAL); + + g_frontBuffer->lpVtbl->SetPrivateData(g_frontBuffer, + IID_IReleaseNotifier, &g_releaseNotifier, sizeof(&g_releaseNotifier), DDSPD_IUNKNOWNPOINTER); + + return DD_OK; +} + +template HRESULT RealPrimarySurface::create(IDirectDraw&); +template HRESULT RealPrimarySurface::create(IDirectDraw2&); +template HRESULT RealPrimarySurface::create(IDirectDraw4&); +template HRESULT RealPrimarySurface::create(IDirectDraw7&); + +HRESULT RealPrimarySurface::flip(DWORD flags) +{ + if (!g_backBuffer) + { + return DDERR_NOTFLIPPABLE; + } + + if (flags & DDFLIP_NOVSYNC) + { + update(); + return DD_OK; + } + + compatBlt(g_backBuffer); + if (flags & DDFLIP_DONOTWAIT) + { + flags ^= DDFLIP_DONOTWAIT; + } + + HRESULT result = g_frontBuffer->lpVtbl->Flip(g_frontBuffer, nullptr, flags | DDFLIP_WAIT); + if (SUCCEEDED(result)) + { + QueryPerformanceCounter(&g_lastUpdateTime); + g_isFlipEvent = true; + SetEvent(g_updateEvent); + } + return result; +} + +IDirectDrawSurface7* RealPrimarySurface::getSurface() +{ + return g_frontBuffer; +} + +bool RealPrimarySurface::isFullScreen() +{ + return nullptr != g_backBuffer; +} + +bool RealPrimarySurface::isLost() +{ + return g_frontBuffer && + DDERR_SURFACELOST == CompatDirectDrawSurface::s_origVtable.IsLost(g_frontBuffer); +} + +void RealPrimarySurface::release() +{ + if (g_frontBuffer) + { + g_frontBuffer->lpVtbl->Release(g_frontBuffer); + } +} + +HRESULT RealPrimarySurface::restore() +{ + return g_frontBuffer->lpVtbl->Restore(g_frontBuffer); +} + +void RealPrimarySurface::setClipper(LPDIRECTDRAWCLIPPER clipper) +{ + if (g_paletteConverterSurface) + { + HRESULT result = CompatDirectDrawSurface::s_origVtable.SetClipper( + g_paletteConverterSurface, clipper); + if (FAILED(result)) + { + LOG_ONCE("Failed to set the clipper on the converter surface: " << result); + } + } + + HRESULT result = CompatDirectDrawSurface::s_origVtable.SetClipper( + g_frontBuffer, clipper); + if (FAILED(result)) + { + LOG_ONCE("Failed to set clipper on the real primary surface: " << result); + } +} + +void RealPrimarySurface::setPalette(LPDIRECTDRAWPALETTE palette) +{ + if (g_paletteConverterSurface && CompatPrimarySurface::pixelFormat.dwRGBBitCount <= 8) + { + HRESULT result = g_paletteConverterSurface->lpVtbl->SetPalette(g_paletteConverterSurface, palette); + if (FAILED(result)) + { + LOG_ONCE("Failed to set the palette on the converter surface: " << result); + } + } + + if (s_surfaceDesc.ddpfPixelFormat.dwRGBBitCount <= 8) + { + HRESULT result = g_frontBuffer->lpVtbl->SetPalette(g_frontBuffer, palette); + if (FAILED(result) && DDERR_NOPALETTEATTACHED != result) + { + LOG_ONCE("Failed to set the palette on the real primary surface: " << result); + } + } + + updatePalette(); +} + +void RealPrimarySurface::update() +{ + g_isFlipEvent = false; + SetEvent(g_updateEvent); +} + +void RealPrimarySurface::updatePalette() +{ + if (isFullScreen()) + { + flip(DDFLIP_WAIT); + } + else + { + update(); + } + + static LARGE_INTEGER lastUpdateTime = {}; + DWORD timeSinceLastUpdate = getTimeElapsedInMs(lastUpdateTime); + if (timeSinceLastUpdate < Config::minPaletteUpdateInterval) + { + Sleep(Config::minPaletteUpdateInterval - timeSinceLastUpdate); + } + QueryPerformanceCounter(&lastUpdateTime); +} diff --git a/DDrawCompat/RealPrimarySurface.h b/DDrawCompat/RealPrimarySurface.h new file mode 100644 index 0000000..36a9a8a --- /dev/null +++ b/DDrawCompat/RealPrimarySurface.h @@ -0,0 +1,25 @@ +#pragma once + +#define CINTERFACE + +#include + +class RealPrimarySurface +{ +public: + template + static HRESULT create(DirectDraw& dd); + + static HRESULT flip(DWORD flags); + static IDirectDrawSurface7* getSurface(); + static bool isFullScreen(); + static bool isLost(); + static void release(); + static HRESULT restore(); + static void setClipper(LPDIRECTDRAWCLIPPER clipper); + static void setPalette(LPDIRECTDRAWPALETTE palette); + static void update(); + static void updatePalette(); + + static DDSURFACEDESC2 s_surfaceDesc; +}; diff --git a/DDrawCompat/UnmodifiedDDrawProcs.cpp b/DDrawCompat/UnmodifiedDDrawProcs.cpp new file mode 100644 index 0000000..0a74b1f --- /dev/null +++ b/DDrawCompat/UnmodifiedDDrawProcs.cpp @@ -0,0 +1,9 @@ +#include "DDrawProcs.h" + +#define CREATE_DDRAW_PROC_STUB(procName) \ + extern "C" __declspec(naked) void __stdcall procName() \ + { \ + __asm jmp Compat::origProcs.procName \ + } + +VISIT_UNMODIFIED_DDRAW_PROCS(CREATE_DDRAW_PROC_STUB)