//=================================================================
//
// Copyright (c) Microsoft Corporation 1999 - All Rights Reserved.
//
//=================================================================

//========================================================================
// include files
//========================================================================
#include <windows.h>
#include <ddraw.h>
#include <d3d.h>
#include <assert.h>
#include "softm.h"
#include "globals.h"
#include "smddraw.h"
#include "smd3d.h"

#define ASSERT assert

#ifndef RELEASE
#define RELEASE(x) if(x) { (x)->Release(); (x)=NULL; }
#endif

//========================================================================
//========================================================================
#ifdef _DEBUG
	static HRESULT NotImpl()
	{
		OutputDebugString("BLADE: **** WARNING returing E_NOTIMPL\r\n");
		//DebugBreak();
		return E_NOTIMPL;
	}
	#undef  E_NOTIMPL
	#define E_NOTIMPL	NotImpl()

	static HRESULT NoInterface()
	{
		OutputDebugString("BLADE: **** WARNING returing E_NOINTERFACE\r\n");
		//DebugBreak();
		return E_NOINTERFACE;
	}
	#undef	E_NOINTERFACE
	#define E_NOINTERFACE NoInterface()
#endif

//========================================================================
//========================================================================
inline int GetRef(void* pUnk)
{
	((IUnknown*)pUnk)->AddRef();
	return ((IUnknown*)pUnk)->Release();
}

//========================================================================
// get the "real" IDirectDrawSurface from a object, correctly handles the NULL case
//========================================================================
#define GetDDS(p)	((p) ? (((CDirectDrawSurface *)(p))->m_pDirectDrawSurface) : NULL)

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
    if (IID_IUnknown == riid)
    {
        AddRef();
        *ppvObj = (void*)this;
        return S_OK;
    }
    else if (IID_IDirectDrawSurface == riid)
    {
        AddRef();
        *ppvObj = (void*)(IDirectDrawSurface *)this;
        return S_OK;
    }
    else if (IID_IDirectDrawSurface2 == riid)
    {
        AddRef();
        *ppvObj = (void*)(IDirectDrawSurface2 *)this;
        return S_OK;
    }
    else if (IID_IDirectDrawSurface4 == riid)
    {
        AddRef();
        *ppvObj = (void*)(IDirectDrawSurface4 *)this;
        return S_OK;
    }
    else if (IID_IDirect3DTexture2 == riid)
    {
        AddRef();
        *ppvObj = (void*)(IDirect3DTexture2 *)this;
        return S_OK;
    }
#ifdef USE_DX7_INTERFACE
    else if (IID_IDirectDrawSurface7 == riid)
    {
        AddRef();
        *ppvObj = (void*)(IDirectDrawSurface7 *)this;
        return S_OK;
    }
#endif
    else
    {
        *ppvObj = NULL;
        return E_NOINTERFACE;
    }
}


//========================================================================
//========================================================================
ULONG CDirectDrawSurface::AddRef(void)
{
    return ++m_cRef;
}

//========================================================================
//========================================================================
ULONG CDirectDrawSurface::Release(void)
{
    if (--m_cRef == 0)
    {
		if (m_pDirectDraw)
			m_pDirectDraw->RemoveSurfaceFromList(this);

		//
		// make sure that our direct draw object is also going to be released
		// here
		//
		ASSERT(GetRef(m_pDirectDrawSurface) == 1);
		RELEASE(m_pDirectDrawSurface);
		RELEASE(m_pZBuffer);
////////RELEASE(m_pDirectDraw);
        delete this;
        return 0;
    }
    return m_cRef;
}


//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::AddAttachedSurface(LPDIRECTDRAWSURFACE lpdds)
{
	ASSERT(m_pDirectDrawSurface);

	if (lpdds && ((CDirectDrawSurface *)lpdds)->m_bZBuffer)
	{
		RELEASE(m_pZBuffer);
		m_pZBuffer = (CDirectDrawSurface *)lpdds;
		m_pZBuffer->AddRef();
		return S_OK;
	}

	if (m_pDirectDrawSurface)
	{
		HRESULT hr=m_pDirectDrawSurface->AddAttachedSurface(GetDDS(lpdds));

		if (SUCCEEDED(hr))
			((CDirectDrawSurface *)lpdds)->AddRef();

		return hr;
	}
	else
		return E_FAIL;
}


//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::AddOverlayDirtyRect(LPRECT lpr)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->AddOverlayDirtyRect(lpr);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::Blt(LPRECT lpr, LPDIRECTDRAWSURFACE lpdds, LPRECT lpr2, DWORD dw, LPDDBLTFX bltfx)
{
	ASSERT(m_pDirectDrawSurface);

	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->Blt(lpr, GetDDS(lpdds), lpr2, dw, bltfx);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::BltBatch(LPDDBLTBATCH lpddbb, DWORD dw1, DWORD dw2)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->BltBatch(lpddbb, dw1, dw2);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::BltFast(DWORD dw1, DWORD dw2, LPDIRECTDRAWSURFACE lpdds, LPRECT lpr, DWORD dw3)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->BltFast(dw1, dw2, GetDDS(lpdds), lpr, dw3);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::DeleteAttachedSurface(DWORD dw, LPDIRECTDRAWSURFACE lpdds)
{
	if (m_pZBuffer && m_pZBuffer == (CDirectDrawSurface *)lpdds)
	{
		RELEASE(m_pZBuffer);
		return S_OK;
	}

	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
	{
		HRESULT hr;
		hr = m_pDirectDrawSurface->DeleteAttachedSurface(dw, GetDDS(lpdds));

		if (SUCCEEDED(hr))
			((CDirectDrawSurface *)lpdds)->Release();

		return hr;
	}
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::EnumAttachedSurfaces(LPVOID lpv, LPDDENUMSURFACESCALLBACK lpdde)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->EnumAttachedSurfaces(lpv, lpdde);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::EnumOverlayZOrders(DWORD dw, LPVOID lpv, LPDDENUMSURFACESCALLBACK lpdde)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->EnumOverlayZOrders(dw, lpv, lpdde);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::Flip(LPDIRECTDRAWSURFACE lpdds, DWORD dw)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->Flip(GetDDS(lpdds), dw);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetAttachedSurface(LPDDSCAPS lpddsc, LPDIRECTDRAWSURFACE FAR *lplpdds)
{
	ASSERT(m_pDirectDrawSurface);
	HRESULT hr;

	IDirectDrawSurface * pdds;

	if (lpddsc->dwCaps & DDSCAPS_ZBUFFER)
	{
		if (m_pZBuffer)
		{
			m_pZBuffer->AddRef();
			*lplpdds = (IDirectDrawSurface*)m_pZBuffer;
			return S_OK;
		}
		else
		{
			*lplpdds = (IDirectDrawSurface*)NULL;
			return E_FAIL;
		}
	}

	hr = m_pDirectDrawSurface->GetAttachedSurface(lpddsc, &pdds);

	if (FAILED(hr))
		return hr;

	CDirectDrawSurface *pCSurface = m_pDirectDraw->FindSurface(pdds);

	if (pCSurface)
	{
		pCSurface->AddRef();
		*lplpdds = (IDirectDrawSurface *)pCSurface;
	}
	else
	{
		pCSurface = new CDirectDrawSurface;
		pCSurface->m_pDirectDraw = m_pDirectDraw;
		pCSurface->m_pDirectDrawSurface = pdds;

		m_pDirectDraw->AddSurfaceToList(pCSurface);

		pCSurface->AddRef();
		pCSurface->AddRef();
		*lplpdds = (IDirectDrawSurface *)pCSurface;
	}

	return S_OK;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetBltStatus(DWORD dw)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->GetBltStatus(dw);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetCaps(LPDDSCAPS lpdds)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->GetCaps(lpdds);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetClipper(LPDIRECTDRAWCLIPPER FAR* lplpddc)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->GetClipper(lplpddc);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetColorKey(DWORD dw, LPDDCOLORKEY lpddc)
{
	ASSERT(m_pDirectDrawSurface);
    return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetDC(HDC FAR * lphdc)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->GetDC(lphdc);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetFlipStatus(DWORD dw)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->GetFlipStatus(dw);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetOverlayPosition(LPLONG lpl1, LPLONG lpl2)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->GetOverlayPosition(lpl1, lpl2);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetPalette(LPDIRECTDRAWPALETTE FAR* lpddp)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->GetPalette(lpddp);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetPixelFormat(LPDDPIXELFORMAT lpddpf)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->GetPixelFormat(lpddpf);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetSurfaceDesc(LPDDSURFACEDESC lpddsd)
{
	HRESULT hr = E_FAIL;
	ASSERT(m_pDirectDrawSurface);

	if (m_pDirectDrawSurface)
	    hr = m_pDirectDrawSurface->GetSurfaceDesc(lpddsd);

	if (m_bZBuffer)
	{
	    lpddsd->ddsCaps.dwCaps |= DDSCAPS_ZBUFFER;
	    lpddsd->ddsCaps.dwCaps &=~DDSCAPS_OFFSCREENPLAIN;
	}

	return hr;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::Initialize(LPDIRECTDRAW lpdd, LPDDSURFACEDESC lpddsd)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->Initialize(lpdd, lpddsd);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::IsLost()
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->IsLost();
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::Lock(LPRECT lpr, LPDDSURFACEDESC lpddsd, DWORD dwFlags, HANDLE h)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
	{
		// turn off DX3 unsupported flags
		dwFlags &= ~(DDLOCK_NOSYSLOCK);
		HRESULT hr=m_pDirectDrawSurface->Lock(lpr, lpddsd, dwFlags, h);
		return hr;
	}
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::ReleaseDC(HDC hdc)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->ReleaseDC(hdc);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::Restore()
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->Restore();
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::SetClipper(LPDIRECTDRAWCLIPPER lpddc)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->SetClipper(lpddc);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::SetColorKey(DWORD dw, LPDDCOLORKEY lpddcc)
{
	ASSERT(m_pDirectDrawSurface);
    return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::SetOverlayPosition(LONG l, LONG l2)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->SetOverlayPosition(l, l2);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::SetPalette(LPDIRECTDRAWPALETTE lpddp)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->SetPalette(lpddp);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::Unlock(LPVOID lpv)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
	{
		HRESULT hr=m_pDirectDrawSurface->Unlock(lpv);
		return hr;
	}
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::UpdateOverlay(LPRECT lpr, LPDIRECTDRAWSURFACE lpdds, LPRECT lpr2, DWORD dw, LPDDOVERLAYFX lpddo)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->UpdateOverlay(lpr, lpdds, lpr2, dw, lpddo);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::UpdateOverlayDisplay(DWORD dw)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->UpdateOverlayDisplay(dw);
	else
		return E_FAIL;
}

//========================================================================
// This method uses a secondary surface, so it must be modified
//========================================================================
STDMETHODIMP CDirectDrawSurface::UpdateOverlayZOrder(DWORD dw, LPDIRECTDRAWSURFACE lpDDS)
{
	ASSERT(m_pDirectDrawSurface);
	IDirectDrawSurface *pDDS = NULL;

	if (lpDDS)
		pDDS = ((CDirectDrawSurface *)lpDDS)->m_pDirectDrawSurface;

	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->UpdateOverlayZOrder(dw, lpDDS);
	else
		return E_FAIL;
}

//========================================================================
// This method uses DX6 interfaces, so it must be modified
//========================================================================
STDMETHODIMP CDirectDrawSurface::AddAttachedSurface(LPDIRECTDRAWSURFACE4 lpDDS4)
{
	ASSERT(m_pDirectDrawSurface);
	IDirectDrawSurface *pDDS = NULL;

	if (lpDDS4 && ((CDirectDrawSurface *)lpDDS4)->m_bZBuffer)
	{
		RELEASE(m_pZBuffer);
		m_pZBuffer = (CDirectDrawSurface *)lpDDS4;
		m_pZBuffer->AddRef();
		return S_OK;
	}

	if (lpDDS4)
		pDDS = ((CDirectDrawSurface *)lpDDS4)->m_pDirectDrawSurface;

	if (m_pDirectDrawSurface)
	{
		HRESULT hr=m_pDirectDrawSurface->AddAttachedSurface(pDDS);

		if (SUCCEEDED(hr))
			((CDirectDrawSurface *)lpDDS4)->AddRef();

		return hr;
	}
	else
		return E_FAIL;
}

//========================================================================
// This method uses DX6 interfaces, so it must be modified
//========================================================================
STDMETHODIMP CDirectDrawSurface::Blt(LPRECT lpr, LPDIRECTDRAWSURFACE4 lpDDS4, LPRECT lpr2, DWORD dw, LPDDBLTFX lpddbfx)
{
	ASSERT(m_pDirectDrawSurface);
	IDirectDrawSurface *pDDS = NULL;

	if (lpDDS4)
		pDDS = ((CDirectDrawSurface *)lpDDS4)->m_pDirectDrawSurface;

	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->Blt(lpr, pDDS, lpr2, dw, lpddbfx);
	else
		return E_FAIL;
}

//========================================================================
// This method uses DX6 interfaces, so it must be modified
//========================================================================
STDMETHODIMP CDirectDrawSurface::BltFast(DWORD dw1, DWORD dw2, LPDIRECTDRAWSURFACE4 lpDDS4, LPRECT lpr, DWORD dw3)
{
	ASSERT(m_pDirectDrawSurface);
	IDirectDrawSurface *pDDS = NULL;

	if (lpDDS4)
		pDDS = ((CDirectDrawSurface *)lpDDS4)->m_pDirectDrawSurface;

	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->BltFast(dw1, dw2, pDDS, lpr, dw3);
	else
		return E_FAIL;
}

//========================================================================
// This method uses DX6 interfaces, so it must be modified
//========================================================================
STDMETHODIMP CDirectDrawSurface::DeleteAttachedSurface(DWORD dw, LPDIRECTDRAWSURFACE4 lpDDS4)
{
	ASSERT(m_pDirectDrawSurface);
	IDirectDrawSurface *pDDS = NULL;

	if (m_pZBuffer && m_pZBuffer == (CDirectDrawSurface *)lpDDS4)
	{
		RELEASE(m_pZBuffer);
		return S_OK;
	}

	if (lpDDS4)
		pDDS = ((CDirectDrawSurface *)lpDDS4)->m_pDirectDrawSurface;

	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->DeleteAttachedSurface(dw, pDDS);
	else
		return E_FAIL;
}

//========================================================================
// $TODO
//========================================================================
STDMETHODIMP CDirectDrawSurface::EnumAttachedSurfaces(LPVOID lpv, LPDDENUMSURFACESCALLBACK2 lpdden)
{
	ASSERT(0);
	__asm mov ds:[0], 0;
	return E_NOTIMPL;

//	ASSERT(m_pDirectDrawSurface);
//	if (m_pDirectDrawSurface)
//		return m_pDirectDrawSurface->EnumAttachedSurfaces(lpv, lpdden);
//	else
//		return E_FAIL;
}

//========================================================================
// $TODO
//========================================================================
STDMETHODIMP CDirectDrawSurface::EnumOverlayZOrders(DWORD dw,LPVOID lpv, LPDDENUMSURFACESCALLBACK2 lpdde)
{
	ASSERT(0);
	__asm mov ds:[0], 0;
	return E_NOTIMPL;

//	ASSERT(m_pDirectDrawSurface);
//	if (m_pDirectDrawSurface)
//		return m_pDirectDrawSurface->EnumOverlayZOrders(dw, lpv, lpdde);
//	else
//		return E_FAIL;
}

//========================================================================
// This method uses DX6 interfaces, so it must be modified
//========================================================================
STDMETHODIMP CDirectDrawSurface::Flip(LPDIRECTDRAWSURFACE4 lpDDS4, DWORD dw)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->Flip(GetDDS(lpDDS4), dw);
	else
		return E_FAIL;
}

//========================================================================
// This method uses DX6 interfaces, so it must be modified
// $TODO
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetAttachedSurface(LPDDSCAPS2 lpddc, LPDIRECTDRAWSURFACE4 FAR *lplpdds)
{
	ASSERT(m_pDirectDrawSurface);
	HRESULT hr;

	if (lpddc->dwCaps & DDSCAPS_ZBUFFER)
	{
		if (m_pZBuffer)
		{
			m_pZBuffer->AddRef();
			*lplpdds = (IDirectDrawSurface4 *)m_pZBuffer;
			return S_OK;
		}
		else
		{
			*lplpdds = (IDirectDrawSurface4 *)NULL;
			return E_FAIL;
		}
	}

	IDirectDrawSurface * pdds;
	hr = m_pDirectDrawSurface->GetAttachedSurface((DDSCAPS *)lpddc, &pdds);

	if (FAILED(hr))
		return hr;

	CDirectDrawSurface *pCSurface = m_pDirectDraw->FindSurface(pdds);

	if (pCSurface)
	{
		pCSurface->AddRef();
		*lplpdds = (IDirectDrawSurface4 *)pCSurface;
	}
	else
	{
		pCSurface = new CDirectDrawSurface;
		pCSurface->m_pDirectDraw = m_pDirectDraw;
		pCSurface->m_pDirectDrawSurface = pdds;

		m_pDirectDraw->AddSurfaceToList(pCSurface);

		pCSurface->AddRef();
		pCSurface->AddRef();
		*lplpdds = (IDirectDrawSurface4 *)pCSurface;
	}

	return S_OK;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetCaps(LPDDSCAPS2 lpddc)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->GetCaps((LPDDSCAPS)lpddc);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetSurfaceDesc(LPDDSURFACEDESC2 lpddsd)
{
	ASSERT(m_pDirectDrawSurface);

	if (m_pDirectDrawSurface)
	{
		HRESULT hr;
		DDSURFACEDESC ddsd = {sizeof(ddsd)};
		hr = m_pDirectDrawSurface->GetSurfaceDesc(&ddsd);

		lpddsd->dwFlags				= ddsd.dwFlags;
		lpddsd->dwHeight			= ddsd.dwHeight;
		lpddsd->dwWidth				= ddsd.dwWidth;
		lpddsd->lPitch				= ddsd.lPitch;
		lpddsd->dwBackBufferCount	= ddsd.dwBackBufferCount;
		lpddsd->dwRefreshRate		= ddsd.dwRefreshRate;
		lpddsd->dwAlphaBitDepth		= ddsd.dwAlphaBitDepth;
		lpddsd->dwReserved			= ddsd.dwReserved;
		lpddsd->lpSurface			= ddsd.lpSurface;
		lpddsd->ddckCKDestOverlay	= ddsd.ddckCKDestOverlay;
		lpddsd->ddckCKDestBlt		= ddsd.ddckCKDestBlt;
		lpddsd->ddckCKSrcOverlay	= ddsd.ddckCKSrcOverlay;
		lpddsd->ddckCKSrcBlt		= ddsd.ddckCKSrcBlt;
		lpddsd->ddpfPixelFormat		= ddsd.ddpfPixelFormat;
		lpddsd->ddsCaps.dwCaps		= ddsd.ddsCaps.dwCaps;

        if (lpddsd->dwSize >= sizeof(DDSURFACEDESC2))
        {
		    lpddsd->ddsCaps.dwCaps2		= 0;
		    lpddsd->ddsCaps.dwCaps3		= 0;
		    lpddsd->ddsCaps.dwCaps4		= 0;
		    lpddsd->dwTextureStage		= 0;
        }

		if (m_bZBuffer)
		{
		    lpddsd->ddsCaps.dwCaps |= DDSCAPS_ZBUFFER;
		    lpddsd->ddsCaps.dwCaps &=~DDSCAPS_OFFSCREENPLAIN;
		}

		return hr;

	}
	else
		return E_FAIL;
}

//========================================================================
// $TODO
//========================================================================
STDMETHODIMP CDirectDrawSurface::Initialize(LPDIRECTDRAW lpdd, LPDDSURFACEDESC2 lpdds)
{
	ASSERT(0);
	__asm mov ds:[0], 0;
	return E_NOTIMPL;

//	ASSERT(m_pDirectDrawSurface);
//	if (m_pDirectDrawSurface)
//		return m_pDirectDrawSurface->Initialize(lpdd, lpdds);
//	else
//		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::Lock(LPRECT lpr, LPDDSURFACEDESC2 lpddsd, DWORD dw, HANDLE h)
{
	DDSURFACEDESC ddsd = {sizeof(ddsd)};

	if (lpddsd->dwSize != sizeof(DDSURFACEDESC2))
		return DDERR_INVALIDPARAMS;

	//DWORD			dwFlags;
	//DWORD			dwHeight;
	//DWORD			dwWidth;
	//LONG       		lPitch;
	//DWORD			dwBackBufferCount;
	//DWORD			dwRefreshRate;
	//DWORD			dwAlphaBitDepth;
	//DWORD			dwReserved;
	//LPVOID			lpSurface;
	//DDCOLORKEY		ddckCKDestOverlay;
	//DDCOLORKEY		ddckCKDestBlt;
	//DDCOLORKEY		ddckCKSrcOverlay;
	//DDCOLORKEY		ddckCKSrcBlt;
	//DDPIXELFORMAT	ddpfPixelFormat;
	//DDSCAPS			ddsCaps;
	//
	//DWORD			dwSize;
	//DWORD			dwFlags;
	//DWORD			dwHeight;
	//DWORD			dwWidth;
	//LONG			lPitch;
	//DWORD			dwBackBufferCount;
	//DWORD			dwRefreshRate;
	//DWORD			dwAlphaBitDepth;
	//DWORD			dwReserved;
	//LPVOID			lpSurface;
	//DDCOLORKEY		ddckCKDestOverlay;
	//DDCOLORKEY		ddckCKDestBlt;
	//DDCOLORKEY		ddckCKSrcOverlay;
	//DDCOLORKEY		ddckCKSrcBlt;
	//DDPIXELFORMAT	ddpfPixelFormat;
	//DDSCAPS2		ddsCaps;
	//DWORD			dwTextureStage;

	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
	{
		HRESULT hr;
		// turn off DX3 unsupported flags
		dw &= ~(DDLOCK_NOSYSLOCK);
		hr = m_pDirectDrawSurface->Lock(lpr, &ddsd, dw, h);

		lpddsd->dwFlags				= ddsd.dwFlags;
		lpddsd->dwHeight			= ddsd.dwHeight;
		lpddsd->dwWidth				= ddsd.dwWidth;
		lpddsd->lPitch				= ddsd.lPitch;
		lpddsd->dwBackBufferCount	= ddsd.dwBackBufferCount;
		lpddsd->dwRefreshRate		= ddsd.dwRefreshRate;
		lpddsd->dwAlphaBitDepth		= ddsd.dwAlphaBitDepth;
		lpddsd->dwReserved			= ddsd.dwReserved;
		lpddsd->lpSurface			= ddsd.lpSurface;
		lpddsd->ddckCKDestOverlay	= ddsd.ddckCKDestOverlay;
		lpddsd->ddckCKDestBlt		= ddsd.ddckCKDestBlt;
		lpddsd->ddckCKSrcOverlay	= ddsd.ddckCKSrcOverlay;
		lpddsd->ddckCKSrcBlt		= ddsd.ddckCKSrcBlt;
		lpddsd->ddpfPixelFormat		= ddsd.ddpfPixelFormat;
		lpddsd->ddsCaps.dwCaps		= ddsd.ddsCaps.dwCaps;
		lpddsd->ddsCaps.dwCaps2		= 0;
		lpddsd->ddsCaps.dwCaps3		= 0;
		lpddsd->ddsCaps.dwCaps4		= 0;
		lpddsd->dwTextureStage		= 0;

		return hr;
	}
	else
		return E_FAIL;

}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::Unlock(LPRECT lpr)
{
	ASSERT(m_pDirectDrawSurface);
	if (m_pDirectDrawSurface)
	{
		HRESULT hr=m_pDirectDrawSurface->Unlock(lpr);
		return hr;
	}
	else
		return E_FAIL;
}

//========================================================================
// This method uses DX6 interfaces, so it must be modified
//========================================================================
STDMETHODIMP CDirectDrawSurface::UpdateOverlay(LPRECT lpr, LPDIRECTDRAWSURFACE4 lpDDS4, LPRECT lpr2, DWORD dw, LPDDOVERLAYFX lpddo)
{
	ASSERT(m_pDirectDrawSurface);
	IDirectDrawSurface *pDDS = NULL;

	if (lpDDS4)
		pDDS = ((CDirectDrawSurface *)lpDDS4)->m_pDirectDrawSurface;

	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->UpdateOverlay(lpr, pDDS, lpr2, dw, lpddo);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::UpdateOverlayZOrder(DWORD dw, LPDIRECTDRAWSURFACE4 lpDDS4)
{
	ASSERT(m_pDirectDrawSurface);
	IDirectDrawSurface *pDDS = NULL;

	if (lpDDS4)
		pDDS = ((CDirectDrawSurface *)lpDDS4)->m_pDirectDrawSurface;

	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->UpdateOverlayZOrder(dw, pDDS);
	else
		return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetDDInterface(LPVOID FAR *lplp)
{
	ASSERT(m_pDirectDraw);

	*lplp = (void *)(IDirectDraw4 *)m_pDirectDraw;
	m_pDirectDraw->AddRef();

	return S_OK;
}

//========================================================================
// not implemented in DX3
//========================================================================
STDMETHODIMP CDirectDrawSurface::PageLock(DWORD dw)
{
	ASSERT(m_pDirectDrawSurface);
	return E_NOTIMPL;
}

//========================================================================
// not implemented in DX3
//========================================================================
STDMETHODIMP CDirectDrawSurface::PageUnlock(DWORD dw)
{
	ASSERT(m_pDirectDrawSurface);
	return E_NOTIMPL;
}

//========================================================================
// not implemented in DX3
//========================================================================
STDMETHODIMP CDirectDrawSurface::SetSurfaceDesc(LPDDSURFACEDESC2 lpddsd, DWORD dw)
{
	ASSERT(m_pDirectDrawSurface);
	return E_NOTIMPL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::SetPrivateData(REFGUID guid, LPVOID lpv, DWORD dw, DWORD dw2)
{
	ASSERT(m_pDirectDrawSurface);
	return E_NOTIMPL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetPrivateData(REFGUID guid, LPVOID lpv, LPDWORD lpv2)
{
	ASSERT(m_pDirectDrawSurface);
	return E_NOTIMPL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::FreePrivateData(REFGUID guid)
{
	ASSERT(m_pDirectDrawSurface);
	return E_NOTIMPL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetUniquenessValue(LPDWORD lpdw)
{
	ASSERT(m_pDirectDrawSurface);
	return E_NOTIMPL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::ChangeUniquenessValue()
{
	ASSERT(m_pDirectDrawSurface);
	return E_NOTIMPL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::GetHandle(LPDIRECT3DDEVICE2,LPD3DTEXTUREHANDLE)
{
	return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::PaletteChanged(DWORD dw1,DWORD dw2)
{
	return E_FAIL;
}

//========================================================================
//========================================================================
STDMETHODIMP CDirectDrawSurface::Load(LPDIRECT3DTEXTURE2 psrc2)
{
	HRESULT hr;

	IDirect3DTexture*	psrc1 = NULL;
	IDirect3DTexture*	pdst1 = NULL;

	hr = ((CDirectDrawSurface *)psrc2)->m_pDirectDrawSurface->QueryInterface(IID_IDirect3DTexture, (void **)&psrc1);

	if (FAILED(hr))
		goto done;

	hr = m_pDirectDrawSurface->QueryInterface(IID_IDirect3DTexture, (void **)&pdst1);

	if (FAILED(hr))
		goto done;

	hr = pdst1->Load(psrc1);

done:
	RELEASE(pdst1);
	RELEASE(psrc1);

	return hr;
}


//========================================================================
//========================================================================
HRESULT CDirectDrawSurface::CreateSurface(CDirectDraw *pdd, DDSURFACEDESC *pddsd)
{
	return CreateSurface(pdd, (DDSURFACEDESC2 *)pddsd);
}

//========================================================================
//========================================================================
HRESULT CDirectDrawSurface::CreateSurface(CDirectDraw *pdd, DDSURFACEDESC2 *pddsd)
{
	HRESULT hr;

	ASSERT(NULL == m_pDirectDrawSurface);
	ASSERT(NULL == m_pDirectDraw);

	RELEASE(m_pDirectDrawSurface);
	RELEASE(m_pDirectDraw);

	DDSURFACEDESC ddsd = {sizeof(ddsd)};

	if (pddsd->dwSize == sizeof(DDSURFACEDESC2))
	{
		ddsd.dwFlags			= pddsd->dwFlags;
		ddsd.dwHeight			= pddsd->dwHeight;
		ddsd.dwWidth			= pddsd->dwWidth;
		ddsd.lPitch				= pddsd->lPitch;
		ddsd.dwBackBufferCount	= pddsd->dwBackBufferCount;
		ddsd.dwRefreshRate		= pddsd->dwRefreshRate;
		ddsd.dwAlphaBitDepth	= pddsd->dwAlphaBitDepth;
		ddsd.dwReserved			= pddsd->dwReserved;
		ddsd.lpSurface			= pddsd->lpSurface;
		ddsd.ddckCKDestOverlay	= pddsd->ddckCKDestOverlay;
		ddsd.ddckCKDestBlt		= pddsd->ddckCKDestBlt;
		ddsd.ddckCKSrcOverlay	= pddsd->ddckCKSrcOverlay;
		ddsd.ddckCKSrcBlt		= pddsd->ddckCKSrcBlt;
		ddsd.ddpfPixelFormat	= pddsd->ddpfPixelFormat;
		ddsd.ddsCaps.dwCaps		= pddsd->ddsCaps.dwCaps;
	}
	else if (pddsd->dwSize == sizeof(DDSURFACEDESC))
	{
		ddsd = *(DDSURFACEDESC *)pddsd;
	}
	else
		return DDERR_INVALIDPARAMS;

	//
	// see if we're supposed to create a z-buffer, and if so,
	// change it to an off screen plain system memory surface,
	// but mark it as a z buffer for our purposes
	//
	if (ddsd.ddsCaps.dwCaps & DDSCAPS_ZBUFFER)
	{
		ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_ZBUFFER | DDSCAPS_VIDEOMEMORY);
		ddsd.ddsCaps.dwCaps |= DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;

		ddsd.ddpfPixelFormat.dwSize		= sizeof(ddsd.ddpfPixelFormat);
		ddsd.ddpfPixelFormat.dwFlags	&= ~(DDPF_ZBUFFER);
		ddsd.ddpfPixelFormat.dwFlags	|= DDPF_RGB;
		ddsd.ddpfPixelFormat.dwRGBBitCount	= 16;
		ddsd.ddpfPixelFormat.dwRBitMask		= 0x7C00;
		ddsd.ddpfPixelFormat.dwGBitMask		= 0x03E0;
		ddsd.ddpfPixelFormat.dwBBitMask		= 0x001F;

		m_bZBuffer = true;
	}

	if ((ddsd.ddsCaps.dwCaps & DDSCAPS_TEXTURE) && (ddsd.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY))
	{
		// always make a system memory texture, we always do sofware in this path
		ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM | DDSCAPS_NONLOCALVIDMEM);
		ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
		ddsd.ddsCaps.dwCaps |= DDSCAPS_ALLOCONLOAD;
	}
	else
	{
		// always make a system memory surface, we always do sofware in this path
		ddsd.ddsCaps.dwCaps &= ~(DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM | DDSCAPS_NONLOCALVIDMEM);
		ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
	}

	m_pDirectDraw = pdd;

    if (FAILED(hr=m_pDirectDraw->m_pDirectDraw->CreateSurface(&ddsd, &m_pDirectDrawSurface, NULL)))
	{
		return hr;
	}

	ASSERT(GetRef(m_pDirectDrawSurface) == 1);
	m_pDirectDraw->AddSurfaceToList(this);
////m_pDirectDraw->AddRef();

	return hr;
}

//========================================================================
// IDirectDrawSurface2 methods
//========================================================================
STDMETHODIMP CDirectDrawSurface::Flip(LPDIRECTDRAWSURFACE2 pdds, DWORD dwFlags)
{
	return Flip(GetDDS(pdds), dwFlags);
}
STDMETHODIMP CDirectDrawSurface::Blt(LPRECT prc1,LPDIRECTDRAWSURFACE2 pdds, LPRECT prc2,DWORD dw, LPDDBLTFX pddfx)
{
	return Blt(prc1, GetDDS(pdds), prc2, dw, pddfx);
}
STDMETHODIMP CDirectDrawSurface::BltFast(DWORD dw1,DWORD dw2,LPDIRECTDRAWSURFACE2 pdds, LPRECT prc,DWORD dw3)
{
	return BltFast(dw1,dw2,GetDDS(pdds),prc,dw3);
}
STDMETHODIMP CDirectDrawSurface::AddAttachedSurface(LPDIRECTDRAWSURFACE2)
{
    return E_NOTIMPL;
}
STDMETHODIMP CDirectDrawSurface::DeleteAttachedSurface(DWORD,LPDIRECTDRAWSURFACE2)
{
    return E_NOTIMPL;
}
STDMETHODIMP CDirectDrawSurface::GetAttachedSurface(LPDDSCAPS, LPDIRECTDRAWSURFACE2 FAR *)
{
    return E_NOTIMPL;
}
STDMETHODIMP CDirectDrawSurface::UpdateOverlay(LPRECT, LPDIRECTDRAWSURFACE2,LPRECT,DWORD, LPDDOVERLAYFX)
{
    return E_NOTIMPL;
}
STDMETHODIMP CDirectDrawSurface::UpdateOverlayZOrder(DWORD, LPDIRECTDRAWSURFACE2)
{
    return E_NOTIMPL;
}


#ifdef USE_DX7_INTERFACE
STDMETHODIMP CDirectDrawSurface::AddAttachedSurface(LPDIRECTDRAWSURFACE7 lpdds)
{
	ASSERT(m_pDirectDrawSurface);

	if (lpdds && ((CDirectDrawSurface *)lpdds)->m_bZBuffer)
	{
		RELEASE(m_pZBuffer);
		m_pZBuffer = (CDirectDrawSurface *)lpdds;
		m_pZBuffer->AddRef();
		return S_OK;
	}

	if (m_pDirectDrawSurface)
	{
		HRESULT hr=m_pDirectDrawSurface->AddAttachedSurface(GetDDS(lpdds));

		if (SUCCEEDED(hr))
			((CDirectDrawSurface *)lpdds)->AddRef();

		return hr;
	}
	else
		return E_FAIL;
}

STDMETHODIMP CDirectDrawSurface::Blt(LPRECT lpr, LPDIRECTDRAWSURFACE7 lpDDS4, LPRECT lpr2, DWORD dw, LPDDBLTFX lpddbfx)
{
	if (m_pDirectDrawSurface)
	{
		if (lpDDS4)
			return m_pDirectDrawSurface->Blt(lpr, ((CDirectDrawSurface *)lpDDS4)->m_pDirectDrawSurface, lpr2, dw, lpddbfx);
		else
			return m_pDirectDrawSurface->Blt(lpr, NULL, lpr2, dw, lpddbfx);
	}
	else
		return E_FAIL;
}

STDMETHODIMP CDirectDrawSurface::BltFast(DWORD,DWORD,LPDIRECTDRAWSURFACE7, LPRECT,DWORD)
{
	ASSERT(0);
	return E_NOTIMPL;
}

STDMETHODIMP CDirectDrawSurface::DeleteAttachedSurface(DWORD dw, LPDIRECTDRAWSURFACE7 lpDDS4)
{
	ASSERT(m_pDirectDrawSurface);
	IDirectDrawSurface *pDDS = NULL;

	if (m_pZBuffer && m_pZBuffer == (CDirectDrawSurface *)lpDDS4)
	{
		RELEASE(m_pZBuffer);
		return S_OK;
	}

	if (lpDDS4)
		pDDS = ((CDirectDrawSurface *)lpDDS4)->m_pDirectDrawSurface;

	if (m_pDirectDrawSurface)
		return m_pDirectDrawSurface->DeleteAttachedSurface(dw, pDDS);
	else
		return E_FAIL;
}

STDMETHODIMP CDirectDrawSurface::EnumAttachedSurfaces(LPVOID,LPDDENUMSURFACESCALLBACK7)
{
	ASSERT(0);
	return E_NOTIMPL;
}

STDMETHODIMP CDirectDrawSurface::EnumOverlayZOrders(DWORD,LPVOID,LPDDENUMSURFACESCALLBACK7)
{
	ASSERT(0);
	return E_NOTIMPL;
}

STDMETHODIMP CDirectDrawSurface::Flip(LPDIRECTDRAWSURFACE7, DWORD)
{
	ASSERT(0);
	return E_NOTIMPL;
}

STDMETHODIMP CDirectDrawSurface::GetAttachedSurface(LPDDSCAPS2 lpddsc, LPDIRECTDRAWSURFACE7 FAR *lplpdds)
{
	ASSERT(m_pDirectDrawSurface);
	HRESULT hr;

	IDirectDrawSurface * pdds;

	if (lpddsc->dwCaps & DDSCAPS_ZBUFFER)
	{
		if (m_pZBuffer)
		{
			m_pZBuffer->AddRef();
			*lplpdds = (IDirectDrawSurface7 *)m_pZBuffer;
			return S_OK;
		}
		else
		{
			*lplpdds = (IDirectDrawSurface7 *)NULL;
			return E_FAIL;
		}
	}

	hr = m_pDirectDrawSurface->GetAttachedSurface((DDSCAPS *)lpddsc, &pdds);

	if (FAILED(hr))
		return hr;

	CDirectDrawSurface *pCSurface = m_pDirectDraw->FindSurface(pdds);

	if (pCSurface)
	{
		pCSurface->AddRef();
		*lplpdds = (IDirectDrawSurface7 *)pCSurface;
	}
	else
	{
		pCSurface = new CDirectDrawSurface;
		pCSurface->m_pDirectDraw = m_pDirectDraw;
		pCSurface->m_pDirectDrawSurface = pdds;

		m_pDirectDraw->AddSurfaceToList(pCSurface);

		pCSurface->AddRef();
		pCSurface->AddRef();
		*lplpdds = (IDirectDrawSurface7 *)pCSurface;
	}

	return S_OK;
}

STDMETHODIMP CDirectDrawSurface::UpdateOverlay(LPRECT, LPDIRECTDRAWSURFACE7,LPRECT,DWORD, LPDDOVERLAYFX)
{
	ASSERT(0);
	return E_NOTIMPL;
}

STDMETHODIMP CDirectDrawSurface::UpdateOverlayZOrder(DWORD, LPDIRECTDRAWSURFACE7)
{
	ASSERT(0);
	return E_NOTIMPL;
}

STDMETHODIMP CDirectDrawSurface::SetPriority(DWORD)
{
	ASSERT(0);
	return E_NOTIMPL;
}

STDMETHODIMP CDirectDrawSurface::GetPriority(LPDWORD)
{
	ASSERT(0);
	return E_NOTIMPL;
}

STDMETHODIMP CDirectDrawSurface::SetLOD(DWORD)
{
	ASSERT(0);
	return E_NOTIMPL;
}

STDMETHODIMP CDirectDrawSurface::GetLOD(LPDWORD)
{
	ASSERT(0);
	return E_NOTIMPL;
}

#endif

