diff --git a/DDrawCompat/Common/CompatVtable.h b/DDrawCompat/Common/CompatVtable.h index 7de3cdd..301787f 100644 --- a/DDrawCompat/Common/CompatVtable.h +++ b/DDrawCompat/Common/CompatVtable.h @@ -24,6 +24,7 @@ public: } static Vtable s_origVtable; + static const Vtable* s_origVtablePtr; }; template @@ -32,10 +33,9 @@ class CompatVtable : public CompatVtableBase public: static void hookVtable(const Vtable* vtable) { - static bool isInitialized = false; - if (!isInitialized && vtable) + if (vtable && !s_origVtablePtr) { - isInitialized = true; + s_origVtablePtr = vtable; InitVisitor visitor(*vtable); forEach>(visitor); @@ -161,6 +161,9 @@ private: template Vtable CompatVtableBase::s_origVtable = {}; +template +const Vtable* CompatVtableBase::s_origVtablePtr = nullptr; + template Vtable CompatVtable::s_compatVtable(getCompatVtable()); diff --git a/DDrawCompat/DDraw/ActivateAppHandler.cpp b/DDrawCompat/DDraw/ActivateAppHandler.cpp index 74d04ce..ec14d24 100644 --- a/DDrawCompat/DDraw/ActivateAppHandler.cpp +++ b/DDrawCompat/DDraw/ActivateAppHandler.cpp @@ -4,6 +4,7 @@ #include "DDraw/ActivateAppHandler.h" #include "DDraw/DirectDraw.h" #include "DDraw/DisplayMode.h" +#include "DDraw/Surfaces/FullScreenTagSurface.h" #include "DDraw/Surfaces/PrimarySurface.h" #include "DDraw/Surfaces/SurfaceImpl.h" #include "Gdi/Gdi.h" @@ -14,7 +15,6 @@ extern HWND g_mainWindow; namespace { bool g_isActive = true; - CompatWeakPtr g_fullScreenDirectDraw = nullptr; HWND g_fullScreenCooperativeWindow = nullptr; DWORD g_fullScreenCooperativeFlags = 0; Win32::FontSmoothing::SystemSettings g_fontSmoothingSettings = {}; @@ -93,9 +93,9 @@ namespace Gdi::disableEmulation(); } - if (g_fullScreenDirectDraw) + auto dd(DDraw::FullScreenTagSurface::getFullScreenDirectDraw()); + if (dd) { - CompatPtr dd(Compat::queryInterface(g_fullScreenDirectDraw.get())); if (isActivated) { activateApp(*dd); @@ -130,9 +130,8 @@ namespace DDraw return g_isActive; } - void setFullScreenCooperativeLevel(CompatWeakPtr dd, HWND hwnd, DWORD flags) + void setFullScreenCooperativeLevel(HWND hwnd, DWORD flags) { - g_fullScreenDirectDraw = dd; g_fullScreenCooperativeWindow = hwnd; g_fullScreenCooperativeFlags = flags; } diff --git a/DDrawCompat/DDraw/ActivateAppHandler.h b/DDrawCompat/DDraw/ActivateAppHandler.h index 58bdf9a..580f52d 100644 --- a/DDrawCompat/DDraw/ActivateAppHandler.h +++ b/DDrawCompat/DDraw/ActivateAppHandler.h @@ -1,12 +1,8 @@ #pragma once -#define CINTERFACE #define WIN32_LEAN_AND_MEAN #include -#include - -#include "Common/CompatWeakPtr.h" namespace DDraw { @@ -14,7 +10,7 @@ namespace DDraw { void installHooks(); bool isActive(); - void setFullScreenCooperativeLevel(CompatWeakPtr dd, HWND hwnd, DWORD flags); + void setFullScreenCooperativeLevel(HWND hwnd, DWORD flags); void uninstallHooks(); } } diff --git a/DDrawCompat/DDraw/DirectDraw.cpp b/DDrawCompat/DDraw/DirectDraw.cpp index c5723ac..aa7169a 100644 --- a/DDrawCompat/DDraw/DirectDraw.cpp +++ b/DDrawCompat/DDraw/DirectDraw.cpp @@ -8,62 +8,6 @@ #include "DDraw/Surfaces/PrimarySurface.h" #include "DDraw/Surfaces/Surface.h" -namespace -{ - struct DirectDrawInterface - { - void* vtable; - void* ddObject; - DirectDrawInterface* next; - DWORD refCount; - DWORD unknown1; - DWORD unknown2; - }; - - DirectDrawInterface* g_fullScreenDirectDraw = nullptr; - CompatWeakPtr g_fullScreenTagSurface; - - bool isFullScreenDirectDraw(void* dd) - { - return dd && g_fullScreenDirectDraw && - static_cast(dd)->ddObject == g_fullScreenDirectDraw->ddObject; - } - - void onReleaseFullScreenTagSurface() - { - DDraw::ActivateAppHandler::setFullScreenCooperativeLevel(nullptr, nullptr, 0); - g_fullScreenDirectDraw = nullptr; - g_fullScreenTagSurface = nullptr; - } - - void setFullScreenDirectDraw(CompatRef dd) - { - g_fullScreenTagSurface.release(); - DDraw::FullScreenTagSurface::create( - dd, g_fullScreenTagSurface.getRef(), &onReleaseFullScreenTagSurface); - - /* - IDirectDraw interfaces don't conform to the COM rule about object identity: - QueryInterface with IID_IUnknown does not always return the same pointer for the same object. - The IUnknown (== IDirectDraw v1) interface may even be freed, making the interface invalid, - while the DirectDraw object itself can still be kept alive by its other interfaces. - Unfortunately, the IDirectDrawSurface GetDDInterface method inherits this problem and may - also return an invalid (already freed) interface pointer. - To work around this problem, a copy of the necessary interface data is passed - to CompatActivateAppHandler, which is sufficient for it to use QueryInterface to "safely" - obtain a valid interface pointer (other than IUnknown/IDirectDraw v1) to the full-screen - DirectDraw object. - */ - - static DirectDrawInterface fullScreenDirectDraw = {}; - ZeroMemory(&fullScreenDirectDraw, sizeof(fullScreenDirectDraw)); - DirectDrawInterface& ddIntf = reinterpret_cast(dd.get()); - fullScreenDirectDraw.vtable = ddIntf.vtable; - fullScreenDirectDraw.ddObject = ddIntf.ddObject; - g_fullScreenDirectDraw = &fullScreenDirectDraw; - } -} - namespace DDraw { template @@ -144,13 +88,13 @@ namespace DDraw if (dwFlags & DDSCL_FULLSCREEN) { CompatPtr dd(Compat::queryInterface(This)); - setFullScreenDirectDraw(*dd); - ActivateAppHandler::setFullScreenCooperativeLevel( - reinterpret_cast(g_fullScreenDirectDraw), hWnd, dwFlags); + DDraw::FullScreenTagSurface::create(*dd); + ActivateAppHandler::setFullScreenCooperativeLevel(hWnd, dwFlags); } - else if (isFullScreenDirectDraw(This) && g_fullScreenTagSurface) + else if (CompatPtr(Compat::queryInterface(This)).get() == + DDraw::FullScreenTagSurface::getFullScreenDirectDraw().get()) { - g_fullScreenTagSurface.release(); + DDraw::FullScreenTagSurface::destroy(); } } return result; diff --git a/DDrawCompat/DDraw/DirectDrawSurface.cpp b/DDrawCompat/DDraw/DirectDrawSurface.cpp index ad3fc5c..7ff2123 100644 --- a/DDrawCompat/DDraw/DirectDrawSurface.cpp +++ b/DDrawCompat/DDraw/DirectDrawSurface.cpp @@ -42,6 +42,19 @@ namespace DDraw SET_COMPAT_METHOD(SetClipper); SET_COMPAT_METHOD(SetPalette); SET_COMPAT_METHOD(Unlock); + + setCompatVtable2(vtable); + } + + template + void DirectDrawSurface::setCompatVtable2(Vtable& vtable) + { + SET_COMPAT_METHOD(GetDDInterface); + } + + template <> + void DirectDrawSurface::setCompatVtable2(Vtable&) + { } template DirectDrawSurface; diff --git a/DDrawCompat/DDraw/DirectDrawSurface.h b/DDrawCompat/DDraw/DirectDrawSurface.h index 3fa05f3..656a968 100644 --- a/DDrawCompat/DDraw/DirectDrawSurface.h +++ b/DDrawCompat/DDraw/DirectDrawSurface.h @@ -15,5 +15,7 @@ namespace DDraw static void setCompatVtable(Vtable& vtable); + private: + static void setCompatVtable2(Vtable& vtable); }; } diff --git a/DDrawCompat/DDraw/Surfaces/FullScreenTagSurface.cpp b/DDrawCompat/DDraw/Surfaces/FullScreenTagSurface.cpp index 599a681..f9454f4 100644 --- a/DDrawCompat/DDraw/Surfaces/FullScreenTagSurface.cpp +++ b/DDrawCompat/DDraw/Surfaces/FullScreenTagSurface.cpp @@ -1,21 +1,22 @@ #include "DDraw/Surfaces/SurfaceImpl.h" #include "DDraw/Surfaces/FullScreenTagSurface.h" +namespace +{ + CompatWeakPtr g_surface = nullptr; +} + namespace DDraw { - FullScreenTagSurface::FullScreenTagSurface(const std::function& releaseHandler) - : m_releaseHandler(releaseHandler) - { - } - FullScreenTagSurface::~FullScreenTagSurface() { - m_releaseHandler(); + g_surface = nullptr; } - HRESULT FullScreenTagSurface::create(CompatRef dd, IDirectDrawSurface*& surface, - const std::function& releaseHandler) + HRESULT FullScreenTagSurface::create(CompatRef dd) { + destroy(); + DDSURFACEDESC desc = {}; desc.dwSize = sizeof(desc); desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_CAPS; @@ -23,13 +24,39 @@ namespace DDraw desc.dwHeight = 1; desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; + IDirectDrawSurface* surface = nullptr; HRESULT result = Surface::create(dd, desc, surface); if (SUCCEEDED(result)) { CompatPtr surface7(Compat::queryInterface(surface)); - std::unique_ptr privateData(new FullScreenTagSurface(releaseHandler)); + std::unique_ptr privateData(new FullScreenTagSurface()); attach(*surface7, privateData); + g_surface = surface; } return result; } + + void FullScreenTagSurface::destroy() + { + g_surface.release(); + } + + CompatPtr FullScreenTagSurface::getFullScreenDirectDraw() + { + if (!g_surface) + { + return nullptr; + } + + CompatPtr dd = nullptr; + auto tagSurface(getFullScreenTagSurface()); + tagSurface.get()->lpVtbl->GetDDInterface(tagSurface, reinterpret_cast(&dd.getRef())); + return CompatPtr(Compat::queryInterface(dd.get())); + } + + CompatPtr FullScreenTagSurface::getFullScreenTagSurface() + { + return CompatPtr( + Compat::queryInterface(g_surface.get())); + } } diff --git a/DDrawCompat/DDraw/Surfaces/FullScreenTagSurface.h b/DDrawCompat/DDraw/Surfaces/FullScreenTagSurface.h index 64557d5..f699c0c 100644 --- a/DDrawCompat/DDraw/Surfaces/FullScreenTagSurface.h +++ b/DDrawCompat/DDraw/Surfaces/FullScreenTagSurface.h @@ -1,8 +1,5 @@ #pragma once -#include - -#include "Common/CompatPtr.h" #include "DDraw/Surfaces/Surface.h" namespace DDraw @@ -10,13 +7,11 @@ namespace DDraw class FullScreenTagSurface : public Surface { public: - FullScreenTagSurface(const std::function& releaseHandler); virtual ~FullScreenTagSurface(); - static HRESULT create(CompatRef dd, IDirectDrawSurface*& surface, - const std::function& releaseHandler); - - private: - std::function m_releaseHandler; + static HRESULT create(CompatRef dd); + static void destroy(); + static CompatPtr getFullScreenDirectDraw(); + static CompatPtr getFullScreenTagSurface(); }; } diff --git a/DDrawCompat/DDraw/Surfaces/Surface.cpp b/DDrawCompat/DDraw/Surfaces/Surface.cpp index f5f0bab..dfb7768 100644 --- a/DDrawCompat/DDraw/Surfaces/Surface.cpp +++ b/DDrawCompat/DDraw/Surfaces/Surface.cpp @@ -32,6 +32,27 @@ namespace } } } + + IID getDdIidFromVtablePtr(const void* vtablePtr) + { + if (CompatVtableBase::s_origVtablePtr == vtablePtr) + { + return IID_IDirectDraw; + } + if (CompatVtableBase::s_origVtablePtr == vtablePtr) + { + return IID_IDirectDraw2; + } + if (CompatVtableBase::s_origVtablePtr == vtablePtr) + { + return IID_IDirectDraw4; + } + if (CompatVtableBase::s_origVtablePtr == vtablePtr) + { + return IID_IDirectDraw7; + } + return IID_IUnknown; + } } namespace DDraw @@ -56,7 +77,10 @@ namespace DDraw return refCount; } - Surface::Surface() : m_refCount(0) + Surface::Surface() + : m_ddId() + , m_ddObject(nullptr) + , m_refCount(0) { } @@ -69,6 +93,20 @@ namespace DDraw if (SUCCEEDED(dds->SetPrivateData(&dds, IID_CompatSurfacePrivateData, privateData.get(), sizeof(privateData.get()), DDSPD_IUNKNOWNPOINTER))) { + CompatPtr dd; + CompatVtableBase::s_origVtable.GetDDInterface( + &dds, reinterpret_cast(&dd.getRef())); + + privateData->createImpl(); + privateData->m_impl->m_data = privateData.get(); + privateData->m_impl2->m_data = privateData.get(); + privateData->m_impl3->m_data = privateData.get(); + privateData->m_impl4->m_data = privateData.get(); + privateData->m_impl7->m_data = privateData.get(); + + privateData->m_ddId = getDdIidFromVtablePtr(reinterpret_cast(dd.get())[0]); + privateData->m_ddObject = reinterpret_cast(dd.get())[1]; + privateData.release(); } } diff --git a/DDrawCompat/DDraw/Surfaces/Surface.h b/DDrawCompat/DDraw/Surfaces/Surface.h index 71c1951..41343e0 100644 --- a/DDrawCompat/DDraw/Surfaces/Surface.h +++ b/DDrawCompat/DDraw/Surfaces/Surface.h @@ -6,12 +6,13 @@ #include +#include "Common/CompatPtr.h" #include "Common/CompatRef.h" namespace DDraw { - template - class SurfaceImpl; + template class SurfaceImpl; + template class SurfaceImpl2; class Surface { @@ -43,10 +44,15 @@ namespace DDraw std::unique_ptr> m_impl7; private: + template + friend class SurfaceImpl2; + static HRESULT WINAPI attachToLinkedSurfaces( IDirectDrawSurface7* surface, DDSURFACEDESC2* desc, void* rootSurface); virtual void createImpl(); + IID m_ddId; + void* m_ddObject; DWORD m_refCount; }; } diff --git a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp index e2a4403..5e289a4 100644 --- a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp +++ b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.cpp @@ -7,6 +7,16 @@ namespace { + struct DirectDrawInterface + { + const void* vtable; + void* ddObject; + DirectDrawInterface* next; + DWORD refCount; + DWORD unknown1; + DWORD unknown2; + }; + bool mirrorBlt(CompatRef dst, CompatRef src, RECT srcRect, DWORD mirrorFx); @@ -232,6 +242,18 @@ namespace DDraw { return s_origVtable.GetCaps(This, lpDDSCaps); } + + template + HRESULT SurfaceImpl2::GetDDInterface(TSurface* /*This*/, LPVOID* lplpDD) + { + DirectDrawInterface dd = {}; + dd.vtable = IID_IDirectDraw7 == m_data->m_ddId + ? static_cast(CompatVtableBase::s_origVtablePtr) + : static_cast(CompatVtableBase::s_origVtablePtr); + dd.ddObject = m_data->m_ddObject; + return CompatVtableBase::s_origVtable.QueryInterface( + reinterpret_cast(&dd), m_data->m_ddId, lplpDD); + } template HRESULT SurfaceImpl::GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc) diff --git a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h index d792aff..6ad0dc3 100644 --- a/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h +++ b/DDrawCompat/DDraw/Surfaces/SurfaceImpl.h @@ -10,8 +10,24 @@ namespace DDraw { + class Surface; + template - class SurfaceImpl + class SurfaceImpl2 + { + public: + SurfaceImpl2() : m_data(nullptr) {} + + virtual HRESULT GetDDInterface(TSurface* This, LPVOID* lplpDD); + + protected: + friend class Surface; + + Surface* m_data; + }; + + template + class SurfaceImpl : public SurfaceImpl2 { public: typedef typename Types::TSurfaceDesc TSurfaceDesc; diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index 55fe89a..59a98ac 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -72,6 +72,7 @@ ddraw $(ProjectDir);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 true @@ -94,10 +95,12 @@ true false $(IntDir)%(RelativeDir) + ProgramDatabase Dll/DDrawCompat.def dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies) + UseLinkTimeCodeGeneration @@ -128,6 +131,7 @@ Dll/DDrawCompat.def dxguid.lib;detours.lib;msimg32.lib;oleacc.lib;uxtheme.lib;dwmapi.lib;winmm.lib;%(AdditionalDependencies) No + UseLinkTimeCodeGeneration