1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00
2022-09-27 21:45:05 +02:00

289 lines
8.3 KiB
C++

#include <set>
#include <d3d.h>
#include <d3dumddi.h>
#include <Common/CompatPtr.h>
#include <D3dDdi/Device.h>
#include <D3dDdi/SurfaceRepository.h>
#include <DDraw/DirectDrawClipper.h>
#include <DDraw/DirectDrawSurface.h>
#include <DDraw/RealPrimarySurface.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <DDraw/Surfaces/Surface.h>
#include <DDraw/Surfaces/SurfaceImpl.h>
#include <Dll/Dll.h>
namespace
{
HANDLE g_bltSrcResource = nullptr;
UINT g_bltSrcSubResourceIndex = 0;
RECT g_bltSrcRect = {};
template <typename TSurface>
typename DDraw::Types<TSurface>::TSurfaceDesc getDesc(TSurface* This)
{
DDraw::Types<TSurface>::TSurfaceDesc desc = {};
desc.dwSize = sizeof(desc);
getOrigVtable(This).GetSurfaceDesc(This, &desc);
return desc;
}
template <typename TSurfaceDesc>
RECT getRect(LPRECT rect, const TSurfaceDesc& desc)
{
return rect ? *rect : RECT{ 0, 0, static_cast<LONG>(desc.dwWidth), static_cast<LONG>(desc.dwHeight) };
}
template <typename TSurface, typename BltFunc>
HRESULT blt(TSurface* This, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, BltFunc bltFunc)
{
if (!lpDDSrcSurface)
{
return bltFunc(This, lpDDSrcSurface, lpSrcRect);
}
auto dstDesc = getDesc(This);
if (!(dstDesc.ddsCaps.dwCaps & DDSCAPS_3DDEVICE) || !(dstDesc.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY))
{
return bltFunc(This, lpDDSrcSurface, lpSrcRect);
}
auto srcDesc = getDesc(lpDDSrcSurface);
if (!(srcDesc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY))
{
return bltFunc(This, lpDDSrcSurface, lpSrcRect);
}
auto srcResource = DDraw::DirectDrawSurface::getDriverResourceHandle(*lpDDSrcSurface);
auto device = D3dDdi::Device::findDeviceByResource(srcResource);
if (!device)
{
return bltFunc(This, lpDDSrcSurface, lpSrcRect);
}
auto& repo = D3dDdi::SurfaceRepository::get(device->getAdapter());
RECT srcRect = getRect(lpSrcRect, srcDesc);
auto& tex = repo.getTempTexture(srcRect.right - srcRect.left, srcRect.bottom - srcRect.top,
srcDesc.ddpfPixelFormat);
if (!tex.resource)
{
return bltFunc(This, lpDDSrcSurface, lpSrcRect);
}
CompatPtr<TSurface> newSrcSurface(tex.surface);
DDCOLORKEY ck = {};
HRESULT result = getOrigVtable(This).GetColorKey(lpDDSrcSurface, DDCKEY_SRCBLT, &ck);
getOrigVtable(This).SetColorKey(newSrcSurface, DDCKEY_SRCBLT, SUCCEEDED(result) ? &ck : nullptr);
g_bltSrcResource = srcResource;
g_bltSrcSubResourceIndex = DDraw::DirectDrawSurface::getSubResourceIndex(*lpDDSrcSurface);
g_bltSrcRect = getRect(lpSrcRect, srcDesc);
RECT r = { 0, 0, g_bltSrcRect.right - g_bltSrcRect.left, g_bltSrcRect.bottom - g_bltSrcRect.top };
result = bltFunc(This, newSrcSurface, &r);
g_bltSrcResource = nullptr;
g_bltSrcSubResourceIndex = 0;
g_bltSrcRect = {};
return SUCCEEDED(result) ? DD_OK : bltFunc(This, lpDDSrcSurface, lpSrcRect);
}
}
namespace DDraw
{
template <typename TSurface>
SurfaceImpl<TSurface>::SurfaceImpl(Surface* data)
: m_data(data)
{
}
template <typename TSurface>
SurfaceImpl<TSurface>::~SurfaceImpl()
{
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::Blt(
TSurface* This, LPRECT lpDestRect, TSurface* lpDDSrcSurface, LPRECT lpSrcRect,
DWORD dwFlags, LPDDBLTFX lpDDBltFx)
{
RealPrimarySurface::waitForFlip(m_data->getDDS());
DirectDrawClipper::update();
return blt(This, lpDDSrcSurface, lpSrcRect, [=](TSurface* This, TSurface* lpDDSrcSurface, LPRECT lpSrcRect)
{ return getOrigVtable(This).Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); });
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::BltFast(
TSurface* This, DWORD dwX, DWORD dwY, TSurface* lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans)
{
RealPrimarySurface::waitForFlip(m_data->getDDS());
return blt(This, lpDDSrcSurface, lpSrcRect, [=](TSurface* This, TSurface* lpDDSrcSurface, LPRECT lpSrcRect)
{ return getOrigVtable(This).BltFast(This, dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); });
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::Flip(TSurface* This, TSurface* lpDDSurfaceTargetOverride, DWORD dwFlags)
{
return getOrigVtable(This).Flip(This, lpDDSurfaceTargetOverride, dwFlags);
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::GetCaps(TSurface* This, TDdsCaps* lpDDSCaps)
{
HRESULT result = getOrigVtable(This).GetCaps(This, lpDDSCaps);
if (SUCCEEDED(result))
{
restoreOrigCaps(lpDDSCaps->dwCaps);
}
return result;
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::GetDC(TSurface* This, HDC* lphDC)
{
RealPrimarySurface::waitForFlip(m_data->getDDS());
HRESULT result = getOrigVtable(This).GetDC(This, lphDC);
if (SUCCEEDED(result))
{
Dll::g_origProcs.ReleaseDDThreadLock();
}
return result;
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::GetSurfaceDesc(TSurface* This, TSurfaceDesc* lpDDSurfaceDesc)
{
HRESULT result = getOrigVtable(This).GetSurfaceDesc(This, lpDDSurfaceDesc);
if (SUCCEEDED(result))
{
if (0 != m_data->m_sizeOverride.cx)
{
lpDDSurfaceDesc->dwWidth = m_data->m_sizeOverride.cx;
lpDDSurfaceDesc->dwHeight = m_data->m_sizeOverride.cy;
m_data->m_sizeOverride = {};
}
restoreOrigCaps(lpDDSurfaceDesc->ddsCaps.dwCaps);
}
return result;
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::IsLost(TSurface* This)
{
return getOrigVtable(This).IsLost(This);
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::Lock(
TSurface* This, LPRECT lpDestRect, TSurfaceDesc* lpDDSurfaceDesc,
DWORD dwFlags, HANDLE hEvent)
{
RealPrimarySurface::waitForFlip(m_data->getDDS());
HRESULT result = getOrigVtable(This).Lock(This, lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent);
if (SUCCEEDED(result))
{
restoreOrigCaps(lpDDSurfaceDesc->ddsCaps.dwCaps);
}
else if (DDERR_SURFACELOST == result)
{
TSurfaceDesc desc = {};
desc.dwSize = sizeof(desc);
if (SUCCEEDED(getOrigVtable(This).GetSurfaceDesc(This, &desc)) && !(desc.dwFlags & DDSD_HEIGHT))
{
// Fixes missing handling for lost vertex buffers in Messiah
getOrigVtable(This).Restore(This);
// Still, pass back DDERR_SURFACELOST to the application in case it handles it
}
}
return result;
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::QueryInterface(TSurface* This, REFIID riid, LPVOID* obp)
{
auto iid = (IID_IDirect3DRampDevice == riid) ? &IID_IDirect3DRGBDevice : &riid;
HRESULT result = getOrigVtable(This).QueryInterface(This, *iid, obp);
if (DDERR_INVALIDOBJECT == result)
{
m_data->setSizeOverride(1, 1);
result = getOrigVtable(This).QueryInterface(This, *iid, obp);
m_data->setSizeOverride(0, 0);
}
return result;
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::ReleaseDC(TSurface* This, HDC hDC)
{
HRESULT result = getOrigVtable(This).ReleaseDC(This, hDC);
if (SUCCEEDED(result))
{
Dll::g_origProcs.AcquireDDThreadLock();
}
return result;
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::Restore(TSurface* This)
{
HRESULT result = getOrigVtable(This).Restore(This);
if (SUCCEEDED(result))
{
m_data->restore();
}
return result;
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::SetClipper(TSurface* This, LPDIRECTDRAWCLIPPER lpDDClipper)
{
HRESULT result = getOrigVtable(This).SetClipper(This, lpDDClipper);
if (SUCCEEDED(result))
{
DDraw::DirectDrawClipper::setClipper(*m_data, lpDDClipper);
}
return result;
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::SetPalette(TSurface* This, LPDIRECTDRAWPALETTE lpDDPalette)
{
return getOrigVtable(This).SetPalette(This, lpDDPalette);
}
template <typename TSurface>
HRESULT SurfaceImpl<TSurface>::Unlock(TSurface* This, TUnlockParam lpRect)
{
return getOrigVtable(This).Unlock(This, lpRect);
}
template <typename TSurface>
void SurfaceImpl<TSurface>::restoreOrigCaps(DWORD& caps)
{
if (m_data->m_origCaps & DDSCAPS_3DDEVICE)
{
caps |= DDSCAPS_3DDEVICE;
}
}
template SurfaceImpl<IDirectDrawSurface>;
template SurfaceImpl<IDirectDrawSurface2>;
template SurfaceImpl<IDirectDrawSurface3>;
template SurfaceImpl<IDirectDrawSurface4>;
template SurfaceImpl<IDirectDrawSurface7>;
void setBltSrc(D3DDDIARG_BLT& data)
{
if (g_bltSrcResource)
{
data.hSrcResource = g_bltSrcResource;
data.SrcSubResourceIndex = g_bltSrcSubResourceIndex;
OffsetRect(&data.SrcRect, g_bltSrcRect.left, g_bltSrcRect.top);
}
}
}