/*	Direct3D driver for OpenGL
 *
 * Copyright 2001-2002 TransGaming Technologies Inc.
 */

#include "config.h"

#include "winbase.h"
#include "wine/winbase16.h"
#include "winreg.h"
#include "wine/debug.h"
#include "ddrawi.h"
#include "d3d9.h"
#include "d3dhal.h"

#include <assert.h>
#include <stdlib.h>
#include <string.h>

#include "d3dgl_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(ddraw);

#define IS_OPTION_TRUE(ch) \
    ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')

/* config options */
int use_cube_maps=1;                      /* default to yes */
int use_anisotropic_texture_filtering=0;  /* default to no */
int use_vertex_shaders=1;                 /* default to yes */
int use_clipspace_fix=1;                  /* default to yes */
int use_texmem_fix=1;                     /* default to yes */
int intercept_mode=0;                     /* default to no */
int max_texture_stages=8;		  /* default to 8 (max supported by d3d) */

static void check_exts(DD_GL_data *gld);
static void add_texture_fmts(DD_GL_data *gld);
static void add_zbuffer_fmts(DD_GL_data *gld);


/***********************************************************************
 * get_config_key
 *
 * Get a config key from either the app-specific or the default config
 */
inline static DWORD get_config_key( HKEY defkey, HKEY appkey, const char *name,
                                    char *buffer, DWORD size )
{
    if (appkey && !RegQueryValueExA( appkey, name, 0, NULL, buffer, &size )) return 0;
    return RegQueryValueExA( defkey, name, 0, NULL, buffer, &size );
}

/***********************************************************************
 * setup_options
 *
 * setup d3dgl specific options
 */
static void setup_options(void)
{
  char buffer[MAX_PATH+16];
  HKEY hkey, appkey = 0;

  if (RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\d3dgl", 0, NULL,
                       REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL )) {
    ERR("Cannot create config registry key\n");
    return;
  }
  
  /* open the app-specific key */
  if (GetModuleFileName16( GetCurrentTask(), buffer, MAX_PATH ) ||
      GetModuleFileNameA( 0, buffer, MAX_PATH )) {
    HKEY tmpkey;
    char *p, *appname = buffer;
    if ((p = strrchr( appname, '/' ))) appname = p + 1;
    if ((p = strrchr( appname, '\\' ))) appname = p + 1;
    strcat( appname, "\\d3dgl" );
    if (!RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Wine\\Wine\\Config\\AppDefaults", &tmpkey ))
    {
	if (RegOpenKeyA( tmpkey, appname, &appkey )) appkey = 0;
	RegCloseKey( tmpkey );
    }
  }

  if (!get_config_key( hkey, appkey, "CubeMaps", buffer, sizeof(buffer) ))
    use_cube_maps = IS_OPTION_TRUE( buffer[0] );
  TRACE("CubeMaps=(%d)\n", use_cube_maps);

  if (!get_config_key( hkey, appkey, "AnisotropicTextureFiltering", buffer, sizeof(buffer) ))
    use_anisotropic_texture_filtering = IS_OPTION_TRUE( buffer[0] );
  TRACE("AnisotropicTextureFiltering=(%d)\n", use_anisotropic_texture_filtering);

  if (!get_config_key( hkey, appkey, "VertexShaders", buffer, sizeof(buffer) ))
    use_vertex_shaders = IS_OPTION_TRUE( buffer[0] );
  TRACE("VertexShaders=(%d)\n", use_vertex_shaders);

  if (!get_config_key( hkey, appkey, "ClipSpaceFix", buffer, sizeof(buffer) ))
    use_clipspace_fix = IS_OPTION_TRUE( buffer[0] );
  TRACE("ClipSpaceFix=(%d)\n", use_clipspace_fix);

  if (!get_config_key( hkey, appkey, "TexMemFix", buffer, sizeof(buffer) ))
    use_texmem_fix = IS_OPTION_TRUE( buffer[0] );
  TRACE("TexMemFix=(%d)\n", use_texmem_fix);

  if (!get_config_key( hkey, appkey, "InterceptMode", buffer, sizeof(buffer) ))
    intercept_mode = IS_OPTION_TRUE( buffer[0] );
  TRACE("InterceptMode=(%d)\n", intercept_mode);

  if (!get_config_key( hkey, appkey, "MaxTextureStages", buffer, sizeof(buffer) )) {
    max_texture_stages = atoi( buffer );
    if( max_texture_stages < 1 ) max_texture_stages = 1;
    else if( max_texture_stages > 8 ) max_texture_stages = 8;
  }
  TRACE("MaxTextureStages=(%d)\n", max_texture_stages);

  if (appkey) RegCloseKey( appkey );
  RegCloseKey( hkey );

}

void WINAPI D3DGL_Init(LPDDRAWI_DIRECTDRAW_GBL dd)
{
    DD_GL_data *gld;
    /* the "cdecl" tool is useful for something... */
    void (*(*gl_GetProcAddress)(const GLubyte *))(void);
    LPD3DHAL_GLOBALDRIVERDATA lpData;
    LPD3DHAL_D3DEXTENDEDCAPS lpExt;
    LPD3DHAL_CALLBACKS lpCB;
    ULONG_PTR dwhContext;
    GLint tmpi;

    lpData = (LPD3DHAL_GLOBALDRIVERDATA)dd->lpD3DGlobalDriverData;
    lpExt = (LPD3DHAL_D3DEXTENDEDCAPS)dd->lpD3DExtendedCaps;
    lpCB = (LPD3DHAL_CALLBACKS)dd->lpD3DHALCallbacks;

    if (dd->dwReserved3) /* already initialized - but since we're being called
                            again, the previous data has been wiped, so we have
                            to fill it in again */
    {
        gld = (DD_GL_data *)dd->dwReserved3;
        goto fill_old_caps;
    }
    else
    {
        gld = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DD_GL_data));
        dd->dwReserved3 = (ULONG_PTR)gld;
    }

    /* In order to retrieve actual device capabilities for an OpenGL device,
     * a context must be created for it first... eek */
    {
	D3DHAL_CONTEXTCREATEDATA data;
	data.u1.lpDDLcl = NULL;
	data.u2.lpDDSLcl = NULL;
	data.u3.lpDDSZLcl = NULL;
	data.u4.dwPID = 0;
	lpCB->ContextCreate(&data);
	if (data.ddrval != D3D_OK) {
	    ERR("cannot acquire OpenGL device capabilities\n");
	    return;
	}
	dwhContext = data.dwhContext;
    }
    TRACE("created temp context %08x\n", dwhContext);
    check_exts(gld);

    /* x11drv puts glXGetProcAddress in here */
    gl_GetProcAddress = lpCB->lpReserved10;
    /* retrieve available extension procs */
    if (gl_GetProcAddress) {
	void (*extproc)(void);
#define DD_GL_PROC(x,y) extproc = gl_GetProcAddress(#y); gld->y = (x)extproc; TRACE(" " #y " = %p\n", extproc);
	DD_GL_PROC_LIST
#undef DD_GL_PROC
    } else {
#define DD_GL_PROC(x,y) gld->y = (x)NULL;
	DD_GL_PROC_LIST
#undef DD_GL_PROC
    }

    /* load options from config file */
    setup_options();

    /* disable cube maps, since their usage usually depends on render-to-texture,
     * which may not work reliable with every possible driver. */
    if ( !use_cube_maps ) {
      TRACE("disabling cubic environment maps\n");
      gld->gl_ext[ARB_texture_cube_map] = 0; 
    }

    /* disable anistropic texture filters -- they cause a major slowdowns when used! 
     * (unless user explicitly turned them on in config file) */
    if( !use_anisotropic_texture_filtering ) {
      TRACE("disabling anisotropic filtering\n");
      gld->gl_ext[EXT_texture_filter_anisotropic] = 0; 
    }
      
    /* disable vertex programs/shaders if explicitly turned off in config file */
    if( !use_vertex_shaders ) {
      TRACE("disabling vertex shaders\n");
      gld->gl_ext[NV_vertex_program] = 0;
      gld->gl_ext[NV_vertex_program1_1] = 0;
      gld->gl_ext[NV_vertex_program2] = 0;
      gld->gl_ext[ARB_vertex_program] = 0;
    } else {
      D3D_GL_VertexProgram_Init(gld);
    }

    /* even though this is the DirectX 8 caps structure, we'll use DX7-compatible data,
     * so that the DX7 caps can be filled in from this caps structure. */
    gld->caps9.DeviceType = D3DDEVTYPE_HAL;
    gld->caps9.AdapterOrdinal = 0;
    gld->caps9.Caps = dd->ddCaps.dwCaps;
    gld->caps9.Caps2 = dd->ddCaps.dwCaps2 | DDCAPS2_CANMANAGETEXTURE | DDCAPS2_CANRENDERWINDOWED;
    gld->caps9.Caps3 = 0;
    gld->caps9.PresentationIntervals = D3DPRESENT_INTERVAL_IMMEDIATE | D3DPRESENT_INTERVAL_ONE |
                                       D3DPRESENT_INTERVAL_TWO | D3DPRESENT_INTERVAL_THREE |
                                       D3DPRESENT_INTERVAL_FOUR;

    gld->caps9.DevCaps = D3DDEVCAPS_FLOATTLVERTEX | D3DDEVCAPS_EXECUTESYSTEMMEMORY |
			 D3DDEVCAPS_TLVERTEXSYSTEMMEMORY | D3DDEVCAPS_TLVERTEXVIDEOMEMORY |
			 D3DDEVCAPS_TEXTURESYSTEMMEMORY | D3DDEVCAPS_TEXTUREVIDEOMEMORY |
			 D3DDEVCAPS_DRAWPRIMTLVERTEX | D3DDEVCAPS_CANRENDERAFTERFLIP |
			 D3DDEVCAPS_TEXTURENONLOCALVIDMEM | D3DDEVCAPS_HWTRANSFORMANDLIGHT |
			 D3DDEVCAPS_CANBLTSYSTONONLOCAL | D3DDEVCAPS_HWRASTERIZATION;

    /* FIXME: add caps for patches */

    gld->caps9.PrimitiveMiscCaps = D3DPMISCCAPS_MASKZ | D3DPMISCCAPS_LINEPATTERNREP | D3DPMISCCAPS_CONFORMANT |
				   D3DPMISCCAPS_CULLNONE | D3DPMISCCAPS_CULLCW | D3DPMISCCAPS_CULLCCW |
				   D3DPMISCCAPS_COLORWRITEENABLE;
    if (gld->gl_ext[ARB_imaging])
	gld->caps9.PrimitiveMiscCaps |= D3DPMISCCAPS_BLENDOP;

    gld->caps9.RasterCaps = D3DPRASTERCAPS_DITHER | D3DPRASTERCAPS_ZTEST | D3DPRASTERCAPS_FOGVERTEX |
			    D3DPRASTERCAPS_FOGTABLE | D3DPRASTERCAPS_STIPPLE | D3DPRASTERCAPS_MIPMAPLODBIAS |
			    D3DPRASTERCAPS_ZBIAS | D3DPRASTERCAPS_ZFOG | D3DPRASTERCAPS_FOGRANGE | D3DPRASTERCAPS_WFOG |
			    D3DPRASTERCAPS_ANTIALIASSORTDEPENDENT;
    if (gld->gl_ext[EXT_texture_filter_anisotropic])
	gld->caps9.RasterCaps |= D3DPRASTERCAPS_ANISOTROPY;

    gld->caps9.ZCmpCaps = 0xFF; /* all Z tests can be done */

    gld->caps9.SrcBlendCaps = D3DPBLENDCAPS_ZERO | D3DPBLENDCAPS_ONE | D3DPBLENDCAPS_SRCALPHA |
			      D3DPBLENDCAPS_INVSRCALPHA | D3DPBLENDCAPS_DESTALPHA | D3DPBLENDCAPS_INVDESTALPHA |
			      D3DPBLENDCAPS_DESTCOLOR | D3DPBLENDCAPS_INVDESTCOLOR | D3DPBLENDCAPS_SRCALPHASAT;
    gld->caps9.DestBlendCaps = D3DPBLENDCAPS_ZERO | D3DPBLENDCAPS_ONE | D3DPBLENDCAPS_SRCCOLOR |
			       D3DPBLENDCAPS_INVSRCCOLOR | D3DPBLENDCAPS_SRCALPHA | D3DPBLENDCAPS_INVSRCALPHA |
			       D3DPBLENDCAPS_DESTALPHA | D3DPBLENDCAPS_INVDESTALPHA;
    if (gld->gl_ext[NV_blend_square]) {
	gld->caps9.SrcBlendCaps |= D3DPBLENDCAPS_SRCCOLOR | D3DPBLENDCAPS_INVSRCCOLOR;
	gld->caps9.DestBlendCaps |= D3DPBLENDCAPS_DESTCOLOR | D3DPBLENDCAPS_INVDESTCOLOR;
    }

    gld->caps9.AlphaCmpCaps = 0xFF; /* all alpha tests can be done */

    gld->caps9.ShadeCaps = D3DPSHADECAPS_COLORFLATRGB | D3DPSHADECAPS_COLORGOURAUDRGB |
			   D3DPSHADECAPS_SPECULARFLATRGB | D3DPSHADECAPS_SPECULARGOURAUDRGB |
			   D3DPSHADECAPS_ALPHAFLATBLEND | D3DPSHADECAPS_ALPHAGOURAUDBLEND |
			   D3DPSHADECAPS_FOGFLAT | D3DPSHADECAPS_FOGGOURAUD;

    gld->caps9.TextureCaps = D3DPTEXTURECAPS_PERSPECTIVE | D3DPTEXTURECAPS_POW2 | D3DPTEXTURECAPS_ALPHA |
			     D3DPTEXTURECAPS_TRANSPARENCY | D3DPTEXTURECAPS_BORDER | D3DPTEXTURECAPS_MIPMAP |
			     D3DPTEXTURECAPS_ALPHAPALETTE;
    if (gld->gl_ext[ARB_texture_cube_map])
	gld->caps9.TextureCaps |= D3DPTEXTURECAPS_CUBEMAP | D3DPTEXTURECAPS_MIPCUBEMAP | D3DPTEXTURECAPS_CUBEMAP_POW2;

    gld->caps9.TextureFilterCaps = D3DPTFILTERCAPS_NEAREST | D3DPTFILTERCAPS_LINEAR |
				   D3DPTFILTERCAPS_MIPNEAREST | D3DPTFILTERCAPS_MIPLINEAR |
				   D3DPTFILTERCAPS_LINEARMIPNEAREST | D3DPTFILTERCAPS_LINEARMIPLINEAR |
				   D3DPTFILTERCAPS_MINFPOINT | D3DPTFILTERCAPS_MINFLINEAR |
				   D3DPTFILTERCAPS_MIPFPOINT | D3DPTFILTERCAPS_MIPFLINEAR |
				   D3DPTFILTERCAPS_MAGFPOINT | D3DPTFILTERCAPS_MAGFLINEAR;
    if (gld->gl_ext[EXT_texture_filter_anisotropic])
	gld->caps9.TextureFilterCaps |= D3DPTFILTERCAPS_MINFANISOTROPIC | D3DPTFILTERCAPS_MAGFANISOTROPIC;

    gld->caps9.TextureAddressCaps = D3DPTADDRESSCAPS_WRAP | D3DPTADDRESSCAPS_CLAMP | D3DPTADDRESSCAPS_INDEPENDENTUV;
    if (gld->gl_ext[ARB_texture_border_clamp])
	gld->caps9.TextureAddressCaps |= D3DPTADDRESSCAPS_BORDER;
    if (gld->gl_ext[ARB_texture_mirrored_repeat] || gld->gl_ext[IBM_texture_mirrored_repeat])
	gld->caps9.TextureAddressCaps |= D3DPTADDRESSCAPS_MIRROR;
    if (gld->gl_ext[ATI_texture_mirror_once])
	gld->caps9.TextureAddressCaps |= D3DPTADDRESSCAPS_MIRRORONCE;

    if (gld->gl_ext[ARB_texture_cube_map])
	gld->caps9.CubeTextureFilterCaps = gld->caps9.TextureFilterCaps;
    /* FIXME: VolumeTextureFilterCaps */

    gld->caps9.LineCaps = D3DLINECAPS_ALPHACMP | D3DLINECAPS_BLEND | D3DLINECAPS_FOG |
			  D3DLINECAPS_TEXTURE | D3DLINECAPS_ZTEST;

    tmpi = 1;
    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &tmpi);
    gld->caps9.MaxTextureWidth  = tmpi;
    gld->caps9.MaxTextureHeight = tmpi;

    /* FIXME: MaxVolumeExtent */

    /* FIXME: fix these numbers */
    gld->caps9.MaxTextureRepeat = 256;
    gld->caps9.MaxTextureAspectRatio = 0;

    tmpi = 1;
    if (gld->gl_ext[EXT_texture_filter_anisotropic])
	glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &tmpi);
    gld->caps9.MaxAnisotropy = tmpi;

    gld->caps9.MaxVertexW = 1.0; /* ? */

    /* FIXME: fix these numbers */
    gld->caps9.GuardBandLeft   = -4096.0;
    gld->caps9.GuardBandTop    = -4096.0;
    gld->caps9.GuardBandRight  = 4095.0;
    gld->caps9.GuardBandBottom = 4095.0;
    gld->caps9.ExtentsAdjust = 0;

    gld->caps9.StencilCaps = D3DSTENCILCAPS_KEEP | D3DSTENCILCAPS_ZERO | D3DSTENCILCAPS_REPLACE |
			     D3DSTENCILCAPS_INCRSAT | D3DSTENCILCAPS_DECRSAT | D3DSTENCILCAPS_INVERT;
    if (gld->gl_ext[EXT_stencil_wrap])
	gld->caps9.StencilCaps |= D3DSTENCILCAPS_INCR | D3DSTENCILCAPS_DECR;

    gld->caps9.FVFCaps = 8; /* FIXME: PSIZE */

    gld->caps9.TextureOpCaps = D3DTEXOPCAPS_DISABLE | D3DTEXOPCAPS_SELECTARG1 | D3DTEXOPCAPS_SELECTARG2 |
			       D3DTEXOPCAPS_MODULATE | D3DTEXOPCAPS_BLENDTEXTUREALPHA;
    if (gld->gl_ext[ARB_texture_env_add] || gld->gl_ext[EXT_texture_env_add])
	gld->caps9.TextureOpCaps |= D3DTEXOPCAPS_ADD;
    if (gld->gl_ext[ARB_texture_env_combine])
	gld->caps9.TextureOpCaps |= D3DTEXOPCAPS_MODULATE | D3DTEXOPCAPS_MODULATE2X | D3DTEXOPCAPS_MODULATE4X |
				    D3DTEXOPCAPS_ADD | D3DTEXOPCAPS_ADDSIGNED | D3DTEXOPCAPS_ADDSIGNED2X |
				    D3DTEXOPCAPS_SUBTRACT | D3DTEXOPCAPS_BLENDDIFFUSEALPHA |
				    D3DTEXOPCAPS_BLENDTEXTUREALPHA | D3DTEXOPCAPS_BLENDFACTORALPHA |
				    D3DTEXOPCAPS_BLENDCURRENTALPHA | D3DTEXOPCAPS_LERP;
    else if (gld->gl_ext[EXT_texture_env_combine])
	gld->caps9.TextureOpCaps |= D3DTEXOPCAPS_MODULATE | D3DTEXOPCAPS_MODULATE2X | D3DTEXOPCAPS_MODULATE4X |
				    D3DTEXOPCAPS_ADD | D3DTEXOPCAPS_ADDSIGNED | D3DTEXOPCAPS_ADDSIGNED2X |
				    D3DTEXOPCAPS_BLENDDIFFUSEALPHA | D3DTEXOPCAPS_BLENDTEXTUREALPHA |
				    D3DTEXOPCAPS_BLENDFACTORALPHA | D3DTEXOPCAPS_BLENDCURRENTALPHA |
				    D3DTEXOPCAPS_LERP;
    if (gld->gl_ext[ATI_texture_env_combine3])
	gld->caps9.TextureOpCaps |= D3DTEXOPCAPS_BLENDTEXTUREALPHAPM | D3DTEXOPCAPS_MODULATEALPHA_ADDCOLOR |
				    D3DTEXOPCAPS_MODULATECOLOR_ADDALPHA | D3DTEXOPCAPS_MODULATEINVALPHA_ADDCOLOR |
				    D3DTEXOPCAPS_MODULATEINVCOLOR_ADDALPHA | D3DTEXOPCAPS_MULTIPLYADD;
    if (gld->gl_ext[NV_texture_env_combine4])
	gld->caps9.TextureOpCaps |= D3DTEXOPCAPS_MODULATE | D3DTEXOPCAPS_MODULATE2X | D3DTEXOPCAPS_MODULATE4X |
				    D3DTEXOPCAPS_ADD | D3DTEXOPCAPS_ADDSIGNED | D3DTEXOPCAPS_ADDSIGNED2X |
				    D3DTEXOPCAPS_BLENDDIFFUSEALPHA | D3DTEXOPCAPS_BLENDTEXTUREALPHA |
				    D3DTEXOPCAPS_BLENDFACTORALPHA | D3DTEXOPCAPS_BLENDCURRENTALPHA |
				    D3DTEXOPCAPS_BLENDTEXTUREALPHAPM | D3DTEXOPCAPS_MODULATEALPHA_ADDCOLOR |
				    D3DTEXOPCAPS_MODULATECOLOR_ADDALPHA | D3DTEXOPCAPS_MODULATEINVALPHA_ADDCOLOR |
				    D3DTEXOPCAPS_MODULATEINVCOLOR_ADDALPHA | D3DTEXOPCAPS_MULTIPLYADD |
				    D3DTEXOPCAPS_LERP;
#if 0 /* not implemented yet */
    if (gld->gl_ext[NV_texture_shader])
	gld->caps9.TextureOpCaps |= D3DTEXOPCAPS_BUMPENVMAP | D3DTEXOPCAPS_BUMPENVMAPLUMINANCE;
#endif
    if (gld->gl_ext[ARB_texture_env_dot3] || gld->gl_ext[EXT_texture_env_dot3])
	gld->caps9.TextureOpCaps |= D3DTEXOPCAPS_DOTPRODUCT3;

    tmpi = 1;
    if (gld->gl_ext[ARB_multitexture])
	glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &tmpi);
    if (tmpi > 8) tmpi = 8; /* D3D can't handle more */
    if (max_texture_stages < tmpi) {
      TRACE("max hardware texture blend stages: %d, limiting usage to: %d\n", tmpi, max_texture_stages);
      tmpi = max_texture_stages;
    }
    gld->caps9.MaxTextureBlendStages = tmpi;
    gld->caps9.MaxSimultaneousTextures = tmpi;
    TRACE("texture blend stages: %ld/%ld\n",
          gld->caps9.MaxTextureBlendStages,
          gld->caps9.MaxSimultaneousTextures);

    gld->caps9.VertexProcessingCaps = D3DVTXPCAPS_TEXGEN | D3DVTXPCAPS_VERTEXFOG |
				      D3DVTXPCAPS_DIRECTIONALLIGHTS | D3DVTXPCAPS_POSITIONALLIGHTS |
				      D3DVTXPCAPS_LOCALVIEWER | D3DVTXPCAPS_MATERIALSOURCE7;

    tmpi = 0;
    glGetIntegerv(GL_MAX_LIGHTS, &tmpi);
    if (tmpi > 32) tmpi = 32; /* light.c can't handle more */
    gld->caps9.MaxActiveLights = tmpi;
    TRACE("max lights: %ld\n", gld->caps9.MaxActiveLights);

    tmpi = 0;
    glGetIntegerv(GL_MAX_CLIP_PLANES, &tmpi);
    if (tmpi > 32) tmpi = 32; /* D3DMAXUSERCLIPPLANES */
    gld->caps9.MaxUserClipPlanes = tmpi;
    TRACE("max user clip planes: %ld\n", gld->caps9.MaxUserClipPlanes);

    tmpi = 1;
    if (gld->gl_ext[ARB_vertex_blend])
	glGetIntegerv(GL_MAX_VERTEX_UNITS_ARB, &tmpi);
    else if (gld->gl_ext[EXT_vertex_weighting])
	tmpi = 2;
    if (tmpi > 4) tmpi = 4; /* D3D can't handle more */
    gld->caps9.MaxVertexBlendMatrices = tmpi;
    gld->caps9.MaxVertexBlendMatrixIndex = 0; /* FIXME */
    TRACE("max blend matrices: %ld\n", gld->caps9.MaxVertexBlendMatrices);

    gld->caps9.MaxPointSize = 1.0f;
    gld->caps9.MaxPrimitiveCount = 0xFFFF;
    gld->caps9.MaxVertexIndex = 0xFFFF;
    gld->caps9.MaxStreams = 16; 
    gld->caps9.MaxStreamStride = 0xFFFF;
    if( gld->gl_ext[ARB_vertex_program] || 
        gld->gl_ext[NV_vertex_program]  || gld->gl_ext[NV_vertex_program1_1] ) {
      gld->caps9.VertexShaderVersion = D3DVS_VERSION(1,1);
      gld->caps9.MaxVertexShaderConst = 96;
      TRACE("vertex shader 1.1\n");
    } else {
      gld->caps9.VertexShaderVersion = 0;
      gld->caps9.MaxVertexShaderConst = 0;
      TRACE("no vertex shader\n");
    }
    gld->caps9.PixelShaderVersion = 0;
    gld->caps9.MaxPixelShaderValue = 0;

    gld->caps9.DeclTypes |= D3DDTCAPS_UBYTE4;

    add_texture_fmts(gld);
    add_zbuffer_fmts(gld);

    {
	D3DHAL_CONTEXTDESTROYDATA data;
	data.dwhContext = dwhContext;
	lpCB->ContextDestroy(&data);
    }

fill_old_caps:
    /* Fill in the DX7 caps */

    dd->ddCaps.dwCaps = gld->caps9.Caps;
    dd->ddCaps.dwCaps2 = gld->caps9.Caps2;
    dd->ddCaps.dwZBufferBitDepths |= DDBD_16;
    if (gld->gl_ext[ARB_texture_cube_map])
	dd->ddsCapsMore.dwCaps2 |= DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_ALLFACES;

    lpData->hwCaps.dwFlags |= D3DDD_COLORMODEL | D3DDD_DEVCAPS | D3DDD_TRANSFORMCAPS | D3DDD_LIGHTINGCAPS |
			      D3DDD_BCLIPPING | D3DDD_LINECAPS | D3DDD_TRICAPS | D3DDD_DEVICERENDERBITDEPTH |
			      D3DDD_DEVICEZBUFFERBITDEPTH | D3DDD_MAXBUFFERSIZE | D3DDD_MAXVERTEXCOUNT;
    lpData->hwCaps.dcmColorModel = D3DCOLOR_RGB;
    lpData->hwCaps.dwDevCaps = gld->caps9.DevCaps;
    lpData->hwCaps.dtcTransformCaps.dwSize = sizeof(D3DTRANSFORMCAPS);
    lpData->hwCaps.dtcTransformCaps.dwCaps = D3DTRANSFORMCAPS_CLIP;
    lpData->hwCaps.bClipping = TRUE;
    lpData->hwCaps.dlcLightingCaps.dwSize = sizeof(D3DLIGHTINGCAPS);
    /* FIXME: we have GLSPOT, but we can't really do plain SPOT... should it be removed? */
    lpData->hwCaps.dlcLightingCaps.dwCaps = D3DLIGHTCAPS_POINT | D3DLIGHTCAPS_SPOT | D3DLIGHTCAPS_DIRECTIONAL |
					    D3DLIGHTCAPS_PARALLELPOINT | D3DLIGHTCAPS_GLSPOT;
    lpData->hwCaps.dlcLightingCaps.dwLightingModel = D3DLIGHTINGMODEL_RGB;
    lpData->hwCaps.dlcLightingCaps.dwNumLights = 1024; /* how much memory can we waste storing inactive lights? */

    lpData->hwCaps.dpcTriCaps.dwSize = sizeof(D3DPRIMCAPS);
    lpData->hwCaps.dpcTriCaps.dwMiscCaps = gld->caps9.PrimitiveMiscCaps;
    lpData->hwCaps.dpcTriCaps.dwRasterCaps = gld->caps9.RasterCaps;
    lpData->hwCaps.dpcTriCaps.dwZCmpCaps = gld->caps9.ZCmpCaps;
    lpData->hwCaps.dpcTriCaps.dwSrcBlendCaps = gld->caps9.SrcBlendCaps;
    lpData->hwCaps.dpcTriCaps.dwDestBlendCaps = gld->caps9.DestBlendCaps;
    lpData->hwCaps.dpcTriCaps.dwAlphaCmpCaps = gld->caps9.AlphaCmpCaps;
    lpData->hwCaps.dpcTriCaps.dwShadeCaps = gld->caps9.ShadeCaps;
    lpData->hwCaps.dpcTriCaps.dwTextureCaps = gld->caps9.TextureCaps;
    lpData->hwCaps.dpcTriCaps.dwTextureFilterCaps = gld->caps9.TextureFilterCaps;
    lpData->hwCaps.dpcTriCaps.dwTextureAddressCaps = gld->caps9.TextureAddressCaps;

    lpData->hwCaps.dpcTriCaps.dwTextureBlendCaps = D3DPTBLENDCAPS_DECAL | D3DPTBLENDCAPS_MODULATE |
						   D3DPTBLENDCAPS_DECALALPHA | D3DPTBLENDCAPS_MODULATEALPHA |
						   D3DPTBLENDCAPS_COPY;
    if (gld->caps9.TextureOpCaps & D3DTEXOPCAPS_ADD)
	lpData->hwCaps.dpcTriCaps.dwTextureBlendCaps |= D3DPTBLENDCAPS_ADD;

    lpData->hwCaps.dpcTriCaps.dwStippleWidth = 32;
    lpData->hwCaps.dpcTriCaps.dwStippleHeight = 32;

    memcpy(&lpData->hwCaps.dpcLineCaps, &lpData->hwCaps.dpcTriCaps, sizeof(D3DPRIMCAPS));

    /* FIXME: get real bit depth data */
    lpData->hwCaps.dwDeviceRenderBitDepth = DDBD_16;
    lpData->hwCaps.dwDeviceZBufferBitDepth = DDBD_16;
    /* FIXME: fix these numbers */
    lpData->hwCaps.dwMaxBufferSize = 0;
    lpData->hwCaps.dwMaxVertexCount = 65536;

    lpExt->dwSize = sizeof(D3DHAL_D3DEXTENDEDCAPS);

    lpExt->dwMinTextureWidth  = 1;
    lpExt->dwMinTextureHeight = 1;
    lpExt->dwMaxTextureWidth  = gld->caps9.MaxTextureWidth;
    lpExt->dwMaxTextureHeight = gld->caps9.MaxTextureHeight;

    /* FIXME: fix these numbers */
    lpExt->dwMinStippleWidth  = 1;
    lpExt->dwMinStippleHeight = 1;
    lpExt->dwMaxStippleWidth  = 32;
    lpExt->dwMaxStippleHeight = 32;

    lpExt->dwMaxTextureRepeat = gld->caps9.MaxTextureRepeat;
    lpExt->dwMaxTextureAspectRatio = gld->caps9.MaxTextureAspectRatio;
    lpExt->dwMaxAnisotropy = gld->caps9.MaxAnisotropy;

    lpExt->dvGuardBandLeft   = gld->caps9.GuardBandLeft;
    lpExt->dvGuardBandTop    = gld->caps9.GuardBandTop;
    lpExt->dvGuardBandRight  = gld->caps9.GuardBandRight;
    lpExt->dvGuardBandBottom = gld->caps9.GuardBandBottom;
    lpExt->dvExtentsAdjust   = gld->caps9.ExtentsAdjust;

    lpExt->dwStencilCaps = gld->caps9.StencilCaps;
    lpExt->dwFVFCaps = gld->caps9.FVFCaps;
    lpExt->dwTextureOpCaps = gld->caps9.TextureOpCaps;

    lpExt->wMaxTextureBlendStages   = gld->caps9.MaxTextureBlendStages;
    lpExt->wMaxSimultaneousTextures = gld->caps9.MaxSimultaneousTextures;
    lpExt->dwMaxActiveLights = gld->caps9.MaxActiveLights;
    lpExt->dvMaxVertexW = gld->caps9.MaxVertexW;
    lpExt->wMaxUserClipPlanes = gld->caps9.MaxUserClipPlanes;
    lpExt->wMaxVertexBlendMatrices = gld->caps9.MaxVertexBlendMatrices;

    lpExt->dwVertexProcessingCaps = gld->caps9.VertexProcessingCaps;

    lpExt->dwReserved1 = 0;
    lpExt->dwReserved2 = 0;
    lpExt->dwReserved3 = 0;
    lpExt->dwReserved4 = 0;

    lpData->lpTextureFormats = gld->lpTextureFormats;
    lpData->dwNumTextureFormats = gld->dwNumTextureFormats;

    dd->lpZPixelFormats = gld->lpZPixelFormats;
    dd->dwNumZPixelFormats = gld->dwNumZPixelFormats;

    /* Hook surface lock/unlock entry points */

    gld->old_lock   = dd->lpDDCBtmp->HALDDSurface.Lock;
    gld->old_unlock = dd->lpDDCBtmp->HALDDSurface.Unlock;
    gld->old_blt    = dd->lpDDCBtmp->HALDDSurface.Blt;

    dd->lpDDCBtmp->HALDDSurface.Lock   = D3DGL_Lock;
    dd->lpDDCBtmp->HALDDSurface.Unlock = D3DGL_Unlock;
    dd->lpDDCBtmp->HALDDSurface.Blt    = D3DGL_Blt;

    TRACE("device capabilities completed\n");
}

#define DD_GL_EXT(x) "GL_"#x
static const char*const GL_ext_names[DD_GL_EXTENSION_COUNT] = {
    DD_GL_EXTENSION_LIST
};
#undef DD_GL_EXT

static void check_exts(DD_GL_data *gld)
{
    const char *exts = glGetString(GL_EXTENSIONS);
    int i;

    TRACE("checking available GL extensions\n");
    if (!exts) WARN("failed to acquire extension list\n");
    for (i=0; i<DD_GL_EXTENSION_COUNT; i++) {
	gld->gl_ext[i] = exts ? (strstr(exts, GL_ext_names[i]) != NULL) : FALSE;
	TRACE(" %s: %s\n", GL_ext_names[i], gld->gl_ext[i] ? "YES" : "NO");
    }
}

static void add_texture_fmts(DD_GL_data *gld)
{
    DDSURFACEDESC temp, *fmts;
    LPDDPIXELFORMAT pfmt;
    DWORD count = 8 + 2 + 4;

    memset(&temp, 0, sizeof(DDSURFACEDESC));
    fmts = HeapAlloc(GetProcessHeap(),0,sizeof(DDSURFACEDESC)*count);
    temp.dwSize = sizeof(DDSURFACEDESC);
    temp.dwFlags = DDSD_PIXELFORMAT | DDSD_CAPS;
    temp.ddsCaps.dwCaps = DDSCAPS_TEXTURE;
    pfmt = &temp.ddpfPixelFormat;
    pfmt->dwSize = sizeof(DDPIXELFORMAT);
    pfmt->dwFourCC = 0;
    count = 0;

    /* X8R8G8B8 */
    pfmt->dwFlags = DDPF_RGB;
    pfmt->u1.dwRGBBitCount     = 32;
    pfmt->u2.dwRBitMask        = 0x00FF0000;
    pfmt->u3.dwGBitMask        = 0x0000FF00;
    pfmt->u4.dwBBitMask        = 0x000000FF;
    pfmt->u5.dwRGBAlphaBitMask = 0x00000000;
    memcpy(&fmts[count++], &temp, sizeof(DDSURFACEDESC));

    /* A8R8G8B8 */
    pfmt->dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS;
    pfmt->u1.dwRGBBitCount     = 32;
    pfmt->u2.dwRBitMask        = 0x00FF0000;
    pfmt->u3.dwGBitMask        = 0x0000FF00;
    pfmt->u4.dwBBitMask        = 0x000000FF;
    pfmt->u5.dwRGBAlphaBitMask = 0xFF000000;
    memcpy(&fmts[count++], &temp, sizeof(DDSURFACEDESC));

    /* R5G6B5 */
    pfmt->dwFlags = DDPF_RGB;
    pfmt->u1.dwRGBBitCount     = 16;
    pfmt->u2.dwRBitMask        = 0xF800;
    pfmt->u3.dwGBitMask        = 0x07E0;
    pfmt->u4.dwBBitMask        = 0x001F;
    pfmt->u5.dwRGBAlphaBitMask = 0x0000;
    memcpy(&fmts[count++], &temp, sizeof(DDSURFACEDESC));

    /* X1R5G5B5 */
    pfmt->dwFlags = DDPF_RGB;
    pfmt->u1.dwRGBBitCount     = 16;
    pfmt->u2.dwRBitMask        = 0x7C00;
    pfmt->u3.dwGBitMask        = 0x03E0;
    pfmt->u4.dwBBitMask        = 0x001F;
    pfmt->u5.dwRGBAlphaBitMask = 0x0000;
    memcpy(&fmts[count++], &temp, sizeof(DDSURFACEDESC));

    /* A1R5G5B5 */
    pfmt->dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS;
    pfmt->u1.dwRGBBitCount     = 16;
    pfmt->u2.dwRBitMask        = 0x7C00;
    pfmt->u3.dwGBitMask        = 0x03E0;
    pfmt->u4.dwBBitMask        = 0x001F;
    pfmt->u5.dwRGBAlphaBitMask = 0x8000;
    memcpy(&fmts[count++], &temp, sizeof(DDSURFACEDESC));

    /* A4R4G4B4 */
    pfmt->dwFlags = DDPF_RGB | DDPF_ALPHAPIXELS;
    pfmt->u1.dwRGBBitCount     = 16;
    pfmt->u2.dwRBitMask        = 0x0F00;
    pfmt->u3.dwGBitMask        = 0x00F0;
    pfmt->u4.dwBBitMask        = 0x000F;
    pfmt->u5.dwRGBAlphaBitMask = 0xF000;
    memcpy(&fmts[count++], &temp, sizeof(DDSURFACEDESC));

    /* L8 */
    pfmt->dwFlags = DDPF_LUMINANCE;
    pfmt->u1.dwLuminanceBitCount       = 8;
    pfmt->u2.dwLuminanceBitMask        = 0xFF;
    pfmt->u3.dwGBitMask                = 0x00;
    pfmt->u4.dwBBitMask                = 0x00;
    pfmt->u5.dwRGBAlphaBitMask         = 0x00;
    memcpy(&fmts[count++], &temp, sizeof(DDSURFACEDESC));

    /* A8L8 */
    pfmt->dwFlags = DDPF_LUMINANCE | DDPF_ALPHAPIXELS;
    pfmt->u1.dwLuminanceBitCount       = 16;
    pfmt->u2.dwLuminanceBitMask        = 0x00FF;
    pfmt->u3.dwGBitMask                = 0x0000;
    pfmt->u4.dwBBitMask                = 0x0000;
    pfmt->u5.dwLuminanceAlphaBitMask   = 0xFF00;
    memcpy(&fmts[count++], &temp, sizeof(DDSURFACEDESC));

    if (gld->gl_ext[EXT_paletted_texture]) {
	/* P8 */
	pfmt->dwFlags = DDPF_RGB | DDPF_PALETTEINDEXED8;
	pfmt->u1.dwRGBBitCount     = 8;
	pfmt->u2.dwRBitMask        = 0x00;
	pfmt->u3.dwGBitMask        = 0x00;
	pfmt->u4.dwBBitMask        = 0x00;
	pfmt->u5.dwRGBAlphaBitMask = 0x00;
	memcpy(&fmts[count++], &temp, sizeof(DDSURFACEDESC));

	/* A8P8 */
	pfmt->dwFlags = DDPF_RGB | DDPF_PALETTEINDEXED8 | DDPF_ALPHAPIXELS;
	pfmt->u1.dwRGBBitCount     = 16;
	pfmt->u2.dwRBitMask        = 0x0000;
	pfmt->u3.dwGBitMask        = 0x0000;
	pfmt->u4.dwBBitMask        = 0x0000;
	pfmt->u5.dwRGBAlphaBitMask = 0xFF00;
	memcpy(&fmts[count++], &temp, sizeof(DDSURFACEDESC));
    }

    /* FIXME: bumpmap format */

    if (gld->gl_ext[ARB_texture_compression]) {
	/* compressed texture formats */
	if (gld->gl_ext[EXT_texture_compression_s3tc]) {
	    pfmt->dwFlags = DDPF_FOURCC;
	    pfmt->dwFourCC = 0x31545844; /* DXT1 */
	    pfmt->u2.dwRBitMask        = 0x00F800;
	    pfmt->u3.dwGBitMask        = 0x0007E0;
	    pfmt->u4.dwBBitMask        = 0x00001F;
	    pfmt->u5.dwRGBAlphaBitMask = 0x000000;
	    memcpy(&fmts[count++], &temp, sizeof(DDSURFACEDESC));

	    pfmt->dwFlags = DDPF_ALPHAPIXELS | DDPF_FOURCC;
	    pfmt->dwFourCC = 0x31545844; /* DXT1 */
	    pfmt->u5.dwRGBAlphaBitMask = 0x010000;
	    memcpy(&fmts[count++], &temp, sizeof(DDSURFACEDESC));

	    pfmt->dwFourCC = 0x33545844; /* DXT3 */
	    pfmt->u5.dwRGBAlphaBitMask = 0x0F0000;
	    memcpy(&fmts[count++], &temp, sizeof(DDSURFACEDESC));

	    pfmt->dwFourCC = 0x35545844; /* DXT5 */
	    pfmt->u5.dwRGBAlphaBitMask = 0xFF0000;
	    memcpy(&fmts[count++], &temp, sizeof(DDSURFACEDESC));
	}
    }

    gld->lpTextureFormats = fmts;
    gld->dwNumTextureFormats = count;
}

static void add_zbuffer_fmts(DD_GL_data *gld)
{
    DDPIXELFORMAT temp, *fmts;
    DWORD count = 2;
    memset(&temp, 0, sizeof(DDPIXELFORMAT));
    fmts = HeapAlloc(GetProcessHeap(),0,sizeof(DDPIXELFORMAT)*count);
    temp.dwSize = sizeof(DDPIXELFORMAT);
    temp.dwFlags = DDPF_ZBUFFER;
    temp.dwFourCC = 0;
    count = 0;

    temp.u1.dwZBufferBitDepth = 16;
    temp.u2.dwStencilBitDepth = 0;
    temp.u3.dwZBitMask = 0xffff;
    temp.u4.dwStencilBitMask = 0;
    temp.u5.dwRGBZBitMask = 0; /* What does this field mean in this case? */
    memcpy(&fmts[count++], &temp, sizeof(DDPIXELFORMAT));

    /* This is pretty silly. Can we safely enumerate a 24/8 format here? */
    temp.dwFlags |= DDPF_STENCILBUFFER;
    temp.u2.dwStencilBitDepth = 8;
    temp.u3.dwZBitMask = 0x00ff;
    temp.u4.dwStencilBitMask = 0xff00;
    memcpy(&fmts[count++], &temp, sizeof(DDPIXELFORMAT));

    gld->lpZPixelFormats = fmts;
    gld->dwNumZPixelFormats = count;
}

LPVOID WINAPI D3DGL_GetCaps(LPDDRAWI_DIRECTDRAW_GBL dd, DWORD d3d_version)
{
    DD_GL_data *gld = (DD_GL_data *)dd->dwReserved3;
    switch (d3d_version) {
    case 9:
    case 8: /* D3DCAPS8 seems to be a proper subset of D3DCAPS9 */
	return &gld->caps9;
    default:
	return NULL;
    }
}
