mirror of
https://github.com/narzoul/DDrawCompat
synced 2024-12-30 08:55:36 +01:00
227 lines
7.1 KiB
C++
227 lines
7.1 KiB
C++
#include "CompatActivateAppHandler.h"
|
|
#include "CompatDirectDraw.h"
|
|
#include "CompatDirectDrawSurface.h"
|
|
#include "CompatDisplayMode.h"
|
|
#include "CompatPtr.h"
|
|
#include "CompatRef.h"
|
|
#include "IReleaseNotifier.h"
|
|
|
|
namespace
|
|
{
|
|
struct DirectDrawInterface
|
|
{
|
|
void* vtable;
|
|
void* ddObject;
|
|
DirectDrawInterface* next;
|
|
DWORD refCount;
|
|
DWORD unknown1;
|
|
DWORD unknown2;
|
|
};
|
|
|
|
DirectDrawInterface* g_fullScreenDirectDraw = nullptr;
|
|
CompatWeakPtr<IDirectDrawSurface> g_fullScreenTagSurface;
|
|
|
|
void onReleaseFullScreenTagSurface();
|
|
|
|
IReleaseNotifier g_fullScreenTagSurfaceReleaseNotifier(&onReleaseFullScreenTagSurface);
|
|
|
|
CompatPtr<IDirectDrawSurface> createFullScreenTagSurface(CompatRef<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 | DDSCAPS_SYSTEMMEMORY;
|
|
|
|
CompatPtr<IDirectDrawSurface> tagSurface;
|
|
dd->CreateSurface(&dd, &desc, &tagSurface.getRef(), nullptr);
|
|
if (tagSurface)
|
|
{
|
|
CompatPtr<IDirectDrawSurface7> tagSurface7(tagSurface);
|
|
tagSurface7->SetPrivateData(
|
|
tagSurface7, IID_IReleaseNotifier, &g_fullScreenTagSurfaceReleaseNotifier,
|
|
sizeof(&g_fullScreenTagSurfaceReleaseNotifier), DDSPD_IUNKNOWNPOINTER);
|
|
}
|
|
|
|
return tagSurface;
|
|
}
|
|
|
|
bool isFullScreenDirectDraw(void* dd)
|
|
{
|
|
return dd && g_fullScreenDirectDraw &&
|
|
static_cast<DirectDrawInterface*>(dd)->ddObject == g_fullScreenDirectDraw->ddObject;
|
|
}
|
|
|
|
void onReleaseFullScreenTagSurface()
|
|
{
|
|
CompatActivateAppHandler::setFullScreenCooperativeLevel(nullptr, nullptr, 0);
|
|
g_fullScreenDirectDraw = nullptr;
|
|
g_fullScreenTagSurface = nullptr;
|
|
}
|
|
|
|
void setFullScreenDirectDraw(CompatRef<IDirectDraw> dd)
|
|
{
|
|
g_fullScreenTagSurface.release();
|
|
g_fullScreenTagSurface = createFullScreenTagSurface(dd).detach();
|
|
|
|
/*
|
|
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<DirectDrawInterface&>(dd.get());
|
|
fullScreenDirectDraw.vtable = ddIntf.vtable;
|
|
fullScreenDirectDraw.ddObject = ddIntf.ddObject;
|
|
g_fullScreenDirectDraw = &fullScreenDirectDraw;
|
|
}
|
|
}
|
|
|
|
template <typename TDirectDraw>
|
|
void CompatDirectDraw<TDirectDraw>::setCompatVtable(Vtable<TDirectDraw>& vtable)
|
|
{
|
|
vtable.CreateSurface = &CreateSurface;
|
|
vtable.GetDisplayMode = &GetDisplayMode;
|
|
vtable.RestoreDisplayMode = &RestoreDisplayMode;
|
|
vtable.SetCooperativeLevel = &SetCooperativeLevel;
|
|
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<TDirectDraw>(
|
|
*This, *lpDDSurfaceDesc, *lplpDDSurface);
|
|
}
|
|
else
|
|
{
|
|
if (lpDDSurfaceDesc &&
|
|
(lpDDSurfaceDesc->dwFlags & DDSD_WIDTH) &&
|
|
(lpDDSurfaceDesc->dwFlags & DDSD_HEIGHT) &&
|
|
!((lpDDSurfaceDesc->dwFlags & DDSD_CAPS) &&
|
|
(lpDDSurfaceDesc->ddsCaps.dwCaps & (DDSCAPS_ALPHA | DDSCAPS_ZBUFFER))))
|
|
{
|
|
CompatPtr<IDirectDraw7> dd(Compat::queryInterface<IDirectDraw7>(This));
|
|
auto dm = CompatDisplayMode::getDisplayMode(*dd);
|
|
|
|
TSurfaceDesc desc = *lpDDSurfaceDesc;
|
|
if (!(desc.dwFlags & DDSD_PIXELFORMAT))
|
|
{
|
|
desc.dwFlags |= DDSD_PIXELFORMAT;
|
|
desc.ddpfPixelFormat = dm.ddpfPixelFormat;
|
|
}
|
|
if (!((desc.dwFlags & DDSD_CAPS) &&
|
|
(desc.ddsCaps.dwCaps & (DDSCAPS_OFFSCREENPLAIN | DDSCAPS_OVERLAY | DDSCAPS_TEXTURE |
|
|
DDSCAPS_FRONTBUFFER | DDSCAPS_BACKBUFFER))))
|
|
{
|
|
desc.dwFlags |= DDSD_CAPS;
|
|
desc.ddsCaps.dwCaps |= DDSCAPS_OFFSCREENPLAIN;
|
|
}
|
|
result = s_origVtable.CreateSurface(This, &desc, lplpDDSurface, pUnkOuter);
|
|
}
|
|
else
|
|
{
|
|
result = s_origVtable.CreateSurface(This, lpDDSurfaceDesc, lplpDDSurface, pUnkOuter);
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(result))
|
|
{
|
|
CompatDirectDrawSurface<TSurface>::fixSurfacePtrs(**lplpDDSurface);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename TDirectDraw>
|
|
HRESULT STDMETHODCALLTYPE CompatDirectDraw<TDirectDraw>::GetDisplayMode(
|
|
TDirectDraw* This, TSurfaceDesc* lpDDSurfaceDesc)
|
|
{
|
|
const DWORD size = lpDDSurfaceDesc ? lpDDSurfaceDesc->dwSize : 0;
|
|
if (sizeof(DDSURFACEDESC) != size && sizeof(DDSURFACEDESC2) != size)
|
|
{
|
|
return DDERR_INVALIDPARAMS;
|
|
}
|
|
|
|
CompatPtr<IDirectDraw7> dd(Compat::queryInterface<IDirectDraw7>(This));
|
|
const DDSURFACEDESC2 dm = CompatDisplayMode::getDisplayMode(*dd);
|
|
CopyMemory(lpDDSurfaceDesc, &dm, size);
|
|
lpDDSurfaceDesc->dwSize = size;
|
|
|
|
return DD_OK;
|
|
}
|
|
|
|
template <typename TDirectDraw>
|
|
HRESULT STDMETHODCALLTYPE CompatDirectDraw<TDirectDraw>::RestoreDisplayMode(TDirectDraw* This)
|
|
{
|
|
CompatPtr<IDirectDraw7> dd(Compat::queryInterface<IDirectDraw7>(This));
|
|
return CompatDisplayMode::restoreDisplayMode(*dd);
|
|
}
|
|
|
|
template <typename TDirectDraw>
|
|
HRESULT STDMETHODCALLTYPE CompatDirectDraw<TDirectDraw>::SetCooperativeLevel(
|
|
TDirectDraw* This, HWND hWnd, DWORD dwFlags)
|
|
{
|
|
if ((dwFlags & DDSCL_FULLSCREEN) && !CompatActivateAppHandler::isActive())
|
|
{
|
|
return DDERR_EXCLUSIVEMODEALREADYSET;
|
|
}
|
|
|
|
HRESULT result = s_origVtable.SetCooperativeLevel(This, hWnd, dwFlags);
|
|
if (SUCCEEDED(result))
|
|
{
|
|
if (dwFlags & DDSCL_FULLSCREEN)
|
|
{
|
|
CompatPtr<IDirectDraw> dd(Compat::queryInterface<IDirectDraw>(This));
|
|
setFullScreenDirectDraw(*dd);
|
|
CompatActivateAppHandler::setFullScreenCooperativeLevel(
|
|
reinterpret_cast<IUnknown*>(g_fullScreenDirectDraw), hWnd, dwFlags);
|
|
}
|
|
else if (isFullScreenDirectDraw(This) && g_fullScreenTagSurface)
|
|
{
|
|
g_fullScreenTagSurface.release();
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename TDirectDraw>
|
|
template <typename... Params>
|
|
HRESULT STDMETHODCALLTYPE CompatDirectDraw<TDirectDraw>::SetDisplayMode(
|
|
TDirectDraw* This,
|
|
DWORD dwWidth,
|
|
DWORD dwHeight,
|
|
DWORD dwBPP,
|
|
Params... params)
|
|
{
|
|
CompatPtr<IDirectDraw7> dd(Compat::queryInterface<IDirectDraw7>(This));
|
|
return CompatDisplayMode::setDisplayMode(*dd, dwWidth, dwHeight, dwBPP, params...);
|
|
}
|
|
|
|
template CompatDirectDraw<IDirectDraw>;
|
|
template CompatDirectDraw<IDirectDraw2>;
|
|
template CompatDirectDraw<IDirectDraw4>;
|
|
template CompatDirectDraw<IDirectDraw7>;
|