1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00

Initial commit

This commit is contained in:
narzoul 2015-12-25 01:57:44 +01:00
commit 2a5e699c6e
31 changed files with 3285 additions and 0 deletions

View File

@ -0,0 +1,140 @@
#include "CompatDirectDraw.h"
#include "CompatDirectDrawSurface.h"
#include "CompatPrimarySurface.h"
namespace
{
template <typename TSurfaceDesc>
HRESULT PASCAL enumDisplayModesCallback(
TSurfaceDesc* lpDDSurfaceDesc,
LPVOID lpContext)
{
if (lpDDSurfaceDesc)
{
*static_cast<DDPIXELFORMAT*>(lpContext) = lpDDSurfaceDesc->ddpfPixelFormat;
}
return DDENUMRET_CANCEL;
}
}
template <typename TDirectDraw>
void CompatDirectDraw<TDirectDraw>::setCompatVtable(Vtable<TDirectDraw>& vtable)
{
vtable.CreateSurface = &CreateSurface;
vtable.RestoreDisplayMode = &RestoreDisplayMode;
vtable.SetDisplayMode = &SetDisplayMode;
}
template <typename TDirectDraw>
HRESULT STDMETHODCALLTYPE CompatDirectDraw<TDirectDraw>::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<TSurface>::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<TSurface>::fixSurfacePtrs(**lplpDDSurface);
if (isPrimary)
{
CompatDirectDrawSurface<TSurface>::updateSurfaceParams();
}
}
return result;
}
template <typename TDirectDraw>
HRESULT STDMETHODCALLTYPE CompatDirectDraw<TDirectDraw>::RestoreDisplayMode(TDirectDraw* This)
{
HRESULT result = s_origVtable.RestoreDisplayMode(This);
if (SUCCEEDED(result))
{
CompatPrimarySurface::displayMode = CompatPrimarySurface::getDisplayMode(*This);
}
return result;
}
template <typename TDirectDraw>
template <typename... Params>
static HRESULT STDMETHODCALLTYPE CompatDirectDraw<TDirectDraw>::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<IDirectDraw>;
template CompatDirectDraw<IDirectDraw2>;
template CompatDirectDraw<IDirectDraw4>;
template CompatDirectDraw<IDirectDraw7>;

View File

@ -0,0 +1,31 @@
#pragma once
#include "CompatVtable.h"
#include "DDrawTypes.h"
#include "DirectDrawVtblVisitor.h"
template <typename TDirectDraw>
class CompatDirectDraw : public CompatVtable<CompatDirectDraw<TDirectDraw>, TDirectDraw>
{
public:
typedef typename Types<TDirectDraw>::TCreatedSurface TSurface;
typedef typename Types<TDirectDraw>::TSurfaceDesc TSurfaceDesc;
static void setCompatVtable(Vtable<TDirectDraw>& vtable);
static HRESULT STDMETHODCALLTYPE CreateSurface(
TDirectDraw* This,
TSurfaceDesc* lpDDSurfaceDesc,
TSurface** lplpDDSurface,
IUnknown* pUnkOuter);
static HRESULT STDMETHODCALLTYPE RestoreDisplayMode(TDirectDraw* This);
template <typename... Params>
static HRESULT STDMETHODCALLTYPE SetDisplayMode(
TDirectDraw* This,
DWORD dwWidth,
DWORD dwHeight,
DWORD dwBPP,
Params... params);
};

View File

@ -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;
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "CompatVtable.h"
#include "DirectDrawPaletteVtblVisitor.h"
class CompatDirectDrawPalette : public CompatVtable<CompatDirectDrawPalette, IDirectDrawPalette>
{
public:
static void setCompatVtable(IDirectDrawPaletteVtbl& vtable);
static HRESULT STDMETHODCALLTYPE SetEntries(
IDirectDrawPalette* This,
DWORD dwFlags,
DWORD dwStartingEntry,
DWORD dwCount,
LPPALETTEENTRY lpEntries);
};

View File

@ -0,0 +1,685 @@
#include <algorithm>
#include <set>
#include <vector>
#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<void**>(&dd), IID_IDirectDraw7, nullptr);
if (!dd ||
FAILED(CompatDirectDraw<IDirectDraw7>::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<std::set<IDirectDrawSurface7*>*>(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<IDirectDrawSurface7*> visitedSurfaces{ &surface };
surface.lpVtbl->EnumAttachedSurfaces(&surface, &visitedSurfaces, &enumSurfacesCallback);
}
IDirectDrawSurface7* getMirroredSurface(IDirectDrawSurface7& surface, RECT* srcRect, DWORD mirrorFx)
{
auto& origVtable = CompatDirectDrawSurface<IDirectDrawSurface7>::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<LONG>(desc.dwWidth), static_cast<LONG>(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 <typename TSurface>
TSurface* getMirroredSurface(TSurface& surface, RECT* rect, DWORD mirrorFx)
{
auto& origVtable = CompatDirectDrawSurface<TSurface>::s_origVtable;
IDirectDrawSurface7* surface7 = nullptr;
origVtable.QueryInterface(&surface, IID_IDirectDrawSurface7, reinterpret_cast<void**>(&surface7));
IDirectDrawSurface7* mirroredSurface7 = getMirroredSurface(*surface7, rect, mirrorFx);
surface7->lpVtbl->Release(surface7);
if (!mirroredSurface7)
{
return nullptr;
}
auto& origVtable7 = CompatDirectDrawSurface<IDirectDrawSurface7>::s_origVtable;
TSurface* mirroredSurface = nullptr;
origVtable7.QueryInterface(mirroredSurface7,
CompatDirectDrawSurface<TSurface>::s_iid, reinterpret_cast<void**>(&mirroredSurface));
origVtable7.Release(mirroredSurface7);
return mirroredSurface;
}
SimilarSurface getSimilarSurface(const DDSURFACEDESC2& desc)
{
static std::vector<SimilarSurface> similarSurfacesVidMem;
static std::vector<SimilarSurface> similarSurfacesSysMem;
std::vector<SimilarSurface>& 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<IDirectDrawSurface7>::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<IDirectDraw7>::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<IDirectDraw7>::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<IDirectDrawSurface7>::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<IDirectDrawSurface7>::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 <typename TSurface>
void CompatDirectDrawSurface<TSurface>::setCompatVtable(Vtable<TSurface>& 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 <typename TSurface>
template <typename TDirectDraw>
HRESULT CompatDirectDrawSurface<TSurface>::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<TDirectDraw>::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 <typename TSurface>
void CompatDirectDrawSurface<TSurface>::fixSurfacePtrs(TSurface& surface)
{
IDirectDrawSurface7* surface7 = nullptr;
surface.lpVtbl->QueryInterface(&surface, IID_IDirectDrawSurface7, reinterpret_cast<LPVOID*>(&surface7));
::fixSurfacePtrs(*surface7);
surface7->lpVtbl->Release(surface7);
}
template <typename TSurface>
void CompatDirectDrawSurface<TSurface>::initPrimarySurfacePtr(const GUID& guid, IUnknown& surface)
{
if (SUCCEEDED(surface.lpVtbl->QueryInterface(
&surface, guid, reinterpret_cast<LPVOID*>(&s_compatPrimarySurface))))
{
s_compatPrimarySurface->lpVtbl->Release(s_compatPrimarySurface);
}
}
template <typename TSurface>
HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface<TSurface>::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 <typename TSurface>
HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface<TSurface>::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 <typename TSurface>
HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface<TSurface>::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 <typename TSurface>
HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface<TSurface>::GetCaps(
TSurface* This,
TDdsCaps* lpDDSCaps)
{
HRESULT result = s_origVtable.GetCaps(This, lpDDSCaps);
if (This == s_compatPrimarySurface && SUCCEEDED(result))
{
restorePrimaryCaps(*lpDDSCaps);
}
return result;
}
template <typename TSurface>
HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface<TSurface>::GetSurfaceDesc(
TSurface* This,
TSurfaceDesc* lpDDSurfaceDesc)
{
HRESULT result = s_origVtable.GetSurfaceDesc(This, lpDDSurfaceDesc);
if (This == s_compatPrimarySurface && SUCCEEDED(result))
{
restorePrimaryCaps(lpDDSurfaceDesc->ddsCaps);
}
return result;
}
template <typename TSurface>
HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface<TSurface>::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 <typename TSurface>
HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface<TSurface>::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 <typename TSurface>
HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface<TSurface>::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 <typename TSurface>
HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface<TSurface>::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 <typename TSurface>
HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface<TSurface>::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 <typename TSurface>
HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface<TSurface>::SetClipper(
TSurface* This,
LPDIRECTDRAWCLIPPER lpDDClipper)
{
HRESULT result = s_origVtable.SetClipper(This, lpDDClipper);
if (This == s_compatPrimarySurface && SUCCEEDED(result))
{
RealPrimarySurface::setClipper(lpDDClipper);
}
return result;
}
template <typename TSurface>
HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface<TSurface>::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 <typename TSurface>
HRESULT STDMETHODCALLTYPE CompatDirectDrawSurface<TSurface>::Unlock(TSurface* This, TUnlockParam lpRect)
{
HRESULT result = s_origVtable.Unlock(This, lpRect);
if (This == s_compatPrimarySurface && SUCCEEDED(result))
{
RealPrimarySurface::update();
}
return result;
}
template <typename TSurface>
void CompatDirectDrawSurface<TSurface>::initCompatPrimarySurface()
{
Compat::LogEnter("CompatDirectDrawSurface::initCompatPrimarySurface");
IUnknown& unk = reinterpret_cast<IUnknown&>(*s_compatPrimarySurface);
CompatDirectDrawSurface<IDirectDrawSurface>::initPrimarySurfacePtr(IID_IDirectDrawSurface, unk);
CompatDirectDrawSurface<IDirectDrawSurface2>::initPrimarySurfacePtr(IID_IDirectDrawSurface2, unk);
CompatDirectDrawSurface<IDirectDrawSurface3>::initPrimarySurfacePtr(IID_IDirectDrawSurface3, unk);
CompatDirectDrawSurface<IDirectDrawSurface4>::initPrimarySurfacePtr(IID_IDirectDrawSurface4, unk);
CompatDirectDrawSurface<IDirectDrawSurface7>::initPrimarySurfacePtr(IID_IDirectDrawSurface7, unk);
if (SUCCEEDED(s_origVtable.QueryInterface(
s_compatPrimarySurface,
IID_IDirectDrawSurface7,
reinterpret_cast<LPVOID*>(&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 <typename TSurface>
void CompatDirectDrawSurface<TSurface>::restorePrimaryCaps(TDdsCaps& caps)
{
caps.dwCaps ^= DDSCAPS_OFFSCREENPLAIN;
caps.dwCaps |= DDSCAPS_PRIMARYSURFACE | DDSCAPS_VISIBLE;
}
template <typename TSurface>
void CompatDirectDrawSurface<TSurface>::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 <typename TSurface>
TSurface* CompatDirectDrawSurface<TSurface>::s_compatPrimarySurface = nullptr;
template <> const IID& CompatDirectDrawSurface<IDirectDrawSurface>::s_iid = IID_IDirectDrawSurface;
template <> const IID& CompatDirectDrawSurface<IDirectDrawSurface2>::s_iid = IID_IDirectDrawSurface2;
template <> const IID& CompatDirectDrawSurface<IDirectDrawSurface3>::s_iid = IID_IDirectDrawSurface3;
template <> const IID& CompatDirectDrawSurface<IDirectDrawSurface4>::s_iid = IID_IDirectDrawSurface4;
template <> const IID& CompatDirectDrawSurface<IDirectDrawSurface7>::s_iid = IID_IDirectDrawSurface7;
template CompatDirectDrawSurface<IDirectDrawSurface>;
template CompatDirectDrawSurface<IDirectDrawSurface2>;
template CompatDirectDrawSurface<IDirectDrawSurface3>;
template CompatDirectDrawSurface<IDirectDrawSurface4>;
template CompatDirectDrawSurface<IDirectDrawSurface7>;
template HRESULT CompatDirectDrawSurface<IDirectDrawSurface>::createCompatPrimarySurface(
IDirectDraw& dd,
TSurfaceDesc compatDesc,
IDirectDrawSurface*& compatSurface);
template HRESULT CompatDirectDrawSurface<IDirectDrawSurface>::createCompatPrimarySurface(
IDirectDraw2& dd,
TSurfaceDesc compatDesc,
IDirectDrawSurface*& compatSurface);
template HRESULT CompatDirectDrawSurface<IDirectDrawSurface4>::createCompatPrimarySurface(
IDirectDraw4& dd,
TSurfaceDesc compatDesc,
IDirectDrawSurface4*& compatSurface);
template HRESULT CompatDirectDrawSurface<IDirectDrawSurface7>::createCompatPrimarySurface(
IDirectDraw7& dd,
TSurfaceDesc compatDesc,
IDirectDrawSurface7*& compatSurface);

View File

@ -0,0 +1,72 @@
#pragma once
#include "CompatVtable.h"
#include "DDrawTypes.h"
#include "DirectDrawSurfaceVtblVisitor.h"
template <typename TSurface>
class CompatDirectDrawSurface : public CompatVtable<CompatDirectDrawSurface<TSurface>, TSurface>
{
public:
typedef typename Types<TSurface>::TSurfaceDesc TSurfaceDesc;
typedef typename Types<TSurface>::TDdsCaps TDdsCaps;
typedef typename Types<TSurface>::TUnlockParam TUnlockParam;
static void setCompatVtable(Vtable<TSurface>& vtable);
template <typename TDirectDraw>
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;
};

View File

@ -0,0 +1,537 @@
#define CINTERFACE
#define WIN32_LEAN_AND_MEAN
#include <unordered_map>
#include <oleacc.h>
#include <Windows.h>
#include <detours.h>
#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<HDC, GdiSurface> 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<CWPRETSTRUCT*>(lParam);
if (WM_WINDOWPOSCHANGED == ret->message)
{
InvalidateRect(nullptr, nullptr, TRUE);
}
else if (WM_ERASEBKGND == ret->message && ret->lResult)
{
HDC origDc = reinterpret_cast<HDC>(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<WPARAM>(compatDc), 0);
releaseCompatDc(compatDc);
}
}
}
}
return CallNextHookEx(nullptr, nCode, wParam, lParam);
}
IDirectDraw7* createDirectDraw()
{
IDirectDraw7* dd = nullptr;
CALL_ORIG_DDRAW(DirectDrawCreateEx, nullptr, reinterpret_cast<LPVOID*>(&dd), IID_IDirectDraw7, nullptr);
if (!dd)
{
Compat::Log() << "Failed to create a DirectDraw interface for GDI";
return nullptr;
}
if (FAILED(CompatDirectDraw<IDirectDraw7>::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<IDirectDraw7>::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<IDirectDrawSurface7>::s_origVtable.SetPalette(
surface, CompatPrimarySurface::palette);
}
return surface;
}
BOOL CALLBACK excludeClipRectsForOverlappingWindows(HWND hwnd, LPARAM lParam)
{
auto excludeClipRectsData = reinterpret_cast<ExcludeClipRectsData*>(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<LPARAM>(&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<PIMAGE_DOS_HEADER>(module);
if (IMAGE_DOS_SIGNATURE != dosHeader->e_magic) {
return nullptr;
}
char* moduleBase = reinterpret_cast<char*>(module);
PIMAGE_NT_HEADERS ntHeader = reinterpret_cast<PIMAGE_NT_HEADERS>(
reinterpret_cast<char*>(dosHeader) + dosHeader->e_lfanew);
if (IMAGE_NT_SIGNATURE != ntHeader->Signature)
{
return nullptr;
}
PIMAGE_EXPORT_DIRECTORY exportDir = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
moduleBase + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
DWORD* rvaOfNames = reinterpret_cast<DWORD*>(moduleBase + exportDir->AddressOfNames);
for (DWORD i = 0; i < exportDir->NumberOfNames; ++i)
{
if (0 == strcmp(procName, moduleBase + rvaOfNames[i]))
{
WORD* nameOrds = reinterpret_cast<WORD*>(moduleBase + exportDir->AddressOfNameOrdinals);
DWORD* rvaOfFunctions = reinterpret_cast<DWORD*>(moduleBase + exportDir->AddressOfFunctions);
return reinterpret_cast<FARPROC>(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 <typename FuncPtr>
void hookGdiFunction(const char* funcName, FuncPtr& origFuncPtr, FuncPtr newFuncPtr)
{
origFuncPtr = reinterpret_cast<FuncPtr>(getProcAddress(GetModuleHandle("user32"), funcName));
DetourAttach(reinterpret_cast<void**>(&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<DWORD>(OBJID_CARET), IID_IAccessible,
reinterpret_cast<void**>(&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;
}

View File

@ -0,0 +1,7 @@
#pragma once
class CompatGdiSurface
{
public:
static void hookGdi();
};

View File

@ -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 <typename TDirectDraw>
DisplayMode getDisplayMode(TDirectDraw& dd)
{
DisplayMode dm = {};
typename CompatDirectDraw<TDirectDraw>::TSurfaceDesc desc = {};
desc.dwSize = sizeof(desc);
CompatDirectDraw<TDirectDraw>::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);
}

View File

@ -0,0 +1,30 @@
#pragma once
#define CINTERFACE
#include <ddraw.h>
class IReleaseNotifier;
namespace CompatPrimarySurface
{
struct DisplayMode
{
LONG width;
LONG height;
DDPIXELFORMAT pixelFormat;
};
template <typename TDirectDraw>
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;
}

View File

@ -0,0 +1,6 @@
#include "CompatVtable.h"
namespace Compat
{
std::map<void*, DetouredMethodInfo> detouredMethods;
}

175
DDrawCompat/CompatVtable.h Normal file
View File

@ -0,0 +1,175 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <map>
#include <string>
#include <vector>
#include <Windows.h>
#include <detours.h>
#include "DDrawLog.h"
#include "DDrawVtableVisitor.h"
template <typename Interface>
using Vtable = typename std::remove_pointer<decltype(Interface::lpVtbl)>::type;
namespace Compat
{
struct DetouredMethodInfo
{
DetouredMethodInfo(void*& updatedOrigMethodPtr, std::map<void*, void*>& vtablePtrToCompatVtable)
: updatedOrigMethodPtr(updatedOrigMethodPtr), vtablePtrToCompatVtable(vtablePtrToCompatVtable)
{
}
void*& updatedOrigMethodPtr;
std::map<void*, void*>& vtablePtrToCompatVtable;
};
extern std::map<void*, DetouredMethodInfo> detouredMethods;
}
template <typename CompatInterface, typename Interface>
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<Vtable<Interface>>(visitor);
DetourTransactionCommit();
}
}
static Vtable<Interface> s_origVtable;
private:
class InitVisitor
{
public:
template <typename MemberDataPtr, MemberDataPtr ptr>
void visit(const std::string& vtableTypeName, const std::string& funcName)
{
s_funcNames[getKey<MemberDataPtr, ptr>()] = vtableTypeName + "::" + funcName;
if (!(s_compatVtable.*ptr))
{
s_compatVtable.*ptr = s_origVtable.*ptr;
s_threadSafeVtable.*ptr = s_origVtable.*ptr;
}
else
{
s_threadSafeVtable.*ptr = getThreadSafeFuncPtr<MemberDataPtr, ptr>(s_compatVtable.*ptr);
hookMethod(reinterpret_cast<void*&>(s_origVtable.*ptr), s_threadSafeVtable.*ptr);
}
}
private:
template <typename Result, typename... Params>
using FuncPtr = Result(STDMETHODCALLTYPE *)(Params...);
template <typename MemberDataPtr, MemberDataPtr ptr>
static std::vector<unsigned char> getKey()
{
MemberDataPtr mp = ptr;
unsigned char* p = reinterpret_cast<unsigned char*>(&mp);
return std::vector<unsigned char>(p, p + sizeof(mp));
}
template <typename MemberDataPtr, MemberDataPtr ptr, typename Result, typename... Params>
static FuncPtr<Result, Params...> getThreadSafeFuncPtr(FuncPtr<Result, Params...>)
{
return &threadSafeFunc<MemberDataPtr, ptr, Result, Params...>;
}
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 <typename MemberDataPtr, MemberDataPtr ptr, typename Result, typename IntfPtr, typename... Params>
static Result STDMETHODCALLTYPE threadSafeFunc(IntfPtr This, Params... params)
{
Compat::origProcs.AcquireDDThreadLock();
Compat::LogEnter(s_funcNames[getKey<MemberDataPtr, ptr>()].c_str(), This, params...);
Result result;
auto it = s_vtablePtrToCompatVtable.find(This->lpVtbl);
if (it != s_vtablePtrToCompatVtable.end())
{
Vtable<Interface>& compatVtable = *static_cast<Vtable<Interface>*>(it->second);
result = (compatVtable.*ptr)(This, params...);
}
else
{
result = (s_origVtable.*ptr)(This, params...);
}
Compat::origProcs.ReleaseDDThreadLock();
Compat::LogLeave(s_funcNames[getKey<MemberDataPtr, ptr>()].c_str(), This, params...) << result;
return result;
}
};
static Vtable<Interface> createCompatVtable()
{
Vtable<Interface> vtable = {};
CompatInterface::setCompatVtable(vtable);
return vtable;
}
static Vtable<Interface>& getCompatVtable()
{
static Vtable<Interface> vtable(createCompatVtable());
return vtable;
}
static Vtable<Interface>* s_vtablePtr;
static Vtable<Interface> s_compatVtable;
static Vtable<Interface> s_threadSafeVtable;
static std::map<void*, void*> s_vtablePtrToCompatVtable;
static std::map<std::vector<unsigned char>, std::string> s_funcNames;
};
template <typename CompatInterface, typename Interface>
Vtable<Interface>* CompatVtable<CompatInterface, Interface>::s_vtablePtr = nullptr;
template <typename CompatInterface, typename Interface>
Vtable<Interface> CompatVtable<CompatInterface, Interface>::s_origVtable = {};
template <typename CompatInterface, typename Interface>
Vtable<Interface> CompatVtable<CompatInterface, Interface>::s_compatVtable(CompatInterface::getCompatVtable());
template <typename CompatInterface, typename Interface>
Vtable<Interface> CompatVtable<CompatInterface, Interface>::s_threadSafeVtable = {};
template <typename CompatInterface, typename Interface>
std::map<void*, void*> CompatVtable<CompatInterface, Interface>::s_vtablePtrToCompatVtable;
template <typename CompatInterface, typename Interface>
std::map<std::vector<unsigned char>, std::string> CompatVtable<CompatInterface, Interface>::s_funcNames;

10
DDrawCompat/Config.h Normal file
View File

@ -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;
}

View File

@ -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

View File

@ -0,0 +1,184 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{1146187A-17DE-4350-B9D1-9F9EAA934908}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>DDrawCompat</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<TargetName>ddraw</TargetName>
<IncludePath>C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\include;$(IncludePath)</IncludePath>
<LibraryPath>C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\lib.X86;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<TargetName>ddraw</TargetName>
<IncludePath>C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\include;$(IncludePath)</IncludePath>
<LibraryPath>C:\Program Files %28x86%29\Microsoft Research\Detours Express 3.0\lib.X86;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<ModuleDefinitionFile>DDrawCompat.def</ModuleDefinitionFile>
<AdditionalDependencies>dxguid.lib;detours.lib;oleacc.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<ModuleDefinitionFile>DDrawCompat.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<ModuleDefinitionFile>DDrawCompat.def</ModuleDefinitionFile>
<AdditionalDependencies>dxguid.lib;detours.lib;oleacc.lib;%(AdditionalDependencies)</AdditionalDependencies>
<GenerateDebugInformation>No</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;DDRAWCOMPAT_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<ModuleDefinitionFile>DDrawCompat.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="CompatDirectDrawPalette.h" />
<ClInclude Include="CompatGdiSurface.h" />
<ClInclude Include="Config.h" />
<ClInclude Include="DDrawProcs.h" />
<ClInclude Include="CompatDirectDraw.h" />
<ClInclude Include="CompatPrimarySurface.h" />
<ClInclude Include="DDrawTypes.h" />
<ClInclude Include="CompatDirectDrawSurface.h" />
<ClInclude Include="DDrawVtableVisitor.h" />
<ClInclude Include="CompatVtable.h" />
<ClInclude Include="DirectDrawPaletteVtblVisitor.h" />
<ClInclude Include="DirectDrawSurfaceVtblVisitor.h" />
<ClInclude Include="DirectDrawVtblVisitor.h" />
<ClInclude Include="DDrawLog.h" />
<ClInclude Include="IReleaseNotifier.h" />
<ClInclude Include="RealPrimarySurface.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="CompatDirectDraw.cpp" />
<ClCompile Include="CompatDirectDrawPalette.cpp" />
<ClCompile Include="CompatDirectDrawSurface.cpp" />
<ClCompile Include="CompatGdiSurface.cpp" />
<ClCompile Include="CompatVtable.cpp" />
<ClCompile Include="DDrawLog.cpp" />
<ClCompile Include="DllMain.cpp" />
<ClCompile Include="CompatPrimarySurface.cpp" />
<ClCompile Include="DDrawProcs.cpp" />
<ClCompile Include="IReleaseNotifier.cpp" />
<ClCompile Include="RealPrimarySurface.cpp" />
<ClCompile Include="UnmodifiedDDrawProcs.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="DDrawCompat.def" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="DDrawProcs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CompatDirectDraw.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DDrawTypes.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CompatDirectDrawSurface.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DDrawVtableVisitor.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CompatVtable.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DDrawLog.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DirectDrawSurfaceVtblVisitor.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DirectDrawVtblVisitor.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="DirectDrawPaletteVtblVisitor.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CompatDirectDrawPalette.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CompatGdiSurface.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RealPrimarySurface.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CompatPrimarySurface.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="IReleaseNotifier.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Config.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="DllMain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="UnmodifiedDDrawProcs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DDrawProcs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="DDrawLog.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="RealPrimarySurface.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CompatPrimarySurface.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CompatGdiSurface.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="IReleaseNotifier.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CompatVtable.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CompatDirectDraw.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CompatDirectDrawSurface.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CompatDirectDrawPalette.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="DDrawCompat.def">
<Filter>Source Files</Filter>
</None>
</ItemGroup>
</Project>

39
DDrawCompat/DDrawLog.cpp Normal file
View File

@ -0,0 +1,39 @@
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#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");
}

104
DDrawCompat/DDrawLog.h Normal file
View File

@ -0,0 +1,104 @@
#pragma once
#define CINTERFACE
#include <fstream>
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 <typename T>
Log& operator<<(const T& t)
{
s_logFile << t;
return *this;
}
protected:
template <typename... Params>
Log(const char* prefix, const char* funcName, Params... params) : Log()
{
s_logFile << prefix << ' ' << funcName << '(';
toList(params...);
s_logFile << ')';
}
private:
void toList()
{
}
template <typename Param>
void toList(Param param)
{
s_logFile << param;
}
template <typename Param, typename... Params>
void toList(Param firstParam, Params... remainingParams)
{
s_logFile << firstParam << ", ";
toList(remainingParams...);
}
static std::ofstream s_logFile;
};
#ifdef _DEBUG
class LogEnter : private Log
{
public:
template <typename... Params>
LogEnter(const char* funcName, Params... params) : Log("-->", funcName, params...)
{
}
};
class LogLeave : private Log
{
public:
template <typename... Params>
LogLeave(const char* funcName, Params... params) : Log("<--", funcName, params...)
{
}
template <typename Result>
void operator<<(const Result& result)
{
static_cast<Log&>(*this) << " = " << result;
}
};
#else
class LogEnter
{
public:
template <typename... Params> LogEnter(const char*, Params...) {}
template <typename Result> void operator<<(const Result&) {}
};
typedef LogEnter LogLeave;
#endif
}

View File

@ -0,0 +1,6 @@
#include "DDrawProcs.h"
namespace Compat
{
DDrawProcs origProcs = {};
}

55
DDrawCompat/DDrawProcs.h Normal file
View File

@ -0,0 +1,55 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#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<decltype(procName)*>(Compat::origProcs.procName)(__VA_ARGS__) : \
E_FAIL)

79
DDrawCompat/DDrawTypes.h Normal file
View File

@ -0,0 +1,79 @@
#pragma once
#define CINTERFACE
#include <ddraw.h>
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 <typename Interface>
struct Types;
#define DD_CONCAT(x, y, ...) x##y
#define DD_TYPES(Interface, ...) \
template <> \
struct Types<DD_CONCAT(Interface, __VA_ARGS__)> : 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

View File

@ -0,0 +1,41 @@
#pragma once
#define CINTERFACE
#include <ddraw.h>
#include <typeinfo>
template <typename Vtable>
struct DDrawVtableForEach;
template <typename Vtable, typename Visitor>
void forEach(Visitor& visitor)
{
DDrawVtableForEach<Vtable>::forEach<Vtable>(visitor);
}
template <typename T>
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<decltype(&Vtable::member), &Vtable::member>(getTypeName<Vtable>(), #member)
template <>
struct DDrawVtableForEach<IUnknownVtbl>
{
template <typename Vtable, typename Visitor>
static void forEach(Visitor& visitor)
{
DD_VISIT(QueryInterface);
DD_VISIT(AddRef);
DD_VISIT(Release);
}
};

View File

@ -0,0 +1,18 @@
#pragma once
#include "DDrawVtableVisitor.h"
template <>
struct DDrawVtableForEach<IDirectDrawPaletteVtbl>
{
template <typename Vtable, typename Visitor>
static void forEach(Visitor& visitor)
{
DDrawVtableForEach<IUnknownVtbl>::forEach<Vtable>(visitor);
DD_VISIT(GetCaps);
DD_VISIT(GetEntries);
DD_VISIT(Initialize);
DD_VISIT(SetEntries);
}
};

View File

@ -0,0 +1,104 @@
#pragma once
#include "DDrawVtableVisitor.h"
template <>
struct DDrawVtableForEach<IDirectDrawSurfaceVtbl>
{
template <typename Vtable, typename Visitor>
static void forEach(Visitor& visitor)
{
DDrawVtableForEach<IUnknownVtbl>::forEach<Vtable>(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<IDirectDrawSurface2Vtbl>
{
template <typename Vtable, typename Visitor>
static void forEach(Visitor& visitor)
{
DDrawVtableForEach<IDirectDrawSurfaceVtbl>::forEach<Vtable>(visitor);
DD_VISIT(GetDDInterface);
DD_VISIT(PageLock);
DD_VISIT(PageUnlock);
}
};
template <>
struct DDrawVtableForEach<IDirectDrawSurface3Vtbl>
{
template <typename Vtable, typename Visitor>
static void forEach(Visitor& visitor)
{
DDrawVtableForEach<IDirectDrawSurface2Vtbl>::forEach<Vtable>(visitor);
DD_VISIT(SetSurfaceDesc);
}
};
template <>
struct DDrawVtableForEach<IDirectDrawSurface4Vtbl>
{
template <typename Vtable, typename Visitor>
static void forEach(Visitor& visitor)
{
DDrawVtableForEach<IDirectDrawSurface3Vtbl>::forEach<Vtable>(visitor);
DD_VISIT(SetPrivateData);
DD_VISIT(GetPrivateData);
DD_VISIT(FreePrivateData);
DD_VISIT(GetUniquenessValue);
DD_VISIT(ChangeUniquenessValue);
}
};
template <>
struct DDrawVtableForEach<IDirectDrawSurface7Vtbl>
{
template <typename Vtable, typename Visitor>
static void forEach(Visitor& visitor)
{
DDrawVtableForEach<IDirectDrawSurface4Vtbl>::forEach<Vtable>(visitor);
DD_VISIT(SetPriority);
DD_VISIT(GetPriority);
DD_VISIT(SetLOD);
DD_VISIT(GetLOD);
}
};

View File

@ -0,0 +1,74 @@
#pragma once
#include "DDrawVtableVisitor.h"
template <>
struct DDrawVtableForEach<IDirectDrawVtbl>
{
template <typename Vtable, typename Visitor>
static void forEach(Visitor& visitor)
{
DDrawVtableForEach<IUnknownVtbl>::forEach<Vtable>(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<IDirectDraw2Vtbl>
{
template <typename Vtable, typename Visitor>
static void forEach(Visitor& visitor)
{
DDrawVtableForEach<IDirectDrawVtbl>::forEach<Vtable>(visitor);
DD_VISIT(GetAvailableVidMem);
}
};
template <>
struct DDrawVtableForEach<IDirectDraw4Vtbl>
{
template <typename Vtable, typename Visitor>
static void forEach(Visitor& visitor)
{
DDrawVtableForEach<IDirectDraw2Vtbl>::forEach<Vtable>(visitor);
DD_VISIT(GetSurfaceFromDC);
DD_VISIT(RestoreAllSurfaces);
DD_VISIT(TestCooperativeLevel);
DD_VISIT(GetDeviceIdentifier);
}
};
template <>
struct DDrawVtableForEach<IDirectDraw7Vtbl>
{
template <typename Vtable, typename Visitor>
static void forEach(Visitor& visitor)
{
DDrawVtableForEach<IDirectDraw4Vtbl>::forEach<Vtable>(visitor);
DD_VISIT(StartModeTest);
DD_VISIT(EvaluateMode);
}
};

207
DDrawCompat/DllMain.cpp Normal file
View File

@ -0,0 +1,207 @@
#include <string>
#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 <typename CompatInterface>
void hookVtable(const GUID& guid, IUnknown& unk)
{
typename CompatInterface::Interface* intf = nullptr;
if (SUCCEEDED(unk.lpVtbl->QueryInterface(&unk, guid, reinterpret_cast<LPVOID*>(&intf))))
{
CompatInterface::hookVtable(*intf);
intf->lpVtbl->Release(intf);
}
}
void hookDirectDraw(IDirectDraw& dd)
{
IUnknown& ddUnk = reinterpret_cast<IUnknown&>(dd);
hookVtable<CompatDirectDraw<IDirectDraw>>(IID_IDirectDraw, ddUnk);
hookVtable<CompatDirectDraw<IDirectDraw2>>(IID_IDirectDraw2, ddUnk);
hookVtable<CompatDirectDraw<IDirectDraw4>>(IID_IDirectDraw4, ddUnk);
hookVtable<CompatDirectDraw<IDirectDraw7>>(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<IUnknown&>(*surface);
hookVtable<CompatDirectDrawSurface<IDirectDrawSurface>>(IID_IDirectDrawSurface, surfaceUnk);
hookVtable<CompatDirectDrawSurface<IDirectDrawSurface2>>(IID_IDirectDrawSurface2, surfaceUnk);
hookVtable<CompatDirectDrawSurface<IDirectDrawSurface3>>(IID_IDirectDrawSurface3, surfaceUnk);
hookVtable<CompatDirectDrawSurface<IDirectDrawSurface4>>(IID_IDirectDrawSurface4, surfaceUnk);
hookVtable<CompatDirectDrawSurface<IDirectDrawSurface7>>(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<GUID*>(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<SetAppCompatDataFunc*>(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;
}

View File

@ -0,0 +1,7 @@
#include "IReleaseNotifier.h"
#include <initguid.h>
// {7810158A-CB51-448A-8706-443A7DF6D4ED}
DEFINE_GUID(IID_IReleaseNotifier,
0x7810158a, 0xcb51, 0x448a, 0x87, 0x6, 0x44, 0x3a, 0x7d, 0xf6, 0xd4, 0xed);

View File

@ -0,0 +1,37 @@
#pragma once
#include <functional>
#include <Unknwnbase.h>
// {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<void()>& 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<void()> m_notifyHandler;
};

View File

@ -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<IDirectDrawSurface7>::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 <typename DirectDraw>
HRESULT createPaletteConverterSurface(DirectDraw& dd)
{
if (CompatPrimarySurface::displayMode.pixelFormat.dwRGBBitCount > 8 &&
RealPrimarySurface::s_surfaceDesc.ddpfPixelFormat.dwRGBBitCount > 8)
{
return DD_OK;
}
typename Types<DirectDraw>::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<DirectDraw>::TCreatedSurface* surface = nullptr;
HRESULT result = dd.lpVtbl->CreateSurface(&dd, &desc, &surface, nullptr);
if (SUCCEEDED(result))
{
surface->lpVtbl->QueryInterface(
surface, IID_IDirectDrawSurface7, reinterpret_cast<LPVOID*>(&g_paletteConverterSurface));
surface->lpVtbl->Release(surface);
}
return result;
}
DWORD getTimeElapsedInMs(const LARGE_INTEGER& time)
{
LARGE_INTEGER currentTime = {};
QueryPerformanceCounter(&currentTime);
return static_cast<DWORD>((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 <typename DirectDraw>
HRESULT RealPrimarySurface::create(DirectDraw& dd)
{
typename Types<DirectDraw>::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<DirectDraw>::TCreatedSurface* surface = nullptr;
HRESULT result = CompatDirectDraw<DirectDraw>::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<DirectDraw>::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<LPVOID*>(&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<IDirectDrawSurface7>::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<IDirectDrawSurface7>::s_origVtable.SetClipper(
g_paletteConverterSurface, clipper);
if (FAILED(result))
{
LOG_ONCE("Failed to set the clipper on the converter surface: " << result);
}
}
HRESULT result = CompatDirectDrawSurface<IDirectDrawSurface7>::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);
}

View File

@ -0,0 +1,25 @@
#pragma once
#define CINTERFACE
#include <ddraw.h>
class RealPrimarySurface
{
public:
template <typename DirectDraw>
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;
};

View File

@ -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)