/*
 * Direct3D8 surfaces
 * Copyright (c) 2002, TransGaming Technologies Inc.
 */

#include "config.h"

#include "winbase.h"
#include "d3d8.h"
#include "wine/debug.h"

#include "d3d8_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(ddraw);

static DDPIXELFORMAT fmts[100] = {
/* 0 */
  {0}, {0}, {0}, {0}, {0},
  {0}, {0}, {0}, {0}, {0},
/* 10 */
  {0}, {0}, {0}, {0}, {0},
  {0}, {0}, {0}, {0}, {0},
/* 20 */
  {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, {24},
   {0xff0000},   {0x00ff00},   {0x0000ff},   {0x000000}},   /* R8G8B8 */
  {sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_ALPHAPIXELS, 0, {32},
   {0x00ff0000}, {0x0000ff00}, {0x000000ff}, {0xff000000}}, /* A8R8G8B8 */
  {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, {32},
   {0x00ff0000}, {0x0000ff00}, {0x000000ff}, {0x00000000}}, /* X8R8G8B8 */
  {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, {16},
   {0xf800},     {0x07e0},     {0x001f},     {0x0000}},     /* R5G6B5 */
  {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, {16},
   {0x7c00},     {0x03e0},     {0x001f},     {0x0000}},     /* X1R5G5B5 */
  {sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_ALPHAPIXELS, 0, {16},
   {0x7c00},     {0x03e0},     {0x001f},     {0x8000}},     /* A1R5G6B5 */
  {sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_ALPHAPIXELS, 0, {16},
   {0x0f00},     {0x00f0},     {0x000f},     {0xf000}},     /* A4R4G4B4 */
  {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, {8},
   {0xe0},       {0x1c},       {0x03},       {0x00}},       /* R3G3B2 */
  {sizeof(DDPIXELFORMAT), DDPF_ALPHA, 0, {8},
   {0x00},       {0x00},       {0x00},       {0xff}},       /* A8 */
  {sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_ALPHAPIXELS, 0, {16},
   {0x00e0},     {0x001c},     {0x0003},     {0xff00}},     /* A8R3G3B2 */
/* 30 */
  {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, {16},
   {0x0f00},     {0x00f0},     {0x000f},     {0x0000}},     /* X4R4G4B4 */
  {sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_ALPHAPIXELS, 0, {32},
   {0x3ff00000}, {0x000ffc00}, {0x000003ff}, {0xc0000000}}, /* A2B10G10R10 */
  {0}, {0},
  {sizeof(DDPIXELFORMAT), DDPF_RGB, 0, {32},
   {0x0000ffff}, {0xffff0000}, {0x00000000}, {0x00000000}}, /* G16R16 */
  {0}, {0}, {0}, {0}, {0},
/* 40 */
  {sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_PALETTEINDEXED8 | DDPF_ALPHAPIXELS, 0, {16},
   {0x0000},     {0x0000},     {0x0000},     {0xff00}},     /* A8P8 */
  {sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_PALETTEINDEXED8, 0, {8},
   {0x00},       {0x00},       {0x00},       {0x00}},       /* P8 */
  {0}, {0}, {0},
  {0}, {0}, {0}, {0}, {0},
/* 50 */
  {sizeof(DDPIXELFORMAT), DDPF_LUMINANCE, 0, {8},
   {0xff},       {0x00},       {0x00},       {0x00}},       /* L8 */
  {sizeof(DDPIXELFORMAT), DDPF_LUMINANCE | DDPF_ALPHAPIXELS, 0, {16},
   {0x00ff},     {0x0000},     {0x0000},     {0xff00}},     /* A8L8 */
  {sizeof(DDPIXELFORMAT), DDPF_LUMINANCE | DDPF_ALPHAPIXELS, 0, {8},
   {0x0f},       {0x00},       {0x00},       {0xf0}},       /* A4L4 */
  {0}, {0},
  {0}, {0}, {0}, {0}, {0},
/* 60 */
  {sizeof(DDPIXELFORMAT), DDPF_BUMPDUDV, 0, {16},
   {0x00ff},     {0xff00},     {0x0000},     {0x0000}},     /* V8U8 */
  {sizeof(DDPIXELFORMAT), DDPF_BUMPDUDV | DDPF_BUMPLUMINANCE, 0, {16},
   {0x001f},     {0x03e0},     {0xfc00},     {0x0000}},     /* L6V5U5 */
  {sizeof(DDPIXELFORMAT), DDPF_BUMPDUDV | DDPF_BUMPLUMINANCE, 0, {32},
   {0x000000ff}, {0x0000ff00}, {0x00ff0000}, {0x00000000}}, /* X8L8V8U8 */
  {0}, /* Q8L8V8U8 - what's that? */
  {sizeof(DDPIXELFORMAT), DDPF_BUMPDUDV, 0, {32},
   {0x0000ffff}, {0xffff0000}, {0x00000000}, {0x00000000}}, /* V16U16 */
  {0}, /* W11V11U10 - what's that? */
  {0},
  {0}, /* A2W10V10U10 - what's that? */
  {0}, {0},
/* 70 */
  {sizeof(DDPIXELFORMAT), DDPF_ZBUFFER, 0, {16},
   {0},          {0xffff},     {0x0000},     {0x0000}},     /* D16_LOCKABLE */
  {sizeof(DDPIXELFORMAT), DDPF_ZBUFFER, 0, {32},
   {0},          {0xffffffff}, {0x00000000}, {0x0000}},     /* D32 */
  {0},
  {sizeof(DDPIXELFORMAT), DDPF_ZBUFFER | DDPF_STENCILBUFFER, 0, {16},
   {1},          {0xfffe},     {0x0001},     {0x0000}},     /* D15S1 */
  {0},
  {sizeof(DDPIXELFORMAT), DDPF_ZBUFFER | DDPF_STENCILBUFFER, 0, {32},
   {8},          {0xffffff00}, {0x000000ff}, {0x0000}},     /* D24S8 */
  {0},
  {sizeof(DDPIXELFORMAT), DDPF_ZBUFFER, 0, {32},
   {8},          {0xffffff00}, {0x00000000}, {0x0000}},     /* D24X8 */
  {0},
  {sizeof(DDPIXELFORMAT), DDPF_ZBUFFER | DDPF_STENCILBUFFER, 0, {32},
   {8},          {0xffffff00}, {0x0000000f}, {0x0000}},     /* D24X4S4 */
/* 80 */
  {sizeof(DDPIXELFORMAT), DDPF_ZBUFFER, 0, {16},
   {0},          {0xffff},     {0x0000},     {0x0000}},     /* D16 */
  {0}, {0}, {0}, {0},
  {0}, {0}, {0}, {0}, {0},
/* 90 */
  {0}, {0}, {0}, {0}, {0},
  {0}, {0}, {0}, {0}, {0}
};

static BOOL IsLinearFormat(D3DFORMAT Format)
{
  return Format >= 100;
}

D3DFORMAT D3D8_FindPixelFormat(LPDDPIXELFORMAT pfmt)
{
  D3DFORMAT fmt;
  if (pfmt->dwFlags & DDPF_FOURCC)
    return pfmt->dwFourCC;
  for (fmt=0; fmt<100; fmt++)
    if (!memcmp(&fmts[fmt], pfmt, sizeof(DDPIXELFORMAT)))
      return fmt;
  return D3DFMT_UNKNOWN;
}

void D3D8_GetPixelFormat(LPDDPIXELFORMAT pfmt, D3DFORMAT fmt)
{
  if (IsLinearFormat(fmt)) {
    memset(pfmt, 0, sizeof(DDPIXELFORMAT));
    pfmt->dwSize = sizeof(DDPIXELFORMAT);
    pfmt->dwFlags = DDPF_FOURCC;
    pfmt->dwFourCC = fmt;
  }
  else *pfmt = fmts[fmt];
}

static UINT CalcPitch(UINT Width, D3DFORMAT Format)
{
  switch (Format) {
  case D3DFMT_DXT1:
    return ((Width+3)>>2) * 8;
  case D3DFMT_DXT2:
  case D3DFMT_DXT3:
  case D3DFMT_DXT4:
  case D3DFMT_DXT5:
    return ((Width+3)>>2) * 16;
  default:
    if (Format < 100)
      return ((fmts[Format].u1.dwRGBBitCount*Width+31)>>3)&~3;
    return 0;
  }
}

static UINT CalcLinearSize(UINT Width, UINT Height, D3DFORMAT Format)
{
  switch (Format) {
  case D3DFMT_DXT1:
  case D3DFMT_DXT2:
  case D3DFMT_DXT3:
  case D3DFMT_DXT4:
  case D3DFMT_DXT5:
    return CalcPitch(Width, Format) * ((Height+3)>>2);
  default:
    if (Format < 100)
      return CalcPitch(Width, Format) * Height;
    return 0;
  }
}

static UINT CalcOffset(IDirect3DSurface8Impl* This, INT X, INT Y)
{
  switch (This->Format) {
  case D3DFMT_DXT1:
    return (Y>>2) * This->Pitch + (X>>2) * 8;
  case D3DFMT_DXT2:
  case D3DFMT_DXT3:
  case D3DFMT_DXT4:
  case D3DFMT_DXT5:
    return (Y>>2) * This->Pitch + (X>>2) * 16;
  default:
    if (This->Format < 100)
      return Y * This->Pitch + ((This->t.gbl.ddpfSurface.u1.dwRGBBitCount*X+7)>>3);
    return 0;
  }
}

HRESULT WINAPI Direct3DSurface8_QueryInterface(LPDIRECT3DSURFACE8 iface,
					       REFIID riid,
					       LPVOID* obj)
{
  ICOM_THIS(IDirect3DSurface8Impl, iface);
  TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), obj);
  if (IsEqualGUID(&IID_IUnknown, riid) ||
      IsEqualGUID(&IID_IDirect3DSurface8, riid)) {
    This->ref++;
    *obj = This;
    return S_OK;
  }
  else {
    FIXME("(%p)->(%s): no interface", iface, debugstr_guid(riid));
    return E_NOINTERFACE;
  }
}

ULONG WINAPI Direct3DSurface8_AddRef(LPDIRECT3DSURFACE8 iface)
{
  ICOM_THIS(IDirect3DSurface8Impl, iface);
  TRACE("(%p)->()\n", iface);
  return ++This->ref - This->intref;
}

ULONG WINAPI Direct3DSurface8_Release(LPDIRECT3DSURFACE8 iface)
{
  ICOM_THIS(IDirect3DSurface8Impl, iface);
  DDHAL_DESTROYSURFACEDATA data;
  DWORD intref = This->intref;

  TRACE("(%p)->()\n", iface);
  if (--This->ref) return This->ref - intref;

  if (This->t.lcl.ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY)
  {
      DWORD alloc;
      if (This->t.gbl.ddpfSurface.dwFlags & DDPF_FOURCC)
      {
          alloc = This->t.gbl.u4.dwLinearSize;
      } else {
          alloc = This->Pitch * This->t.gbl.wWidth;
      }
      This->parent->local.lpGbl->ddCaps.dwVidMemFree += alloc;
  }

  /* destroy HAL surface */
  data.lpDD = This->parent->local.lpGbl;
  data.lpDDSurface = &This->t.lcl;
  data.ddRVal = 0;
  data.DestroySurface = This->parent->local.lpGbl->lpDDCBtmp->HALDDSurface.DestroySurface;
  data.DestroySurface(&data);

  /* clear any remaining references to us */
  if (This->parent->primary == iface) This->parent->primary = NULL;
  if (This->parent->backbuf == iface) This->parent->backbuf = NULL;
  if (This->parent->zbuffer == iface) {
    This->parent->zbuffer = NULL;
    This->parent->d3dp.zbuf = NULL;
  }
  if (This->parent->target == iface) {
    This->parent->target = NULL;
    This->parent->d3dp.surf = NULL;
  }

  if (This->t.gbl_more.hKernelSurface)
    DeleteObject(This->t.gbl_more.hKernelSurface);

  if (This->own_memory)
    if (This->t.gbl.fpVidMem)
      HeapFree(surface_heap, 0, (LPVOID)This->t.gbl.fpVidMem);
  HeapFree(GetProcessHeap(), 0, This);
  return -intref;
}

HRESULT WINAPI Direct3DSurface8_GetDevice(LPDIRECT3DSURFACE8 iface,
					  IDirect3DDevice8** ppDevice)
{
  ICOM_THIS(IDirect3DSurface8Impl, iface);
  IDirect3DDevice8* pDevice;

  TRACE("(%p)->(%p)\n", iface, ppDevice);

  pDevice = (IDirect3DDevice8*)This->parent;
  IDirect3DDevice8_AddRef(pDevice);
  *ppDevice = pDevice;

  TRACE("=> %p\n", *ppDevice);
  return D3D_OK;
}

HRESULT WINAPI Direct3DSurface8_GetContainer(LPDIRECT3DSURFACE8 iface,
					     REFIID riid,
					     void** ppContainer)
{
  ICOM_THIS(IDirect3DSurface8Impl, iface);

  TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppContainer);

  TRACE("=> %p\n", This->container);
  return IUnknown_QueryInterface(This->container, riid, ppContainer);
}

HRESULT WINAPI Direct3DSurface8_GetDesc(LPDIRECT3DSURFACE8 iface,
					D3DSURFACE_DESC* pDesc)
{
  ICOM_THIS(IDirect3DSurface8Impl, iface);

  TRACE("(%p)->(%p)\n", iface, pDesc);
  pDesc->Format = This->Format;
  pDesc->Type = D3DRTYPE_SURFACE;
  pDesc->Usage = 0;
  if (This->t.lcl.ddsCaps.dwCaps & DDSCAPS_3DDEVICE)  pDesc->Usage |= D3DUSAGE_RENDERTARGET;
  if (This->t.lcl.ddsCaps.dwCaps & DDSCAPS_ZBUFFER)   pDesc->Usage |= D3DUSAGE_DEPTHSTENCIL;
  if (This->t.lcl.ddsCaps.dwCaps & DDSCAPS_WRITEONLY) pDesc->Usage |= D3DUSAGE_WRITEONLY;
  pDesc->Pool = D3DPOOL_DEFAULT;
  if (This->t.lcl.ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) pDesc->Pool = D3DPOOL_SYSTEMMEM;
  if (This->t.more.ddsCapsEx.dwCaps2 & DDSCAPS2_TEXTUREMANAGE) pDesc->Pool = D3DPOOL_MANAGED;
  if (IsLinearFormat(This->Format))
    pDesc->Size = This->t.gbl.u4.dwLinearSize;
  else
    pDesc->Size = This->t.gbl.u4.lPitch * This->t.gbl.wHeight;
  pDesc->Width = This->t.gbl.wWidth;
  pDesc->Height = This->t.gbl.wHeight;
  return D3D_OK;
}

HRESULT WINAPI Direct3DSurface8_LockRect(LPDIRECT3DSURFACE8 iface,
					 D3DLOCKED_RECT* pLockedRect,
					 CONST RECT* pRect,
					 DWORD Flags)
{
  ICOM_THIS(IDirect3DSurface8Impl, iface);
  DDHAL_LOCKDATA data;
  LPBYTE ptr = (LPBYTE)This->t.gbl.fpVidMem;
  HRESULT hr = DDHAL_DRIVER_NOTHANDLED;

  TRACE("(%p)->(%p,%p,%lx)\n", iface,
	pLockedRect, pRect, Flags);
  if (pRect) {
    TRACE("rect: (%d,%d)-(%d,%d)\n", pRect->left, pRect->top, pRect->right, pRect->bottom);
  }

  data.lpDD = This->parent->local.lpGbl;
  data.lpDDSurface = &This->t.lcl;
  data.ddRVal = 0;
  data.lpSurfData = ptr;
  if (pRect) {
    data.rArea.top = pRect->top;
    data.rArea.bottom = pRect->bottom;
    data.rArea.left = pRect->left;
    data.rArea.right = pRect->right;
    data.bHasRect = TRUE;
  } else {
    data.bHasRect = FALSE;
  }
  data.dwFlags = Flags;
  data.Lock = This->parent->local.lpGbl->lpDDCBtmp->HALDDSurface.Lock;
  if (data.Lock) hr = data.Lock(&data);

  if (hr == DDHAL_DRIVER_HANDLED)
    ptr = data.lpSurfData;

  pLockedRect->Pitch = This->Pitch;
  if (pRect)
    ptr += CalcOffset(This, pRect->left, pRect->top);
  pLockedRect->pBits = ptr;

  /* update texture's dirty region */
  if ((!(Flags & (D3DLOCK_READONLY | D3DLOCK_NO_DIRTY_UPDATE))) &&
      (This->t.lcl.ddsCaps.dwCaps & DDSCAPS_TEXTURE) &&
      !(This->t.more.ddsCapsEx.dwCaps2 & DDSCAPS2_MIPMAPSUBLEVEL)) {
    IDirect3DBaseTexture8Impl* tex = NULL;
    IUnknown_QueryInterface(This->container, &IID_IDirect3DBaseTexture8, (void**)&tex);
    if (tex) {
      tex->AddDirtyRect(tex, iface, pRect);
      IDirect3DTexture8_Release((LPDIRECT3DTEXTURE8)tex);
    }
  }

  TRACE("returning: pitch %d, bits %p\n",
	pLockedRect->Pitch, pLockedRect->pBits);

  return D3D_OK;
}

HRESULT WINAPI Direct3DSurface8_UnlockRect(LPDIRECT3DSURFACE8 iface)
{
  ICOM_THIS(IDirect3DSurface8Impl, iface);
  DDHAL_UNLOCKDATA data;
  HRESULT hr;

  TRACE("(%p)->()\n", iface);

  data.lpDD = This->parent->local.lpGbl;
  data.lpDDSurface = &This->t.lcl;
  data.ddRVal = 0;
  data.Unlock = This->parent->local.lpGbl->lpDDCBtmp->HALDDSurface.Unlock;
  if (data.Unlock) hr = data.Unlock(&data);

  return D3D_OK;
}

static ICOM_VTABLE(IDirect3DSurface8) D3DS8_Vtbl =
{
  ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
  Direct3DSurface8_QueryInterface,
  Direct3DSurface8_AddRef,
  Direct3DSurface8_Release,
  Direct3DSurface8_GetDevice,
  (void*)0xdead8005,
  (void*)0xdead8006,
  (void*)0xdead8007,
  Direct3DSurface8_GetContainer,
  Direct3DSurface8_GetDesc,
  Direct3DSurface8_LockRect,
  Direct3DSurface8_UnlockRect
};

/* internal */
HRESULT WINAPI Direct3DDevice8_CreateSurface(LPDIRECT3DDEVICE8 iface,
					     LPUNKNOWN container,
					     UINT Width,
					     UINT Height,
					     D3DFORMAT Format,
					     DDSCAPS2* ddsCaps,
					     IDirect3DSurface8** ppSurface,
					     BOOL internal)
{
  ICOM_THIS(IDirect3DDevice8Impl, iface);
  IDirect3DSurface8Impl *pSurface;
  DDSURFACEDESC2* pDDSD;
  LPDDRAWI_DDRAWSURFACE_LCL local;
  DDHAL_CREATESURFACEDATA data;
  HRESULT hr;
  ULONG alloc;

  TRACE("(%p}->(%p,%d,%d,0x%x,%p,%p)\n", iface, container, Width, Height,
	Format, ddsCaps, ppSurface);

  pSurface = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirect3DSurface8Impl));
  pSurface->lpVtbl = &D3DS8_Vtbl;
  pSurface->ref = 1;
  pSurface->intref = internal ? 1 : 0;
  pSurface->parent = This;
  pSurface->container = container;
  pSurface->Format = Format;
  pSurface->Pitch = CalcPitch(Width, Format);

  pDDSD = &pSurface->surface_desc;

  pDDSD->dwSize = sizeof(DDSURFACEDESC2);
  pDDSD->dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT;
  pDDSD->dwHeight = Height;
  pDDSD->dwWidth = Width;
  pDDSD->ddsCaps = *ddsCaps;

  D3D8_GetPixelFormat(&pDDSD->u4.ddpfPixelFormat, Format);

  if (pDDSD->u4.ddpfPixelFormat.dwFlags & DDPF_FOURCC) {
    pDDSD->u1.dwLinearSize = CalcLinearSize(Width, Height, Format);
    alloc = pDDSD->u1.dwLinearSize;
  } else {
    pDDSD->u1.lPitch = pSurface->Pitch;
    alloc = pDDSD->u1.lPitch * pDDSD->dwHeight;
  }

  if (ddsCaps->dwCaps & DDSCAPS_VIDEOMEMORY)
  {
      TRACE("current vid mem is %li, going to decrease by %li\n",
              This->local.lpGbl->ddCaps.dwVidMemFree, alloc);
      if (alloc > This->local.lpGbl->ddCaps.dwVidMemFree)
      {
          WARN("too much memory used (out of fake video memory)!\n");
          HeapFree(GetProcessHeap(), 0, pSurface);
          return D3DERR_OUTOFVIDEOMEMORY;
      }
      This->local.lpGbl->ddCaps.dwVidMemFree -= alloc;
  }


  pSurface->t.lcl.lpSurfMore = &pSurface->t.more;
  pSurface->t.lcl.lpGbl = &pSurface->t.gbl;
  pSurface->t.lcl.dwFlags = 0; /* FIXME */
  pSurface->t.lcl.ddsCaps.dwCaps = pDDSD->ddsCaps.dwCaps;
  pSurface->t.more.lpDD_lcl = &This->local;
  pSurface->t.more.ddsCapsEx.dwCaps2 = pDDSD->ddsCaps.dwCaps2;
  pSurface->t.more.ddsCapsEx.dwCaps3 = pDDSD->ddsCaps.dwCaps3;
  pSurface->t.more.ddsCapsEx.dwCaps4 = pDDSD->ddsCaps.dwCaps4;
  pSurface->t.more.pCreatedDDSurfaceDesc2 = &pSurface->surface_desc;
  pSurface->t.gmore = &pSurface->t.gbl_more;
  pSurface->t.gbl.u3.lpDD = This->local.lpGbl;
  pSurface->t.gbl.wWidth = pDDSD->dwWidth;
  pSurface->t.gbl.wHeight = pDDSD->dwHeight;
  pSurface->t.gbl.u4.dwLinearSize = pDDSD->u1.dwLinearSize;
  pSurface->t.gbl.ddpfSurface = pDDSD->u4.ddpfPixelFormat;
  pSurface->sibling.lpAttached = &pSurface->t.lcl;

  TRACE(" pitch=%d\n", pSurface->Pitch);

  local = &pSurface->t.lcl;

  /* tell HAL to create surface */
  data.lpDD = This->local.lpGbl;
  data.lpDDSurfaceDesc = (LPDDSURFACEDESC)pDDSD;
  data.lplpSList = &local;
  data.dwSCnt = 1;
  data.ddRVal = 0;
  data.CreateSurface = This->local.lpGbl->lpDDCBtmp->HALDD.CreateSurface;
  hr = data.CreateSurface(&data);

  /* if HAL didn't allocate the surface, we will */
  if (pSurface->t.gbl.fpVidMem < 4) {
    LPVOID data;
    TRACE("allocating %ld bytes\n", alloc);
    data = HeapAlloc(surface_heap, HEAP_ZERO_MEMORY, alloc);
    pSurface->t.gbl.fpVidMem = (FLATPTR)data;
    pSurface->own_memory = TRUE;
  }

  TRACE("created %p\n", pSurface);
  *ppSurface = (LPDIRECT3DSURFACE8)pSurface;
  return D3D_OK;
}

HRESULT WINAPI Direct3DDevice8_AttachSurface(IDirect3DSurface8* pParent,
					     IDirect3DSurface8* pChild)
{
  IDirect3DSurface8Impl *Parent = (IDirect3DSurface8Impl *)pParent;
  IDirect3DSurface8Impl *Child  = (IDirect3DSurface8Impl *)pChild;

  if (Child->t.lcl.lpAttachListFrom) {
    ERR("attaching already attached surface\n");
    return D3DERR_INVALIDCALL;
  }

  Child->t.lcl.lpAttachListFrom = &Parent->sibling;

  Child->sibling.lpLink = Parent->t.lcl.lpAttachList;
  Parent->t.lcl.lpAttachList = &Child->sibling;

  return D3D_OK;
}

HRESULT WINAPI Direct3DDevice8_CreateRenderTarget(LPDIRECT3DDEVICE8 iface,
						  UINT Width,
						  UINT Height,
						  D3DFORMAT Format,
						  D3DMULTISAMPLE_TYPE MultiSample,
						  BOOL Lockable,
						  IDirect3DSurface8** ppSurface)
{
  DDSCAPS2 caps;

  TRACE("(%p}->(%d,%d,0x%x,%d,%d,%p)\n", iface, Width, Height, Format, MultiSample, Lockable, ppSurface);

  memset(&caps, 0, sizeof(caps));
  caps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_3DDEVICE;
  return Direct3DDevice8_CreateSurface(iface, (LPUNKNOWN)iface,
				       Width, Height, Format,
				       &caps, ppSurface,
				       FALSE);
}

HRESULT WINAPI Direct3DDevice8_CreateDepthStencilSurface(LPDIRECT3DDEVICE8 iface,
							 UINT Width,
							 UINT Height,
							 D3DFORMAT Format,
							 D3DMULTISAMPLE_TYPE MultiSample,
							 IDirect3DSurface8** ppSurface)
{
  DDSCAPS2 caps;

  TRACE("(%p}->(%d,%d,0x%x,%d,%p)\n", iface, Width, Height, Format, MultiSample, ppSurface);

  memset(&caps, 0, sizeof(caps));
  caps.dwCaps = DDSCAPS_ZBUFFER;
  return Direct3DDevice8_CreateSurface(iface, (LPUNKNOWN)iface,
				       Width, Height, Format,
				       &caps, ppSurface,
				       FALSE);
}

HRESULT WINAPI Direct3DDevice8_CreateImageSurface(LPDIRECT3DDEVICE8 iface,
						  UINT Width,
						  UINT Height,
						  D3DFORMAT Format,
						  IDirect3DSurface8** ppSurface)
{
  DDSCAPS2 caps;

  TRACE("(%p}->(%d,%d,0x%x,%p)\n", iface, Width, Height, Format, ppSurface);

  memset(&caps, 0, sizeof(caps));
  caps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
  return Direct3DDevice8_CreateSurface(iface, (LPUNKNOWN)iface,
				       Width, Height, Format,
				       &caps, ppSurface,
				       FALSE);
}
