1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00
2023-11-05 14:21:18 +01:00

231 lines
6.6 KiB
C++

#include <set>
#include <initguid.h>
#include <Common/CompatPtr.h>
#include <Config/Settings/AlignSysMemSurfaces.h>
#include <DDraw/DirectDrawClipper.h>
#include <DDraw/DirectDrawSurface.h>
#include <DDraw/Surfaces/Surface.h>
#include <DDraw/Surfaces/SurfaceImpl.h>
#include <Win32/DisplayMode.h>
// {C62D8849-DFAC-4454-A1E8-DA67446426BA}
DEFINE_GUID(IID_CompatSurfacePrivateData,
0xc62d8849, 0xdfac, 0x4454, 0xa1, 0xe8, 0xda, 0x67, 0x44, 0x64, 0x26, 0xba);
namespace
{
std::set<DDraw::Surface*> g_surfaces;
void heapFree(void* p)
{
HeapFree(GetProcessHeap(), 0, p);
}
}
namespace DDraw
{
HRESULT STDMETHODCALLTYPE Surface::QueryInterface(REFIID, LPVOID*)
{
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE Surface::AddRef()
{
return ++m_refCount;
}
ULONG STDMETHODCALLTYPE Surface::Release()
{
DWORD refCount = --m_refCount;
if (0 == refCount)
{
delete this;
}
return refCount;
}
Surface::Surface(DWORD origFlags, DWORD origCaps)
: m_origFlags(origFlags)
, m_origCaps(origCaps)
, m_refCount(0)
, m_sizeOverride{}
, m_sysMemBuffer(nullptr, &heapFree)
{
g_surfaces.insert(this);
}
Surface::~Surface()
{
DirectDrawClipper::setClipper(*this, nullptr);
g_surfaces.erase(this);
}
void* Surface::alignBuffer(void* buffer)
{
auto p = static_cast<BYTE*>(buffer);
const DWORD alignmentOffset = Config::alignSysMemSurfaces.get();
const DWORD mod = reinterpret_cast<DWORD>(p) % ALIGNMENT;
p = p - mod + alignmentOffset;
if (mod > alignmentOffset)
{
p += ALIGNMENT;
}
return p;
}
void Surface::attach(CompatRef<IDirectDrawSurface7> dds, std::unique_ptr<Surface> privateData)
{
if (SUCCEEDED(dds->SetPrivateData(&dds, IID_CompatSurfacePrivateData,
privateData.get(), sizeof(privateData.get()), DDSPD_IUNKNOWNPOINTER)))
{
privateData->m_surface = &dds;
privateData->createImpl();
privateData.release();
}
}
template <typename TDirectDraw, typename TSurface, typename TSurfaceDesc>
HRESULT Surface::create(
CompatRef<TDirectDraw> dd, TSurfaceDesc desc, TSurface*& surface, std::unique_ptr<Surface> privateData)
{
if ((desc.ddsCaps.dwCaps & DDSCAPS_3DDEVICE) &&
((desc.dwFlags & DDSD_PIXELFORMAT) && (desc.ddpfPixelFormat.dwRGBBitCount <= 8)) ||
(!(desc.dwFlags & DDSD_PIXELFORMAT) && Win32::DisplayMode::getBpp() <= 8))
{
desc.ddsCaps.dwCaps &= ~DDSCAPS_3DDEVICE;
}
if ((desc.dwFlags & DDSD_MIPMAPCOUNT) && 1 == desc.dwMipMapCount)
{
desc.dwFlags &= ~DDSD_MIPMAPCOUNT;
desc.ddsCaps.dwCaps &= ~(DDSCAPS_COMPLEX | DDSCAPS_MIPMAP);
}
HRESULT result = dd->CreateSurface(&dd, &desc, &surface, nullptr);
if (FAILED(result))
{
return result;
}
auto surface7(CompatPtr<IDirectDrawSurface7>::from(surface));
if (desc.ddsCaps.dwCaps & DDSCAPS_COMPLEX)
{
auto attachedSurfaces(DirectDrawSurface::getAllAttachedSurfaces(*surface7));
for (DWORD i = 0; i < attachedSurfaces.size(); ++i)
{
attach(*attachedSurfaces[i], std::make_unique<Surface>(privateData->m_origFlags, privateData->m_origCaps));
}
}
else if ((desc.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) && !(desc.dwFlags & DDSD_LPSURFACE))
{
privateData->fixAlignment(*surface7);
}
attach(*surface7, std::move(privateData));
return result;
}
template HRESULT Surface::create(
CompatRef<IDirectDraw> dd, DDSURFACEDESC desc, IDirectDrawSurface*& surface, std::unique_ptr<Surface> privateData);
template HRESULT Surface::create(
CompatRef<IDirectDraw2> dd, DDSURFACEDESC desc, IDirectDrawSurface*& surface, std::unique_ptr<Surface> privateData);
template HRESULT Surface::create(
CompatRef<IDirectDraw4> dd, DDSURFACEDESC2 desc, IDirectDrawSurface4*& surface, std::unique_ptr<Surface> privateData);
template HRESULT Surface::create(
CompatRef<IDirectDraw7> dd, DDSURFACEDESC2 desc, IDirectDrawSurface7*& surface, std::unique_ptr<Surface> privateData);
void Surface::createImpl()
{
m_impl.reset(new SurfaceImpl<IDirectDrawSurface>(this));
m_impl2.reset(new SurfaceImpl<IDirectDrawSurface2>(this));
m_impl3.reset(new SurfaceImpl<IDirectDrawSurface3>(this));
m_impl4.reset(new SurfaceImpl<IDirectDrawSurface4>(this));
m_impl7.reset(new SurfaceImpl<IDirectDrawSurface7>(this));
}
void Surface::enumSurfaces(const std::function<void(Surface&)>& callback)
{
for (auto surface : g_surfaces)
{
callback(*surface);
}
}
void Surface::fixAlignment(CompatRef<IDirectDrawSurface7> surface)
{
DDSURFACEDESC2 desc = {};
desc.dwSize = sizeof(desc);
if (FAILED(surface->Lock(&surface, nullptr, &desc, DDLOCK_WAIT, nullptr)))
{
return;
}
surface->Unlock(&surface, nullptr);
const DWORD alignmentOffset = Config::alignSysMemSurfaces.get();
const DWORD size = desc.lPitch * desc.dwHeight;
if (0 == size || alignmentOffset == reinterpret_cast<DWORD>(desc.lpSurface) % ALIGNMENT)
{
return;
}
m_sysMemBuffer.reset(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size + ALIGNMENT));
if (!m_sysMemBuffer)
{
return;
}
desc = {};
desc.dwSize = sizeof(desc);
desc.dwFlags = DDSD_LPSURFACE;
desc.lpSurface = alignBuffer(m_sysMemBuffer.get());
if (FAILED(surface->SetSurfaceDesc(&surface, &desc, 0)))
{
m_sysMemBuffer.reset();
}
}
template <>
SurfaceImpl<IDirectDrawSurface>* Surface::getImpl<IDirectDrawSurface>() const { return m_impl.get(); }
template <>
SurfaceImpl<IDirectDrawSurface2>* Surface::getImpl<IDirectDrawSurface2>() const { return m_impl2.get(); }
template <>
SurfaceImpl<IDirectDrawSurface3>* Surface::getImpl<IDirectDrawSurface3>() const { return m_impl3.get(); }
template <>
SurfaceImpl<IDirectDrawSurface4>* Surface::getImpl<IDirectDrawSurface4>() const { return m_impl4.get(); }
template <>
SurfaceImpl<IDirectDrawSurface7>* Surface::getImpl<IDirectDrawSurface7>() const { return m_impl7.get(); }
template <typename TSurface>
Surface* Surface::getSurface(TSurface& dds)
{
Surface* surface = nullptr;
DWORD surfacePtrSize = sizeof(surface);
// This can get called during surface release so a proper QueryInterface would be dangerous
CompatVtable<IDirectDrawSurface7Vtbl>::s_origVtable.GetPrivateData(
reinterpret_cast<IDirectDrawSurface7*>(&dds),
IID_CompatSurfacePrivateData, &surface, &surfacePtrSize);
return surface;
}
template Surface* Surface::getSurface(IDirectDrawSurface& dds);
template Surface* Surface::getSurface(IDirectDrawSurface2& dds);
template Surface* Surface::getSurface(IDirectDrawSurface3& dds);
template Surface* Surface::getSurface(IDirectDrawSurface4& dds);
template Surface* Surface::getSurface(IDirectDrawSurface7& dds);
void Surface::restore()
{
}
void Surface::setSizeOverride(DWORD width, DWORD height)
{
m_sizeOverride.cx = width;
m_sizeOverride.cy = height;
}
}