///////////////////////////////////////////////////////////////////////////////
/// @file DDirect3D.cpp
///
/// @brief class for Direct3D8 driver
///
/// @author Lightning
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Aug 2001
/// @author Lightning
/// @remarks Creation
///
///////////////////////////////////////////////////////////////////////////////

//#include "Destiny3D.hpp"

#include "DDirect3D.hpp"
#include "deBitmap.hpp"
#include "deMath.hpp"

// For testing purposes
#if 1
#define D3D_DEV_TYPE (D3DDEVTYPE_HAL)
#else
#define D3D_DEV_TYPE (D3DDEVTYPE_REF)
#endif

#if 0
#ifdef _DEBUG
#include <crtdbg.h>
	#if 0
	#undef malloc
	#undef new
	#define new new(_NORMAL_BLOCK,strdup(__FILE__), __LINE__)
	#define malloc(s) _malloc_dbg(s,_NORMAL_BLOCK,strdup(__FILE__),__LINE__)
	#else
	#define new new(_NORMAL_BLOCK,__FILE__, __LINE__)
	#define malloc(s) _malloc_dbg(s,_NORMAL_BLOCK,__FILE__,__LINE__)
	#endif
#endif
#endif

typedef IDirect3D8* (WINAPI *fDirect3DCreate8)(UINT SDKVersion);

//information about the driver
deDriverInfo	DriverInfo;

//a flag to indicate if the adapter information has been logged
deBoolean		AdapterInfoLogged = deFALSE;

long			deDirect3D8::s_AdapterCount = 0;			//number of adapters
int *			deDirect3D8::s_ModeCount = 0;			//total number of modes
deBoolean *		deDirect3D8::s_WindowPossible = 0;		//if each adapter can do windowed
HINSTANCE		deDirect3D8::s_libD3D8 = 0;
LPDIRECT3D8		deDirect3D8::s_D3D = 0;
D3DCAPS8 *		deDirect3D8::s_DevCaps = 0;
IdeDriver::deDriverCaps*	deDirect3D8::s_Caps = 0;
long			deDirect3D8::s_NumDevices = 0;

#define VERTEXSHADERID	0x01005356		//"VS",0,1
#define TEXTURESTRUCTID 0x01005354		//"TS",0,1
#define ReportErrorHR(hr)\
{\
	char buffer[256];\
	LogInfo("Error value: 0x%x (%s)\n", hr, HR2String(hr, buffer));\
	DEBUGMSG1("Error return value: %s", buffer);\
}

//needed functions to identify the driver
#ifdef _DEBUG
deBoolean IsDestiny3DDriverDebug()
{
	//tell them that we are a Destiny3D driver
	return deTRUE;
}
#else
deBoolean IsDestiny3DDriver()
{
	//tell them that we are a Destiny3D driver
	return deTRUE;
}
#endif

IdeDriver	*GetDriverInstance(long ID)
{
	//return a new instance of this driver
	deDirect3D8 *NewInstance = new deDirect3D8(ID);
	return NewInstance;
}

const deDriverInfo *GetDriverInfo(long StructSize)
{
	//make sure we can talk to each other and use the same size structs
	if(StructSize != sizeof(deDriverInfo))
		return NULL;

	//setup the type of driver this is
	IdeDriver_FillVersionInfo(&DriverInfo, __DATE__, __TIME__);
	DriverInfo.Name = "Direct3D8.1";
	DriverInfo.DLL = NULL;

	//return the name of this driver that people can show off
	return &DriverInfo;
}

deDirect3D8::deDirect3D8(long ID)
: m_TexturesList(97), m_VBufferList(997)
{
	s_NumDevices++;

	//m_D3DDevice variables
	m_D3DDevice = NULL;
	m_AdapterNum = -1;

	//set the unique ID
	m_UniqueID = ID;

	m_LastShader = 0xffffffff;
	m_DeviceLost = deTRUE;
	m_RenderStatesValid = deTRUE;
	m_TextureRendered = deFALSE;
	m_pZBuffer = NULL;
	m_pCurrentTarget = NULL;
	m_FrameNum = 1;

	m_AmbientSource = IdeRenderMaterial::MATSOURCE_MATERIAL;
	m_DiffuseSource = IdeRenderMaterial::MATSOURCE_DIFFUSE;
	m_SpecularSource = IdeRenderMaterial::MATSOURCE_SPECULAR;
	m_EmissiveSource = IdeRenderMaterial::MATSOURCE_MATERIAL;

	memset(&m_LightsEnabled, 0, deD3D8_MAX_LIGHTS*sizeof(deBoolean));

	memset(&m_WorldMatrix, 0, 16*sizeof(float));
	m_WorldMatrix._11 = 1;
	m_WorldMatrix._22 = 1;
	m_WorldMatrix._33 = 1;
	m_WorldMatrix._44 = 1;
	memcpy(&m_CameraMatrix, &m_WorldMatrix, 16*sizeof(float));

	m_CachedTexture = NULL;
	if (!CreateDefaultCachedTexture())
		throw "Couldn't create Direct3D cached texture state";

	return;
}

deDirect3D8::~deDirect3D8()
{
	ReleaseVideoMemResources();
	{
		int failed;
		failed = DeleteShaders();
		if (failed > 0)
			throw;
	}

	if (m_CachedTexture)
	{
		m_CachedTexture->Release();
		m_CachedTexture = NULL;
	}

	if(m_D3DDevice)
	{
		m_D3DDevice->Release();
		m_D3DDevice = NULL;
	}

	s_NumDevices--;
	if (s_NumDevices <= 0)
	{
		if(s_D3D)
		{
			s_D3D->Release();
			s_D3D = NULL;
		}
		
		if(s_DevCaps)
		{
			delete[] s_DevCaps;
			s_DevCaps = NULL;
		}
		
		if(s_Caps)
		{
			delete[] s_Caps;
			s_Caps = NULL;
		}
		
		if(s_ModeCount)
		{
			delete[] s_ModeCount;
			s_ModeCount = NULL;
		}
		
		if(s_WindowPossible)
		{
			delete[] s_WindowPossible;
			s_WindowPossible = NULL;
		}
		
		if (s_libD3D8)
			FreeLibrary(s_libD3D8);
	}
	
	return;
}

long deDirect3D8::Release()
{
	delete this;
	return 0;
}

deDriverInfo *deDirect3D8::GetInfo() const
{
	//return the driver's information
	return (deDriverInfo *)GetDriverInfo(sizeof(deDriverInfo));
}

deBoolean deDirect3D8::CreateDefaultCachedTexture()
{
	// this should all match d3d's default settings
	int i;
	if (m_CachedTexture)
		m_CachedTexture->Release();
	m_CachedTexture = IdeRenderState_CreateRenderTexture();
	if (!m_CachedTexture)
		DEBUGRETURN(deFALSE, "CreateDefaultCachedTexture: failure to create object");
	m_CachedTexture->Lock();
	for (i = 0; i < 8; ++i)
	{
		m_CachedTexture->SetBitmap(i, NULL);
		m_CachedTexture->SetColorArg(i, 1, IdeRenderTexture::TEXTUREARG_TEXTURE);
		m_CachedTexture->SetColorArg(i, 2, IdeRenderTexture::TEXTUREARG_PREVIOUS);
		m_CachedTexture->SetColorArg(i, 3, IdeRenderTexture::TEXTUREARG_PREVIOUS);
		m_CachedTexture->SetColorOperation(i, IdeRenderTexture::TEXTUREOP_DISABLE);
		m_CachedTexture->SetAlphaArg(i, 1, IdeRenderTexture::TEXTUREARG_TEXTURE);
		m_CachedTexture->SetAlphaArg(i, 2, IdeRenderTexture::TEXTUREARG_PREVIOUS);
		m_CachedTexture->SetAlphaArg(i, 3, IdeRenderTexture::TEXTUREARG_PREVIOUS);
		m_CachedTexture->SetAlphaOperation(i, IdeRenderTexture::TEXTUREOP_DISABLE);
		m_CachedTexture->SetResultTarget(i, IdeRenderTexture::TEXTUREARG_PREVIOUS);
		m_CachedTexture->SetTextureMagFilter(i, IdeRenderTexture::TEXTUREFILTER_POINT);
		m_CachedTexture->SetTextureMinFilter(i, IdeRenderTexture::TEXTUREFILTER_POINT);
		m_CachedTexture->SetTextureMipFilter(i, IdeRenderTexture::TEXTUREFILTER_NONE);
	}
//	m_CachedTexture->SetColorOperation(0, IdeRenderTexture::TEXTUREOP_MODULATE);
	m_CachedTexture->SetAlphaOperation(0, IdeRenderTexture::TEXTUREOP_SELECTARG1);
	m_CachedTexture->Unlock();

	return deTRUE;
}

//default returns for the virtual functions if they are not implemented
//below are common functions that the drivers themselves do not have to implement
deBoolean deDirect3D8::WindowedAllowed(long Adapter) const
{
	//make sure we have a valid adapter number
	if(Adapter < 0 || Adapter >= s_AdapterCount)
		return deFALSE;
	
	//tell if this adapter can do window'd mode or not
	return s_WindowPossible[Adapter];
}

long deDirect3D8::GetModeCount(long Adapter) const
{
	//make sure we have a valid adapter number
	if(Adapter < 0 || Adapter >= s_AdapterCount)
		return 0;
	
	//return the number of modes this adapter has
	return s_ModeCount[Adapter];
}

long deDirect3D8::GetAdapterCount() const
{
	//return the number of adapters we have
	return s_AdapterCount;
}

const IdeDriver::deDriverCaps* deDirect3D8::GetCaps(long Adapter) const
{
	if (Adapter < 0 || Adapter >= s_AdapterCount)
		return NULL;
	
	return &s_Caps[Adapter];
}

#pragma todo (try to test these values to find out the real effective size)
struct CardVCache
{
	const char * Name;
	int Size;
};
static CardVCache s_CardListing[] = 
{
	{"RADEON 8500", 12},
	{"RADEON 9700", 12},
	{"GEFORCE 256", 12},
	{"GEFORCE2", 12},
	{"GEFORCE3", 16},
	{"GEFORCE4", 18},
};
deBoolean deDirect3D8::Init(HWND WindowHandle)
{
	HRESULT		hr;
	int			Counter;
	SYSTEMTIME	Time;
	D3DADAPTER_IDENTIFIER8	AdapterIdentifier;

	//set what window we are using for all this
	m_CurWindowHandle = WindowHandle;

	//if we haven't logged anything yet then log some data
	if(!AdapterInfoLogged)
	{
		//make sure we have the driver's information
		if(!GetInfo())
			return deFALSE;
		
		//put in the information for a new entry
		LogInfo("------------------------------------------\n");
		LogInfo("%s v%d.%d.%d\n", DriverInfo.Name, DriverInfo.Major, DriverInfo.Minor, DriverInfo.Revision);
#ifdef __GNUC__
		LogInfo("Compiled on: \t%s\n", __DATE__ __TIME__);
#else
		LogInfo("Compiled on: \t%s\n", __TIMESTAMP__);
#endif
		LogInfo("Compiled under: \t%s\n", DESTINY3D_COMPILER);

		//output what time this was logged
		GetLocalTime(&Time);
		LogInfo("Current Time:\t%.2d/%.2d/%.2d %.2d:%.2d:%.2d\n\n", Time.wMonth, Time.wDay, Time.wYear, Time.wHour, Time.wMinute, Time.wSecond);
	}

	{
		s_libD3D8 = LoadLibrary("d3d8.dll");
		if (s_libD3D8)
		{
			fDirect3DCreate8 pDirect3DCreate8;
			
			pDirect3DCreate8 = (fDirect3DCreate8)GetProcAddress(s_libD3D8, "Direct3DCreate8");
			
			if (pDirect3DCreate8)
			{
				s_D3D = pDirect3DCreate8(D3D_SDK_VERSION);

			}
			else
			{
				if(!AdapterInfoLogged)
					LogInfo("Failed to initalize DirectX, could not load Direct3DCreate8\n");
			}
		}
		else
		{
			if(!AdapterInfoLogged)
				LogInfo("Failed to initalize DirectX, could not load d3d8.dll\n");
		}
	}

	//initialize Direct3D
	if(!s_D3D)
	{
		if(!AdapterInfoLogged)
			LogInfo("Failed to initalize DirectX, incorrect DirectX version\n");
		
		return deFALSE;
	}

	//grab how many adapters there are
	s_AdapterCount = s_D3D->GetAdapterCount();

	//write out how many adapters we found
	if(!AdapterInfoLogged)
		LogInfo("Adapter Count: %d\n\n", s_AdapterCount);

	if(!s_AdapterCount)
	{
		LogInfo("No valid adapters found, exiting...\n\n");
		return deFALSE;
	}

	//resize the array for the adapters

	//our list of what adapters can do windowed mode
	s_WindowPossible = new deBoolean[s_AdapterCount];
	if(!s_WindowPossible)
		return deFALSE;
	memset(s_WindowPossible, 0, sizeof(deBoolean) * s_AdapterCount);

	//our list of number of modes for the adapters
	s_ModeCount = new int[s_AdapterCount];
	if(!s_ModeCount)
		return deFALSE;
	memset(s_ModeCount, 0, sizeof(int) * s_AdapterCount);

	//our device capabilities for each adapter
	s_DevCaps = new D3DCAPS8[s_AdapterCount];
	if(!s_DevCaps)
		return deFALSE;
	memset(s_DevCaps, 0, sizeof(D3DCAPS8) * s_AdapterCount);

	s_Caps = new deDriverCaps[s_AdapterCount];
	if(!s_Caps)
		return deFALSE;
	memset(s_Caps, 0, sizeof(deDriverCaps) * s_AdapterCount);

	//memory for the bits per pixel for the different adapters

	//get a count of the total number of modes
	for(Counter=0; Counter < s_AdapterCount; Counter++)
	{
		if(!AdapterInfoLogged)
			LogInfo("Adapter %d:\n", Counter);
		
		hr = s_D3D->GetAdapterIdentifier(Counter, D3DENUM_NO_WHQL_LEVEL, &AdapterIdentifier);
		if (D3D_OK != hr)
		{
			if(!AdapterInfoLogged)
				LogInfo("Failed to retrieve Adapter driver information\n");
			
			ReportErrorHR(hr);
			
			return deFALSE;
		}
		
		if(!AdapterInfoLogged)
		{
			LogInfo("\tAdapter name: %s\n", AdapterIdentifier.Description);
			LogInfo("\t\tProduct:%d Version:%d SubVer:%d Build:%d\n", 
				HIWORD(AdapterIdentifier.DriverVersion.HighPart),
				LOWORD(AdapterIdentifier.DriverVersion.HighPart),
				HIWORD(AdapterIdentifier.DriverVersion.LowPart),
				LOWORD(AdapterIdentifier.DriverVersion.LowPart));
			LogInfo("\t\tVendor:0x%.4x Device:0x%.4x SubSys:0x%.8x Rev:%d\n", 
				AdapterIdentifier.VendorId,
				AdapterIdentifier.DeviceId,
				AdapterIdentifier.SubSysId,
				AdapterIdentifier.Revision);
		}
		
		strncpy(s_Caps[Counter].AdapterDescription, AdapterIdentifier.Description, 254);
		s_Caps[Counter].AdapterDescription[254] = '\0';
		_strupr(s_Caps[Counter].AdapterDescription);

		s_ModeCount[Counter] = s_D3D->GetAdapterModeCount(Counter);

		//write out how many modes for the current adapter
		if(!AdapterInfoLogged)
			LogInfo("\tMode Count: %d\n", s_ModeCount[Counter]);

		//if any modes, get information on them
		if(s_ModeCount[Counter])
		{
			//we have modes for this video card so we can use it
			
			//see what we have to initalize the adapter in
			hr = s_D3D->GetDeviceCaps(Counter, D3D_DEV_TYPE, &s_DevCaps[Counter]);
			if(hr != D3D_OK)
			{
				if(!AdapterInfoLogged)
				{
					LogInfo("\tFailed to initialize in HAL mode\n");
					ReportErrorHR(hr);
				}
				s_ModeCount[Counter] = 0;
				if (hr == D3DERR_INVALIDCALL)
					return deFALSE;
				if (hr == D3DERR_INVALIDDEVICE)
					continue;
				if (hr == D3DERR_OUTOFVIDEOMEMORY)
					continue;
				// way to go microsoft, this one is undocumented
				if (hr == D3DERR_NOTAVAILABLE)
					continue;
				continue; // go to the next adapter
			}
			else
			{
				//write out what mode we initalized in
				if(!AdapterInfoLogged)
					LogInfo("\tInitialized in HAL mode\n");
			}

			//write out if window'd is supported
			if(!AdapterInfoLogged)
				LogInfo("\tSupports windowed: ");

			//check if this adapter can do windowed mode
			if(s_DevCaps[Counter].Caps2 & D3DCAPS2_CANRENDERWINDOWED)
			{
				s_WindowPossible[Counter] = deTRUE;
				if(!AdapterInfoLogged)
					LogInfo("Yes\n");
			}
			else
			{
				if(!AdapterInfoLogged)
					LogInfo("No\n");
			}

			//see how many streams are allowed on the card
			if(!AdapterInfoLogged)
			{
				LogInfo("\tSupported Streams: %d", s_DevCaps[Counter].MaxStreams);

				if(s_DevCaps[Counter].MaxStreams == 0)
					LogInfo(" - WARNING! Card does not appear to support streams, or will support them through software");
				
				LogInfo("\n");
			}

			s_Caps[Counter].VertCacheSize = 0; // default to 0
			{
				for (int x = 0; x < sizeof(s_CardListing)/sizeof(CardVCache); ++x)
				{
					if (strstr(s_Caps[Counter].AdapterDescription, s_CardListing[x].Name))
					{
						s_Caps[Counter].VertCacheSize = s_CardListing[x].Size;
						break;
					}
				}
			}
			s_Caps[Counter].MaxTextureBitDepth = 0; // don't know until the device is set up in a format
			s_Caps[Counter].SimulTextures = s_DevCaps[Counter].MaxSimultaneousTextures;
			s_Caps[Counter].TexWidth = s_DevCaps[Counter].MaxTextureWidth;
			s_Caps[Counter].TexHeight = s_DevCaps[Counter].MaxTextureHeight;
			s_Caps[Counter].NumStreams = s_DevCaps[Counter].MaxStreams;
			s_Caps[Counter].MaxVert = s_DevCaps[Counter].MaxVertexIndex;
			s_Caps[Counter].MaxPrim = s_DevCaps[Counter].MaxPrimitiveCount;
			s_Caps[Counter].MaxLights = s_DevCaps[Counter].MaxActiveLights;
			s_Caps[Counter].VShader = ((s_DevCaps[Counter].VertexShaderVersion >> 8) & 0xff) + ((s_DevCaps[Counter].VertexShaderVersion & 0xff)/256.0f);
			s_Caps[Counter].PShader = ((s_DevCaps[Counter].PixelShaderVersion >> 8) & 0xff) + ((s_DevCaps[Counter].VertexShaderVersion & 0xff)/256.0f);
			s_Caps[Counter].HWTnL = !!(s_DevCaps[Counter].DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT);
			s_Caps[Counter].Dot3Support = !!(s_DevCaps[Counter].TextureOpCaps & D3DTEXOPCAPS_DOTPRODUCT3);
		}

		//put a blank line after the last line
		if(!AdapterInfoLogged)
			LogInfo("\n");
	}

	//add a blank line
	if(!AdapterInfoLogged)
		LogInfo("\n");
	
	//set the flag so that we don't go logging the info again
	AdapterInfoLogged = deTRUE;

	return deTRUE;
}

deBoolean deDirect3D8::SetDisplay(IdeDriver::deDisplay *Display)
{
	if (!Display)
		DEBUGRETURN(deFALSE, "SetDisplay: Display was NULL");
	//if they want windowed and this card can't handle it, forget it
	if(Display->Windowed && !s_WindowPossible[Display->Adapter])
		return deFALSE;

	// initialize variables after initial fail-checks
	D3DPRESENT_PARAMETERS	D3DParams;
	HRESULT					hr;
	long					VertexProcess;
	deBoolean				FindingDisplay;

	//if a current device exists, destroy it and recreate it
	//also must go thru and destroy the bitmaps and vertex buffers
	if(m_D3DDevice)
	{
		if (!ReleaseVideoMemResources())
			return deFALSE;
		m_D3DDevice->Release();
		m_D3DDevice = NULL;
	}

	// fill in the s_D3D params
	if (!BuildD3DParams(Display, &D3DParams))
	{
		LogInfo("SetDisplay failed: unable to use selected display parameters");
		return deFALSE;
	}

	//set the current display to the one passed in
	Display->MonitorHandle = s_D3D->GetAdapterMonitor(Display->Adapter);
	m_CurrentDisplay = *Display;

	//create the device
	VertexProcess = D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE;

	//lets see if we can find a mode that this card can do...
	FindingDisplay = deTRUE;
	hr = D3D_OK;
	while(FindingDisplay)
	{
		hr = s_D3D->CreateDevice(Display->Adapter, D3D_DEV_TYPE, m_CurWindowHandle, VertexProcess, &D3DParams, &m_D3DDevice);
#pragma todo (insert more detailed testing here to make sure the device can handle things like vshader)
		if(hr == D3D_OK)
		{
			//found a valid mode, exit the loop
			break;
		}

		if (Display->TripleBuffer && D3DParams.BackBufferCount == 1)
		{
			// try again with the adjusted struct
			continue;
		}

		switch(VertexProcess)
		{
		case D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE:
			VertexProcess = D3DCREATE_HARDWARE_VERTEXPROCESSING;
			break;

		case D3DCREATE_HARDWARE_VERTEXPROCESSING:
			VertexProcess = D3DCREATE_MIXED_VERTEXPROCESSING;
			break;

		case D3DCREATE_MIXED_VERTEXPROCESSING:
			VertexProcess = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
			break;

		case D3DCREATE_SOFTWARE_VERTEXPROCESSING:
			//haven't found it yet, fail
			FindingDisplay = deFALSE;
			break;
		}
	}

	if(!FindingDisplay)
	{
		LogInfo("Could not find valid vertex processing mode for HAL device type\n");
		ReportErrorHR(hr);
		return deFALSE;
	}

	//log the rendertype being used
	LogInfo("\tRender Type: HAL\n");

	//log the type of vertex processing
	LogInfo("\tVertex Processing: ");
	switch(VertexProcess)
	{
	case D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE:
		LogInfo("Hardware, using Pure Device\n");
		break;

	case D3DCREATE_HARDWARE_VERTEXPROCESSING:
		LogInfo("Hardware\n");
		break;

	case D3DCREATE_MIXED_VERTEXPROCESSING:
		LogInfo("Mixed\n");
		break;

	case D3DCREATE_SOFTWARE_VERTEXPROCESSING:
		LogInfo("Software\n");
		break;
	}

	{
		typedef struct _D3DDEVINFO_VCACHE {
			DWORD   Pattern;                    /* bit pattern, return value must be FOUR_CC(‘C’, ‘A’, ‘C’, ‘H’) */
			DWORD   OptMethod;                  /* optimization method 0 means longest strips, 1 means vertex cache based */
			DWORD   CacheSize;                  /* cache size to optimize for  (only required if type is 1) */
			DWORD   MagicNumber;                /* used to determine when to restart strips (only required if type is 1)*/
		} D3DDEVINFO_VCACHE, *LPD3DDEVINFO_VCACHE;
		const int D3DDEVINFOID_VCACHE = 4;

		D3DDEVINFO_VCACHE vcache;
		HRESULT hr;
		hr = m_D3DDevice->GetInfo(D3DDEVINFOID_VCACHE, &vcache, sizeof(vcache));
		LogInfo("\tVertex Cache Size: ", vcache.CacheSize);
		if (hr == D3D_OK)
		{
			s_Caps[Display->Adapter].VertCacheSize = vcache.CacheSize;
			LogInfo("%d\n", vcache.CacheSize);
		}
		else
		{
			LogInfo("Unknown, guessing %d\n", s_Caps[Display->Adapter].VertCacheSize);
		}
	}

	IdeDriver::BPP TexFormat;
	for (TexFormat = IdeDriver::BPP_UNKNOWN; TexFormat < IdeDriver::BPP_COUNT; ((long&)TexFormat)++)
	{
		if (ValidFormat(Display->Adapter, RESOURCE_TEXTURE, TexFormat, ZPP_UNKNOWN, Display->FrontBpp, 0))
		{
			if (IdeDriver_BPPBitDepth(TexFormat) > s_Caps[Display->Adapter].MaxTextureBitDepth)
				s_Caps[Display->Adapter].MaxTextureBitDepth = IdeDriver_BPPBitDepth(TexFormat);
		}
	}

	// record the just-found info
	LogInfo("\tMax Texture BitDepth: %d", s_Caps[Display->Adapter].MaxTextureBitDepth);

	//extra blank lines
	LogInfo("\n\n");

	// must set this before renderstates are set, or they won't work
	m_DeviceLost = deFALSE;

	//set the default renderstates
	SetDefaultRenderstates();

	//update the window rect
	UpdateWindowRect();

	m_D3DDevice->GetDepthStencilSurface(&m_pZBuffer);
	if (m_pCurrentTarget)
	{
		m_pCurrentTarget->Release();
		m_pCurrentTarget = NULL;
	}

	m_AdapterNum = Display->Adapter;

	//return
	return deTRUE;
}

deBoolean deDirect3D8::Reset(deDisplay *Display)
{
	// this looks like a nice simple function doesn't it?
	// but this took me a freakin WHOLE DAY to write!
	D3DPRESENT_PARAMETERS	D3DParams;
	deDisplay *				Display2;

	Display2 = (deDisplay*)GetDisplay();
	if (Display == NULL)
	{
		Display = Display2;
	}
	else
	{
		if (Display->Adapter != Display2->Adapter)
			return deFALSE;
	}

	if (!BuildD3DParams(Display, &D3DParams))
	{
		LogInfo("Reset failed: unable to use selected display parameters");
		return deFALSE;
	}

	m_DeviceLost = deTRUE;
	
	m_CurrentDisplay = *Display;
	memset(&m_CurrentViewport, 0, sizeof(m_CurrentViewport));
	
	if (!ResetDevice(&D3DParams))
		return deFALSE;

	return deTRUE;
}

deBoolean deDirect3D8::TestDeviceStatus(deBoolean * DeviceCanReset)
{
	HRESULT Ret;
	
	Ret = m_D3DDevice->TestCooperativeLevel();

	// device is ok, can use it
	if (Ret == D3D_OK)
		return deTRUE;

	// if the device is not usable, return false down below

	if (Ret == D3DERR_DEVICELOST)
		*DeviceCanReset = deFALSE;		// device can not be reset yet
	if (Ret == D3DERR_DEVICENOTRESET)
		*DeviceCanReset = deTRUE;		// device may now be reset

	return deFALSE;
}

deBoolean deDirect3D8::ResetDevice(D3DPRESENT_PARAMETERS	*D3DParams)
{
	HRESULT Ret;

	if (!ReleaseVideoMemResources())
		return deFALSE;

	m_DeviceLost = deTRUE;
	
	Ret = m_D3DDevice->Reset(D3DParams);

	if (Ret != D3D_OK)
	{
		// other return values:
		// D3DERR_INVALIDCALL  
		// D3DERR_OUTOFVIDEOMEMORY 
		// E_OUTOFMEMORY 
		
		LogInfo("Failure while resetting device\n");
		ReportErrorHR(Ret);

		return deFALSE;
	}

//#pragma todo(go through and re-upload necessary items)
	
	//update the window rect
	UpdateWindowRect();
	
	Ret = m_D3DDevice->GetDepthStencilSurface(&m_pZBuffer);
	if (Ret != D3D_OK)
	{
		LogInfo("Failure while obtaining depth-stencil surface\n");
		ReportErrorHR(Ret);
		
		return deFALSE;
	}
	if (m_pCurrentTarget)
	{
		m_pCurrentTarget->Release();
		m_pCurrentTarget = NULL;
	}
	
	m_DeviceLost = deFALSE;

	//set the default renderstates
	SetDefaultRenderstates();
	
	return deTRUE;
}

deBoolean deDirect3D8::ReleaseVideoMemResources()
{
	int i;
	CreateDefaultCachedTexture();
	{
		deTArray <IdeBitmap**>	array;
		IdeBitmap *				Bitmap;
		TextureEntry*			Entry;
		
		m_TexturesList.GetAllElements(array);
		for (i = 0; i < array.Length(); i++)
		{
			Bitmap = *(array[i]);
			if (Bitmap)
			{
				Entry = (TextureEntry*)Bitmap->GetDriverData(m_UniqueID);
				if (Entry->DepthStencil)
				{
					Entry->DepthStencil->Release();
				}
				if (Entry->Texture)
				{
					Entry->Texture->Release();
				}
				if (Entry)
				{
					free(Entry);
				}
				if (!Bitmap->SetDriverData(m_UniqueID, NULL, this))
					return deFALSE;
			}
		}
		m_TexturesList.RemoveAllElements();
	}
	{
		deTArray <IdeVertexBuffer**> array;
		VertexBufferEntry* Entry;
		IdeVertexBuffer* VBuffer;
		
		m_VBufferList.GetAllElements(array);
		for (i = 0; i < array.Length(); i++)
		{
			VBuffer = *(array[i]);
			Entry = (VertexBufferEntry*)VBuffer->GetDriverBuffer(m_UniqueID, NULL);
			VBuffer->Lock();
			VBuffer->SetDriverBuffer(m_UniqueID, NULL, this);
			VBuffer->Unlock();
			if (Entry)
			{
				if (!Entry->SubBuffer)
				{
					long Counter;
					for(Counter = 0; Counter < IdeVertexBuffer::BUFFER_COUNT - 1; Counter++)
					{
						//if the buffer exists, free it
						if(Entry->Buffers[Counter])
						{
							Entry->Buffers[Counter]->Release();
							Entry->Buffers[Counter] = NULL;
						}
					}
					if (Entry->IndexBuffer)
					{
						Entry->IndexBuffer->Release();
						Entry->IndexBuffer = NULL;
					}
				}
				free(Entry);
			}
		}
		m_VBufferList.RemoveAllElements();
	}
	if (m_pZBuffer)
	{
		m_pZBuffer->Release();
		m_pZBuffer = NULL;
	}
	if (m_pCurrentTarget)
	{
		m_pCurrentTarget->Release();
		m_pCurrentTarget = NULL;
	}
	m_LastShader = 0;
	return deTRUE;
}

deBoolean deDirect3D8::BuildD3DParams(deDisplay * Display, D3DPRESENT_PARAMETERS *D3DParams)
{
	if (!s_D3D || !Display || !D3DParams)
		return deFALSE;

	//clear it all out
	memset(D3DParams, 0, sizeof(D3DPRESENT_PARAMETERS));
	
	//set a few flags
	D3DParams->Windowed = Display->Windowed;
	
	D3DParams->hDeviceWindow = m_CurWindowHandle;
	D3DParams->EnableAutoDepthStencil = 1;
	D3DParams->BackBufferHeight = Display->Height;
	D3DParams->BackBufferWidth = Display->Width;
	if (Display->TripleBuffer)
		D3DParams->BackBufferCount = 2;
	else
		D3DParams->BackBufferCount = 1;
	if (Display->FSAASamples == 0 || (Display->FSAASamples >= 2 && Display->FSAASamples <= 16))
	{
		D3DParams->MultiSampleType =  (D3DMULTISAMPLE_TYPE)(Display->FSAASamples);
	}
	else
	{
		DEBUGMSG1("Unsupported number of FSAA samples: %d", Display->FSAASamples);
		D3DParams->MultiSampleType = D3DMULTISAMPLE_NONE;
	}
	
	//log the adapter being used
	LogInfo("Using Display Adapter %d:\n\tWidth: %d\tHeight: %d\tFSAA: %d\n", Display->Adapter, Display->Width, Display->Height, D3DParams->MultiSampleType);
	
	//if not windowed mode, then tell it the width and height to be
	if(!Display->Windowed)
	{
		//verify the BPP & z-buffer are valid
		if(!ValidFormat(Display->Adapter, RESOURCE_DISPLAY, Display->BackBpp, ZPP_UNKNOWN, Display->FrontBpp, 0))
			return deFALSE;

		if(!ValidFormat(Display->Adapter, RESOURCE_FSAA, Display->BackBpp, ZPP_UNKNOWN, BPP_UNKNOWN, Display->FSAASamples))
			return deFALSE;
		
		//set the backbuffer format
		D3DParams->BackBufferFormat = ToD3DBPP(Display->BackBpp);

		if (Display->RefreshRate != 0)
			D3DParams->FullScreen_RefreshRateInHz = Display->RefreshRate;
		else
			D3DParams->FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;

		if (Display->IgnoreVSync)
			D3DParams->FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
		else
			D3DParams->FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_ONE;
		
		D3DParams->SwapEffect = D3DSWAPEFFECT_DISCARD;
	}
	else
	{
		if (!ValidFormat(Display->Adapter, RESOURCE_DISPLAYWINDOWED, Display->BackBpp, ZPP_UNKNOWN, Display->FrontBpp, 0))
		{
			deDisplay		DisplayMode;
			
			//windowed. we gotta grab the current display's pixel format
			if (!GetCurrentDisplay(Display->Adapter, &DisplayMode))
				return deFALSE;
			Display->FrontBpp = DisplayMode.BackBpp;
			
			if (!ValidFormat(Display->Adapter, RESOURCE_DISPLAYWINDOWED, Display->BackBpp, ZPP_UNKNOWN, Display->FrontBpp, 0))
			{
				return deFALSE;
			}
		}

		if(!ValidFormat(Display->Adapter, RESOURCE_FSAAWINDOWED, Display->BackBpp, ZPP_UNKNOWN, BPP_UNKNOWN, Display->FSAASamples))
			return deFALSE;
		
		D3DParams->BackBufferFormat = ToD3DBPP(Display->BackBpp);
		
		D3DParams->FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
		D3DParams->FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;

		//if we are doing windowed mode, we have to use a different swap effect due to the
		//rect we use
		D3DParams->SwapEffect = D3DSWAPEFFECT_DISCARD;
		D3DParams->BackBufferCount = 1;
	}

	if (!ValidFormat(Display->Adapter, RESOURCE_DEPTHSTENCIL, Display->BackBpp, Display->Zpp, Display->FrontBpp, 0))
	{
		LogInfo("Failed to use selected z-buffer format");
		return deFALSE;
	}
	D3DParams->AutoDepthStencilFormat = ToD3DZPP(Display->Zpp);
	
	return deTRUE;
}

deBoolean deDirect3D8::BeginFrame()
{
	if (m_DeviceLost)
	{
		deBoolean CanReset;
		if (!TestDeviceStatus(&CanReset))
		{
			if (!CanReset)
				return deTRUE;
			if (!Reset(NULL))
				return deFALSE;
		}
		// else: it's good, so carry on
	}

	//start a scene
	if(m_D3DDevice->BeginScene() != D3D_OK)
		return deFALSE;

	m_FrameNum++;
	if (m_FrameNum < 1)
	{
		m_FrameNum = 1;
		int i;
		{
			deTArray <IdeBitmap**>	array;
			IdeBitmap *				Bitmap;
			TextureEntry*			Entry;
			
			m_TexturesList.GetAllElements(array);
			for (i = 0; i < array.Length(); i++)
			{
				Bitmap = *(array[i]);
				if (Bitmap)
				{
					Entry = (TextureEntry*)Bitmap->GetDriverData(m_UniqueID);
					if (Entry)
					{
						Entry->FrameUsed = -1;
						Bitmap->SetDriverData(m_UniqueID, Entry, this);
					}
				}
			}
			m_TexturesList.RemoveAllElements();
		}
		{
			deTArray <IdeVertexBuffer**> array;
			IdeVertexBuffer* VBuffer;
			VertexBufferEntry* Entry;
			
			m_VBufferList.GetAllElements(array);
			for (i = 0; i < array.Length(); i++)
			{
				VBuffer = *(array[i]);
				Entry = (VertexBufferEntry*)VBuffer->GetDriverBuffer(m_UniqueID, NULL);
				if (Entry)
				{
					Entry->FrameUsed = -1;
					VBuffer->Lock();
					VBuffer->SetDriverBuffer(m_UniqueID, Entry, this);
					VBuffer->Unlock();
				}
			}
			m_VBufferList.RemoveAllElements();
		}
	}

	return deTRUE;
}

deBoolean deDirect3D8::EndFrame()
{
	if (m_DeviceLost)
		return deTRUE;

	HRESULT Ret;
	
	//end the scene
	Ret = m_D3DDevice->EndScene();
	if(Ret != D3D_OK)
		DEBUGRETURN(deFALSE, "EndFrame: m_D3DDevice->EndScene() failed");

	// blank out all the streams so vertex buffers dont cause memory leaks
	IdeVertexBuffer::BufferType	Counter;
	for(Counter=IdeVertexBuffer::BUFFER_POSITION; Counter < IdeVertexBuffer::BUFFER_INDICES16; ((int&)Counter)++)
	{
		if (s_DevCaps[m_AdapterNum].MaxStreams > (DWORD)Counter || s_DevCaps[m_AdapterNum].MaxStreams == 0)
			Ret = m_D3DDevice->SetStreamSource(Counter, NULL, 0);
		if(Ret != D3D_OK)
			DEBUGMSG0("EndFrame: Error nullifying stream");
	}

	if (D3D_OK != m_D3DDevice->SetIndices(NULL, 0))
		return deFALSE;

	const RECT* pSourceRect;
	const RECT* pDestRect;

	if(m_CurrentDisplay.Windowed)
	{
		//windowed, update it with some copy rects
		pSourceRect = (RECT*)&m_SourceRect;
		pDestRect = (RECT*)&m_ClientRect;
	}
	else
	{
		//full screen, do a normal update
		pSourceRect = NULL;
		pDestRect = NULL;
	}
	Ret = m_D3DDevice->TestCooperativeLevel();
	if(Ret != D3D_OK)
		ReportErrorHR(Ret);
	Ret = m_D3DDevice->Present(pSourceRect, pDestRect, m_CurWindowHandle, NULL);

	if(Ret != D3D_OK)
	{
		if (Ret == D3DERR_DEVICELOST)
		{
			m_DeviceLost = deTRUE;
		}
		else
		{
			LogInfo("Failure while calling Present\n");
			LogInfo("Present Params:\n");
			LogInfo("\tpSourceRect = ");
			if (pSourceRect)
				LogInfo("{top=%d, left=%d, bottom=%d, right=%d}\n", pSourceRect->top, pSourceRect->left, pSourceRect->bottom, pSourceRect->right);
			else
				LogInfo("null\n");
			LogInfo("\tpDestRect = ");
			if (pDestRect)
				LogInfo("{top=%d, left=%d, bottom=%d, right=%d}\n", pDestRect->top, pDestRect->left, pDestRect->bottom, pDestRect->right);
			else
				LogInfo("null\n");
			LogInfo("\tm_CurWindowHandle = 0x%x\n", m_CurWindowHandle);
			ReportErrorHR(Ret);
			
			DEBUGRETURN(deFALSE, "EndFrame: m_D3DDevice->Present() failed");
		}
	}
	
	return deTRUE;
}

deBoolean deDirect3D8::ClearFrame(deARGB Color, deBoolean ClearTarget, deBoolean ClearZBuffer, deBoolean ClearStencil)
{
	if (m_DeviceLost)
		return deTRUE;
	
	long	ClearFlags = 0;
	HRESULT hr;

	//if they want the target cleared, do it
	if(ClearTarget)
		ClearFlags = ClearFlags | D3DCLEAR_TARGET;

	//if they want the zbuffer cleared, clear it if a zbuffer exists
	if(ClearZBuffer && m_CurrentDisplay.ZDepth != 0)
		ClearFlags = ClearFlags | D3DCLEAR_ZBUFFER;

	//if they want the stencil cleared, do it if a stencil buffer exists
	if(ClearStencil && m_CurrentDisplay.StencilDepth != 0)
		ClearFlags = ClearFlags | D3DCLEAR_STENCIL;

	hr = m_D3DDevice->Clear(0, NULL, ClearFlags, Color, 1.0f, 0);
	if(hr != D3D_OK)
	{
		LogInfo("Failure while calling Clear\n");
		ReportErrorHR(hr);

		DEBUGRETURN(deFALSE, "ClearFrame: m_D3DDevice->Clear() failed");
	}

	return deTRUE;
}

deBoolean deDirect3D8::FillBufferInfo(VertexBufferEntry *BufferEntry, IdeVertexBuffer *Vertex, IdeVertexBuffer::BufferType BufferType, deBoolean WipeBuffer)
{
	//fill in info about a d3d vertex buffer

	if (m_DeviceLost)
		return deTRUE;
	
	deBoolean	SubBuffer;

	// nothing here for a sub-vbuffer to use
	SubBuffer = (Vertex->GetVBufferType() == IdeVertexBuffer::BUFFERTYPE_SUB);
	
	// this is as far as we go
	if (SubBuffer)
		return deTRUE;
	
	deBoolean	DynamicVertex;
	void *		VertexBuffer;
	long		DataSize;
	void *		Buffer;
	DWORD		Flags;
	HRESULT		hr;
	
	//grab the buffer
	if (!Vertex->BufferExists(BufferType))
	{
		//if no buffer, just return
		return deTRUE;
	}
	
	Buffer = Vertex->GetBuffer(BufferType);
	
	//first, figure out the size of the buffer element
	Vertex->GetBufferDataType(BufferType, &DataSize);
	
	//set the size property for the buffer
	BufferEntry->EntrySize[BufferType] = DataSize;

	//if any buffer existed for the vbuffer and we don't want it, wipe it
	if(WipeBuffer && BufferEntry->Buffers[BufferType])
	{
		BufferEntry->Buffers[BufferType]->Release();
		BufferEntry->Buffers[BufferType] = NULL;
	}

	DynamicVertex = Vertex->GetDynamicStatusVertex();

	//figure out the size of the vertex buffer
	DataSize *= BufferEntry->PointCount;

	if (BufferEntry->PointCount == 0)
		return deTRUE;

	//if no d3d vbuffer exists, then create one
	if(!BufferEntry->Buffers[BufferType])
	{
		if (DynamicVertex)
		{
			// can't use managed for dynamic VB's, and we don't want to either
			Flags = 0 | D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC;
			hr = m_D3DDevice->CreateVertexBuffer(DataSize, Flags, 0, D3DPOOL_DEFAULT, &BufferEntry->Buffers[BufferType]);
			if(hr != D3D_OK)
			{
				LogInfo("Failure while creating vertex buffer (unmanaged)\n");
				ReportErrorHR(hr);

				//got a prob, fail
				DEBUGRETURN(deFALSE, "FillBufferInfo: m_D3DDevice->CreateVertexBuffer() failed (dynamic)");
			}
		}
		else
		{
			Flags = 0 | D3DUSAGE_WRITEONLY;
			hr = m_D3DDevice->CreateVertexBuffer(DataSize, Flags, 0, D3DPOOL_MANAGED, &BufferEntry->Buffers[BufferType]);
			if(hr != D3D_OK)
			{
				LogInfo("Failure while creating vertex buffer (managed)\n");
				ReportErrorHR(hr);

				//got a prob, fail
				DEBUGRETURN(deFALSE, "FillBufferInfo: m_D3DDevice->CreateVertexBuffer() failed (static)");
			}
		}
	}

	if (DynamicVertex)
		Flags = D3DLOCK_DISCARD /*| D3DLOCK_NOSYSLOCK*/;
	else
		Flags = 0;
	//lock it
	hr = BufferEntry->Buffers[BufferType]->Lock(0, DataSize, (BYTE**)&VertexBuffer, Flags);
	if(hr != D3D_OK)
	{
		LogInfo("Failure while locking vertex buffer\n");
		ReportErrorHR(hr);

		//got a prob, fail
		DEBUGRETURN(deFALSE, "FillBufferInfo: Buffer->Lock() failed");
	}

	//fill it
	memcpy(VertexBuffer, Buffer, DataSize);
	
	//unlock it
	hr = BufferEntry->Buffers[BufferType]->Unlock();
	if(hr != D3D_OK)
	{
		LogInfo("Failure while unlocking vertex buffer\n");
		ReportErrorHR(hr);

		//got a prob, fail
		DEBUGRETURN(deFALSE, "FillBufferInfo: Buffer->Unlock() failed");
	}

	m_Stats.VBufferUploads++;

	return deTRUE;
}

deBoolean deDirect3D8::AddVertexBuffer(IdeVertexBuffer *Vertex)
{
	VertexBufferEntry *		VertexBufferInfo;
	long					Counter;

	//create a vertex shader and tell the vertex buffer about it

	//make sure we don't already have a shader set, if a shader exists, then fail
	if(Vertex->GetDriverBuffer(m_UniqueID, NULL) != NULL)
		return deFALSE;

	//create the vertex shader info buffer

	//create some memory for the shader
	VertexBufferInfo = (VertexBufferEntry *)malloc(sizeof(VertexBufferEntry));
	if(!VertexBufferInfo)
		return deFALSE;

	//clear it all out
	memset(VertexBufferInfo, 0, sizeof(VertexBufferEntry));

	//set some flags on the buffer
	VertexBufferInfo->ID = VERTEXSHADERID;

	//how many buffers exist minus the index buffer
	Counter = Vertex->BUFFER_COUNT - 1;
	
	//allocate memory for the buffer entries
/*	VertexBufferInfo->Buffers = (LPDIRECT3DVERTEXBUFFER8 *)malloc(Counter * sizeof(LPDIRECT3DVERTEXBUFFER8));
	if(!VertexBufferInfo->Buffers)
	{
		RemoveVertexBuffer(Vertex);
		return deFALSE;
	}

	//clear the buffers out
	memset(VertexBufferInfo->Buffers, 0, Counter * sizeof(IDirect3DVertexBuffer8));

	//allocate memory for the entry sizes
	VertexBufferInfo->EntrySize = (long *)malloc(Counter * sizeof(long));
	if(!VertexBufferInfo->EntrySize)
	{
		RemoveVertexBuffer(Vertex);
		return deFALSE;
	}

	//clear the entry size buffer out
	memset(VertexBufferInfo->EntrySize, 0, Counter * sizeof(long));
*/
	VertexBufferInfo->SubBuffer = (Vertex->GetVBufferType() == IdeVertexBuffer::BUFFERTYPE_SUB);

	//lock the buffer so we can set it
	if(!Vertex->Lock())
	{
		Vertex->Unlock();
		RemoveVertexBuffer(Vertex);
		return deFALSE;
	}

	//set the buffer in the vertex buffer class
	if (!Vertex->SetDriverBuffer(m_UniqueID, VertexBufferInfo, this))
	{
		Vertex->Unlock();
		RemoveVertexBuffer(Vertex);
		return deFALSE;
	}
	
	//unlock it so we can process
	if(!Vertex->Unlock())
	{
		RemoveVertexBuffer(Vertex);
		return deFALSE;
	}

	m_VBufferList.AddElement(Vertex, (DWORD)(Vertex));

	//process the data, pass all bits set to show a new buffer
	return ProcessVertexBuffer(Vertex, VertexBufferInfo, 0xFFFFFFFF);
}

deBoolean deDirect3D8::RemoveVertexBuffer(IdeVertexBuffer *Vertex)
{
	//remove a vertex shader

	if (!Vertex->Lock())
		return deFALSE;

	long				Counter;
	VertexBufferEntry *	VertexBufferInfo;
	deBoolean			SubBuffer;
	
	SubBuffer = (Vertex->GetVBufferType() == Vertex->BUFFERTYPE_SUB);

	//get our info
	VertexBufferInfo = (VertexBufferEntry *)Vertex->GetDriverBuffer(m_UniqueID, NULL);

	//make sure we have a shader set
	if(!VertexBufferInfo)
	{
		Vertex->Unlock();
		return deFALSE;
	}

	//remove it from the class
	if(!Vertex->SetDriverBuffer(m_UniqueID, NULL, this))
	{
		Vertex->Unlock();
		return deFALSE;
	}

	//make sure the ID matches, if not, we won't try releasing it as who
	//knows what it is
	if(VertexBufferInfo->ID != VERTEXSHADERID)
	{
		Vertex->Unlock();
		return deFALSE;
	}

	//if any buffers exist, free them
	if(VertexBufferInfo->Buffers)
	{
		if (!SubBuffer)
		{
			//release all vertex buffers (don't count the index buffer)
			for(Counter = 0; Counter < IdeVertexBuffer::BUFFER_COUNT - 1; Counter++)
			{
				//if the buffer exists, free it
				if(VertexBufferInfo->Buffers[Counter])
				{
					VertexBufferInfo->Buffers[Counter]->Release();
					VertexBufferInfo->Buffers[Counter] = NULL;
				}
			}
		}

		//free the buffer memory
//		free(VertexBufferInfo->Buffers);
	}

	//if the index buffer exists, free it
	if(!SubBuffer && VertexBufferInfo->IndexBuffer)
	{
		VertexBufferInfo->IndexBuffer->Release();
		VertexBufferInfo->IndexBuffer = NULL;
	}

	//free some memory if it is allocated
/*	if(VertexBufferInfo->EntrySize)
		free(VertexBufferInfo->EntrySize);
*/

	//free any memory it held
	free(VertexBufferInfo);

	if (!Vertex->Unlock())
		return deFALSE;

	m_VBufferList.RemoveElement((DWORD)(Vertex));

	return deTRUE;
}

deBoolean deDirect3D8::ProcessVertexBuffer(IdeVertexBuffer *VBuffer, VertexBufferEntry *VertexBufferInfo, long BufferFlags)
{
	//see if we can lock the buffer
	if(!VBuffer->Lock())
		return deFALSE;

	//if no info exists, someone is being an idiot so do an add instead
	if(!VertexBufferInfo)
	{
		DEBUGRETURN(deFALSE, "ProcessVertexBuffer: Cannot process buffer with no info");
	}

	// initialize variables after initial fail-checks
	IdeVertexBuffer*			StreamBuffer;
	deBoolean					DynamicIndex;
	deBoolean					is32;
	deBoolean					WipeBuffers;
	deBoolean					SubVB, SubIB;
	IdeVertexBuffer::BufferType	Counter;
	long						IndexCount;
	void *						Buffer;
	void *						D3DIndexBuffer;
	HRESULT						hr;

#pragma todo (test & debug the separate index streams)

	StreamBuffer = VBuffer->GetBoundDataBuffer();
	if (!StreamBuffer)
		StreamBuffer = VBuffer;
	else
	{
		if (!UpdateVertexBuffer(StreamBuffer))
			goto process_fail;
		if (!StreamBuffer->Lock())
			goto process_fail;
	}

	//see if we have to wipe the buffers out. If the point count differs then the buffers
	//are not the correct size
	WipeBuffers = ((StreamBuffer->GetVertexCount() == VertexBufferInfo->PointCount) ? deFALSE : deTRUE);

	SubVB = (StreamBuffer->GetVBufferType() == StreamBuffer->BUFFERTYPE_SUB);
	SubIB = (VBuffer->GetVBufferType() == VBuffer->BUFFERTYPE_SUB);

	//set the point count and tri count for the vertex buffer incase they changed
	VertexBufferInfo->PointCount = StreamBuffer->GetVertexCount();
	VertexBufferInfo->TriCount = VBuffer->GetTriCount();

	if (SubVB)
		VertexBufferInfo->PointStart = StreamBuffer->GetVertexStart();
	else
		VertexBufferInfo->PointStart = 0;

	if (SubIB)
		VertexBufferInfo->IndexStart = VBuffer->GetIndexStart();
	else
		VertexBufferInfo->IndexStart = 0;

	//set the type of rendering incase it changed
	switch(VBuffer->GetRenderType())
	{
		case VBuffer->RENDER_POINTLIST:
			VertexBufferInfo->RenderType = D3DPT_POINTLIST;
			break;

		case VBuffer->RENDER_LINELIST:
			VertexBufferInfo->RenderType = D3DPT_LINELIST;
			break;

		case VBuffer->RENDER_LINESTRIP:
			VertexBufferInfo->RenderType = D3DPT_LINESTRIP;
			break;

		case VBuffer->RENDER_TRIANGLELIST:
			VertexBufferInfo->RenderType = D3DPT_TRIANGLELIST;
			break;

		case VBuffer->RENDER_TRIANGLESTRIP:
			VertexBufferInfo->RenderType = D3DPT_TRIANGLESTRIP;
			break;

		case VBuffer->RENDER_TRIANGLEFAN:
			VertexBufferInfo->RenderType = D3DPT_TRIANGLEFAN;
			break;

		default:
			RemoveVertexBuffer(VBuffer);
			return deFALSE;
	}

#pragma todo (verify that this optimization for sub-vbs works)
	if (BufferFlags)
	{
		if (SubVB)
		{
			IdeVertexBuffer * Parent = StreamBuffer->GetParentBuffer();
			VertexBufferEntry *	ParentBufferInfo;
			if (!Parent)
				goto process_fail;
			UpdateVertexBuffer(Parent);
			if (!Parent->Lock())
				goto process_fail;
			ParentBufferInfo = (VertexBufferEntry*)Parent->GetDriverBuffer(m_UniqueID, NULL);
			Parent->Unlock();
			if (!ParentBufferInfo)
				goto process_fail;
			
			for(Counter = StreamBuffer->BUFFER_POSITION; Counter < StreamBuffer->BUFFER_INDICES16; ((int&)Counter)++)
			{
				StreamBuffer->GetBufferDataType(Counter, &(VertexBufferInfo->EntrySize[Counter]));
				VertexBufferInfo->Buffers[Counter] = ParentBufferInfo->Buffers[Counter];
			}
		}
		else if (BufferFlags)
		{
			//go thru the buffers that have changed and update them
			for(Counter = StreamBuffer->BUFFER_POSITION; Counter < StreamBuffer->BUFFER_INDICES16; ((int&)Counter)++)
			{
				//if a buffer is changed, update it
				if(BufferFlags & (1 << Counter))
				{
					//got a changed buffer, pull it and update it
					if(!FillBufferInfo(VertexBufferInfo, StreamBuffer, Counter, WipeBuffers))
					{
						RemoveVertexBuffer(StreamBuffer);
						return deFALSE;
					}
				}
			}
		}

		if(VBuffer->BufferExists(VBuffer->BUFFER_INDICES16) || VBuffer->BufferExists(VBuffer->BUFFER_INDICES32))
		{
			if (SubIB)
			{
				IdeVertexBuffer * Parent = VBuffer->GetParentBuffer();
				IdeVertexBuffer * ParentDataBuffer = Parent->GetBoundDataBuffer();
				if (ParentDataBuffer)
					VertexBufferInfo->PointCount = ParentDataBuffer->GetVertexCount();
				VertexBufferEntry *	ParentBufferInfo;
				if (!Parent)
					goto process_fail;
				UpdateVertexBuffer(Parent);
				if (!Parent->Lock())
					goto process_fail;
				ParentBufferInfo = (VertexBufferEntry*)Parent->GetDriverBuffer(m_UniqueID, NULL);
				Parent->Unlock();
				if (!ParentBufferInfo)
					goto process_fail;
				
				VertexBufferInfo->IndexBuffer = ParentBufferInfo->IndexBuffer;
				VertexBufferInfo->IndexCount = VBuffer->GetIndexCount();
			}
			else if (BufferFlags & (1 << VBuffer->BUFFER_INDICES16 | 1 << VBuffer->BUFFER_INDICES32))
			{
				is32 = VBuffer->BufferExists(VBuffer->BUFFER_INDICES32);
				DynamicIndex = VBuffer->GetDynamicStatusIndex();
				//if the index count differs, destroy the old buffer if it exists
				IndexCount = VBuffer->GetIndexCount();
				if(IndexCount != VertexBufferInfo->IndexCount && VertexBufferInfo->IndexBuffer)
				{
					VertexBufferInfo->IndexBuffer->Release();
					VertexBufferInfo->IndexBuffer = NULL;
				}

				//grab the buffer
				if (is32)
					Buffer = VBuffer->GetBuffer(VBuffer->BUFFER_INDICES32);
				else
					Buffer = VBuffer->GetBuffer(VBuffer->BUFFER_INDICES16);
					
				//if a buffer, update it, otherwise, wipe it out
				if(Buffer)
				{
					//got a buffer, update it
					DWORD Flags;

					//set the number of index entries
					VertexBufferInfo->IndexCount = IndexCount;

					//set the counter to the size of the buffer
					if (is32)
						IndexCount = IndexCount * sizeof(int);
					else
						IndexCount = IndexCount * sizeof(short);

					//create the buffer if it doesn't exist
					if(!VertexBufferInfo->IndexBuffer)
					{
						D3DFORMAT Flags2;
						if (is32)
							Flags2 = D3DFMT_INDEX32;
						else
							Flags2 = D3DFMT_INDEX16;

						Flags = 0 | D3DUSAGE_WRITEONLY;
						if (DynamicIndex)
						{
							Flags |= D3DUSAGE_DYNAMIC;
							//create an unmanaged index buffer
							hr = m_D3DDevice->CreateIndexBuffer(IndexCount, Flags, Flags2, D3DPOOL_DEFAULT, &(VertexBufferInfo->IndexBuffer));
							if(hr != D3D_OK)
							{
								LogInfo("Failure while creating index buffer (unmanaged)\n");
								ReportErrorHR(hr);

								RemoveVertexBuffer(VBuffer);
								return deFALSE;
							}
						}
						else
						{
							//create a managed index buffer
							hr = m_D3DDevice->CreateIndexBuffer(IndexCount, Flags, Flags2, D3DPOOL_MANAGED, &(VertexBufferInfo->IndexBuffer));
							if(hr != D3D_OK)
							{
								LogInfo("Failure while creating index buffer (managed)\n");
								ReportErrorHR(hr);

								RemoveVertexBuffer(VBuffer);
								return deFALSE;
							}
						}
					}

					if (DynamicIndex)
						Flags = D3DLOCK_DISCARD /*| D3DLOCK_NOSYSLOCK*/;
					else
						Flags = 0;
					//lock the index buffer
					hr = (VertexBufferInfo->IndexBuffer)->Lock(0, IndexCount, (BYTE **)&D3DIndexBuffer, Flags);
					if(hr != D3D_OK)
					{
						LogInfo("Failure while locking index buffer\n");
						ReportErrorHR(hr);

						RemoveVertexBuffer(VBuffer);
						return deFALSE;
					}

					//copy it
					memcpy(D3DIndexBuffer, Buffer, IndexCount);

					//unlock the index buffer
					hr = VertexBufferInfo->IndexBuffer->Unlock();
					if(hr != D3D_OK)
					{
						LogInfo("Failure while unlocking index buffer\n");
						ReportErrorHR(hr);

						RemoveVertexBuffer(VBuffer);
						return deFALSE;
					}
				}
				else
				{
					//no index buffer, make sure non exists
					if(VertexBufferInfo->IndexBuffer)
					{
						VertexBufferInfo->IndexBuffer->Release();
						VertexBufferInfo->IndexBuffer = NULL;
						VertexBufferInfo->IndexCount = 0;
					}
				}
			}
		}
	}

	if (VertexBufferInfo->FrameUsed != m_FrameNum)
	{
		VertexBufferInfo->FrameUsed = m_FrameNum;
		if (!SubVB)
		{
			if (!VBuffer->GetDynamicStatusVertex())
			{
				for(Counter = IdeVertexBuffer::BUFFER_POSITION; Counter < IdeVertexBuffer::BUFFER_INDICES16; ((int&)Counter)++)
				{
					if (VertexBufferInfo->Buffers[Counter])
						VertexBufferInfo->Buffers[Counter]->PreLoad();
					m_Stats.BytesUsed += VertexBufferInfo->EntrySize[Counter]*VertexBufferInfo->PointCount;
				}
			}
		}
		if (!SubIB)
		{
			if (VertexBufferInfo->IndexBuffer)
			{
				if (!VBuffer->GetDynamicStatusIndex())
					VertexBufferInfo->IndexBuffer->PreLoad();
			}
		}
	}

	//tell the vertex buffer that we updated the buffers
	if(!VBuffer->SetDriverBuffer(m_UniqueID, VertexBufferInfo, this))
		goto process_fail;

	//unlock the vertex buffer
	if(!VBuffer->Unlock())
		return deFALSE;
	if (StreamBuffer != VBuffer)
		if (!StreamBuffer->Unlock())
			return deFALSE;

	return deTRUE;

process_fail:
	VBuffer->Unlock();
	if (StreamBuffer != VBuffer)
		StreamBuffer->Unlock();
	RemoveVertexBuffer(VBuffer);
	return deFALSE;
}

deBoolean deDirect3D8::UpdateVertexBuffer(IdeVertexBuffer *VBuffer)
{
	if (m_DeviceLost)
		return deTRUE;

	VertexBufferEntry *		VertexBufferInfo;
	long					BufferFlags;

	//update a vertex shader and tell the vertex buffer about it

	//grab our info
	VertexBufferInfo = (VertexBufferEntry *)VBuffer->GetDriverBuffer(m_UniqueID, &BufferFlags);

	//if now vertex shader info, call the add
	if(!VertexBufferInfo)
		return AddVertexBuffer(VBuffer);

	//got an entry, verify that it is valid
	if(VertexBufferInfo->ID != VERTEXSHADERID)
		return deFALSE;

	//call the function to process it
	return ProcessVertexBuffer(VBuffer, VertexBufferInfo, BufferFlags);
}

deBoolean deDirect3D8::Render(IdeVertexBuffer *VBuffer, long IndexOffset, IdeVertexBuffer *IndexOverride)
{
	if (m_DeviceLost)
		return deTRUE;
	
	IdeVertexBuffer::BufferType	Counter;
	VertexBufferEntry *			VertexBufferInfo;
	VertexBufferEntry *			IndexBufferInfo;
	VertexShaderEntry*			VertexShader;
	HRESULT						Result;

	//get the shader info
	VertexBufferInfo = (VertexBufferEntry *)VBuffer->GetDriverBuffer(m_UniqueID, NULL);
	if (IndexOverride)
	{
		IndexBufferInfo = (VertexBufferEntry *)IndexOverride->GetDriverBuffer(m_UniqueID, NULL);
		if (IndexBufferInfo == NULL)
			DEBUGRETURN(deFALSE, "Render: IndexOverride has no d3d info");
	}
	else
		IndexBufferInfo = NULL;

	//if no buffer, return
	if (!VertexBufferInfo)
		DEBUGRETURN(deFALSE, "Render: VBuffer has no d3d info");

	//make sure it is a valid shader
	if (VertexBufferInfo->ID != VERTEXSHADERID)
		return deFALSE;
	if (IndexBufferInfo && IndexBufferInfo->ID != VERTEXSHADERID)
		return deFALSE;

	VertexShader = GetFixedFunctionShader(VBuffer);

	if (!VertexShader)
		return deFALSE;

	//set the vertex shader
	if (VertexShader->VertexShader != m_LastShader)
	{
		Result = m_D3DDevice->SetVertexShader(VertexShader->VertexShader);
		if(Result != D3D_OK)
		{
			LogInfo("Failure while setting vertex shader\n");
			ReportErrorHR(Result);

			return deFALSE;
		}
		m_LastShader = VertexShader->VertexShader;
	}

	//setup all the stream sources
	D3DVERTEXBUFFER_DESC Desc;
	for(Counter=IdeVertexBuffer::BUFFER_POSITION; Counter < IdeVertexBuffer::BUFFER_INDICES16; ((int&)Counter)++)
	{
		if (s_DevCaps[m_AdapterNum].MaxStreams > (DWORD)Counter || s_DevCaps[m_AdapterNum].MaxStreams == 0)
		{
			//if a stream exists, set it
			if(VertexBufferInfo->Buffers[Counter])
			{
				VertexBufferInfo->Buffers[Counter]->GetDesc(&Desc);
				//setup a stream based on saved info
				Result = m_D3DDevice->SetStreamSource(Counter, VertexBufferInfo->Buffers[Counter], VertexBufferInfo->EntrySize[Counter]);
				if(Result != D3D_OK)
				{
					LogInfo("Failure while calling SetStreamSource(%d)\n", Counter);
					ReportErrorHR(Result);
					
					return deFALSE;
				}
			}
			else
			{
				Result = m_D3DDevice->SetStreamSource(Counter, NULL, VertexBufferInfo->EntrySize[Counter]);
			}
		}
	}

	//if we have an index buffer, render with it, otherwise go without it
	if(VertexBufferInfo->IndexBuffer || IndexBufferInfo)
	{
		int	TriCount, IndexStart, VertexStart;
		IDirect3DIndexBuffer8* IndexBuffer = NULL;
		D3DPRIMITIVETYPE PrimType;
		if (IndexBufferInfo)
		{
			TriCount = IndexBufferInfo->TriCount;
			IndexStart = IndexBufferInfo->IndexStart;
			PrimType = IndexBufferInfo->RenderType;
			IndexBuffer = IndexBufferInfo->IndexBuffer;
		}
		else
		{
			TriCount = VertexBufferInfo->TriCount;
			IndexStart = VertexBufferInfo->IndexStart;
			PrimType = VertexBufferInfo->RenderType;
			IndexBuffer = VertexBufferInfo->IndexBuffer;
		}
		VertexStart = VertexBufferInfo->PointStart;
		if (VertexStart < 0)
			VertexStart = 0;
		VertexStart += IndexOffset;
		//got an index buffer, use it
		//we use the PointStart as the offset so that sub-VBs work correctly
		Result = m_D3DDevice->SetIndices(IndexBuffer, VertexStart);
		if(Result != D3D_OK)
		{
			LogInfo("Failure while calling SetIndices\n");
			ReportErrorHR(Result);

			return deFALSE;
		}

		if (TriCount > 65535)
			TriCount = 65535;

		//if (VertexBufferInfo->PointCount < 100)
		{
			Result = m_D3DDevice->DrawIndexedPrimitive(PrimType, 0 /*VertexBufferInfo->PointStart*/, 
				VertexBufferInfo->PointCount, IndexStart, TriCount);
			if(Result != D3D_OK)
			{
				LogInfo("Failure while calling DrawIndexedPrimitive\n");
				ReportErrorHR(Result);

				return deFALSE;
			}
		}
	}
	else
	{
		Result = m_D3DDevice->DrawPrimitive(VertexBufferInfo->RenderType, VertexBufferInfo->PointStart, 
			VertexBufferInfo->TriCount);
		if(Result != D3D_OK)
		{
			LogInfo("Failure while calling DrawPrimitive\n");
			ReportErrorHR(Result);

			return deFALSE;
		}
	}

	return deTRUE;
}

IdeDriver::Types deDirect3D8::GetType() const
{
	//let people know our type
	return IdeDriver::DRIVER_DIRECT3D8;
}

deBoolean deDirect3D8::GetCurrentDisplay(long Adapter, deDisplay * Display) const
{
	HRESULT hr;
	D3DDISPLAYMODE	DisplayMode;

	hr = s_D3D->GetAdapterDisplayMode(Adapter, &DisplayMode);
	if(hr != D3D_OK)
		return deFALSE;

	memset(Display, 0, sizeof(deDisplay));

	Display->Adapter = Adapter;
	Display->Width = DisplayMode.Width;
	Display->Height = DisplayMode.Height;
	Display->RefreshRate = DisplayMode.RefreshRate;
	Display->BackBpp = FromD3DBPP(DisplayMode.Format);
	Display->FrontBpp = Display->BackBpp;
	Display->MonitorHandle = s_D3D->GetAdapterMonitor(Adapter);

	return deTRUE;
}

deBoolean deDirect3D8::GetDisplayMode(long Adapter, long Mode, IdeDriver::deDisplay *Display) const
{
	D3DDISPLAYMODE		DisplayMode;
	HRESULT				hr;

	//an issue here is the Direct3D will return the same width and height for multiple
	//display and refresh values. May be extra flags to set or return...
	hr = s_D3D->EnumAdapterModes(Adapter, Mode, &DisplayMode);
	if(hr != D3D_OK)
		return deFALSE;

	Display->Adapter = Adapter;							//set what adapter
	Display->Width = DisplayMode.Width;					//mode width
	Display->Height = DisplayMode.Height;				//mode height
	Display->RefreshRate = DisplayMode.RefreshRate;		//refresh rate for this mode
	Display->FrontBpp = FromD3DBPP(DisplayMode.Format);	//format of the front-buffer
	Display->Windowed = deFALSE;						//default to full screen
	return deTRUE;
}

static inline deBoolean IsPow2(int num)
{
	return (((num << 1) - 1) & num) == num;
}

deBoolean deDirect3D8::AddBitmapProxy(IdeBitmapProxy *BitmapProxy)
{
	IdeBitmap*	bmp;
	if (!BitmapProxy)
		return deFALSE;
	bmp = BitmapProxy->GetBitmap();
	return AddBitmap(bmp);
}

#pragma todo (add another method like this for creating cubemaps)
deBoolean deDirect3D8::AddBitmap(IdeBitmap *Bitmap)
{
	if (!Bitmap)
		return deFALSE;

	TextureEntry *		NewTexture;
	deBoolean			RenderTarget;
	long				MipLevels;

	MipLevels = Bitmap->GetMipLevels();
	RenderTarget = Bitmap->IsRenderTarget();

	NewTexture = (TextureEntry *)Bitmap->GetDriverData(m_UniqueID);
	if (NewTexture)
	{
		if (NewTexture->ID == TEXTURESTRUCTID)
		{
			if (RenderTarget && NewTexture->DepthStencil)
				return deTRUE;
			if (!RenderTarget && !NewTexture->DepthStencil)
				return deTRUE;
		}
		free(NewTexture);
		Bitmap->SetDriverData(m_UniqueID, NULL, this);
	}
	
	LPDIRECT3DTEXTURE8	AddedTex = NULL;
	LPDIRECT3DCUBETEXTURE8	AddedCubeTex = NULL;
	D3DLOCKED_RECT		LockRect;
	DWORD				Flags;
	int					BitmapWidth;
	int					BitmapHeight;
	D3DFORMAT			D3D_Format;
	deBoolean			IsDXTC;
	HRESULT				Ret;
	BPP					TexFormat;
	deBoolean			ManglingAllowed;
	deBoolean			InvalidAllowed;
	deBoolean			IsCubeMap;
	
	//add a texture for rendering

	if(!m_D3DDevice)
		return deFALSE;

	TexFormat = Bitmap->GetFormat();
	ManglingAllowed = (deBoolean)(GetQualityFlag(QUALITY_ALLOW_TEXTURE_MANGLING));
	InvalidAllowed = (deBoolean)(GetQualityFlag(QUALITY_ALLOW_INVALID_TEXTURES));

	//verify that the current display allows it
	while(!ValidFormat(m_CurrentDisplay.Adapter, RESOURCE_TEXTURE, TexFormat, ZPP_UNKNOWN, BPP_UNKNOWN, 0))
	{
		if (ManglingAllowed && IdeDriver_BPPBitDepth(TexFormat) > 0)
		{
			// keep "downsampling" until you get a format that works or you've exhausted the formats
			TexFormat = DownSampleFormat(TexFormat);
		}
		else
		{
			int i = (int)(Bitmap->GetFormat());
			DEBUGMSG1("Unsupported bitmap format: %d", i);
			DEBUGRETURN(deFALSE, "AddBitmap: Invalid texture format");
		}
	}

	//allocate some memory for it
	NewTexture = (TextureEntry *)malloc(sizeof(TextureEntry));
	if(!NewTexture)
		return deFALSE;
	memset(NewTexture, 0, sizeof(TextureEntry));

	//set it's ID
	NewTexture->ID = TEXTURESTRUCTID;
	// Face ID is < 0 for a single-faced (normal) texture (it's >= 0 for cubemaps and stuff)
	NewTexture->NumFaces = Bitmap->GetNumFaces();
	if (NewTexture->NumFaces == 6)
		IsCubeMap = deTRUE;
	else
		IsCubeMap = deFALSE;

	if (MipLevels < -1)
		return deFALSE;
	
	Flags = 0;
	if (RenderTarget)
	{
		while (!ValidFormat(m_CurrentDisplay.Adapter, RESOURCE_RENDERTARGET, TexFormat, ZPP_UNKNOWN, BPP_UNKNOWN, 0))
		{
			if (ManglingAllowed && IdeDriver_BPPBitDepth(TexFormat) > 0)
			{
				// keep "downsampling" until you get a format that works or you've exhausted the formats
				TexFormat = DownSampleFormat(TexFormat);
			}
			else
			{
				int i = (int)(Bitmap->GetFormat());
				DEBUGMSG1("Unsupported render-target bitmap format: %d", i);
				DEBUGRETURN(deFALSE, "AddBitmap: Invalid texture format");
			}
		}
		Flags |= D3DUSAGE_RENDERTARGET;
	}

	BitmapWidth = Bitmap->GetWidth();
	BitmapHeight = Bitmap->GetHeight();
	D3D_Format = ToD3DBPP(TexFormat);
	if (D3D_Format == D3DFMT_DXT1 ||
			D3D_Format == D3DFMT_DXT2 ||
			D3D_Format == D3DFMT_DXT3 ||
			D3D_Format == D3DFMT_DXT4 ||
			D3D_Format == D3DFMT_DXT5)
		IsDXTC = deTRUE;
	else
		IsDXTC = deFALSE;

	if (ManglingAllowed && BitmapWidth > s_Caps[m_CurrentDisplay.Adapter].TexWidth)
	{
		BitmapWidth = s_Caps[m_CurrentDisplay.Adapter].TexWidth;
	}
	if (ManglingAllowed && BitmapHeight > s_Caps[m_CurrentDisplay.Adapter].TexHeight)
	{
		BitmapHeight = s_Caps[m_CurrentDisplay.Adapter].TexHeight;
	}
	
	// create the texture and add it to the d3d device
	if (Flags == 0)
	{
		while (!ValidFormat(m_CurrentDisplay.Adapter, RESOURCE_TEXTURE, TexFormat, ZPP_UNKNOWN, BPP_UNKNOWN, 0))
		{
			if (ManglingAllowed && IdeDriver_BPPBitDepth(TexFormat) > 0)
			{
				// keep "downsampling" until you get a format that works or you've exhausted the formats
				TexFormat = DownSampleFormat(TexFormat);
				D3D_Format = ToD3DBPP(TexFormat);
				if (D3D_Format == D3DFMT_DXT1 ||
						D3D_Format == D3DFMT_DXT2 ||
						D3D_Format == D3DFMT_DXT3 ||
						D3D_Format == D3DFMT_DXT4 ||
						D3D_Format == D3DFMT_DXT5)
					IsDXTC = deTRUE;
				else
					IsDXTC = deFALSE;
			}
			else
			{
				int i = (int)(Bitmap->GetFormat());
				DEBUGMSG1("Unsupported texture format: %d", i);
				DEBUGRETURN(deFALSE, "AddBitmap: Invalid texture format");
			}
		}
		if (IsDXTC)
		{
			DWORD ImageSize = Bitmap->GetImageSize();
			int ByteDepth = Bitmap->GetByteDepth();
			if (ImageSize <= (DWORD)(ByteDepth*BitmapWidth*BitmapHeight)/4)
			{
				if (MipLevels != 0)
				{
					DEBUGMSG1("Could not create requested mip levels on DXTC texture: %d", MipLevels);
					MipLevels = 0;
				}
			}
		}
		if (!IsCubeMap)
		{
			Ret = m_D3DDevice->CreateTexture(BitmapWidth, BitmapHeight, MipLevels+1, 0, D3D_Format, D3DPOOL_MANAGED, &AddedTex);
			NewTexture->Texture = AddedTex;
		}
		else
		{
			Ret = m_D3DDevice->CreateCubeTexture(BitmapWidth, MipLevels+1, 0, D3D_Format, D3DPOOL_MANAGED, &AddedCubeTex);
			NewTexture->Texture = AddedCubeTex;
		}
		if(D3D_OK != Ret)
		{
			LogInfo("Failure while creating texture '%s', w=%d, h=%d\n", Bitmap->GetFilename(), BitmapWidth, BitmapHeight);
			ReportErrorHR(Ret);
			
			DEBUGMSG1("While loading texture with name '%s'", Bitmap->GetFilename());
			if (!IsPow2(BitmapWidth))
				DEBUGMSG1("Erroneous texture width: %d", BitmapWidth);
			if (!IsPow2(BitmapHeight))
				DEBUGMSG1("Erroneous texture height: %d", BitmapHeight);
			DEBUGMSG1("Create[Cube]Texture return value: 0x%x", Ret);
			if (!InvalidAllowed)
			{
				free(NewTexture);
				DEBUGRETURN(deFALSE, "AddBitmap: Create[Cube]Texture failed");
			}
			else if (InvalidAllowed)
			{
				BitmapWidth = deTArray<IdeBitmap**>::SmallestLargerPow2(BitmapWidth)/2;
				BitmapHeight = deTArray<IdeBitmap**>::SmallestLargerPow2(BitmapHeight)/2;
				if (!IsCubeMap)
				{
					Ret = m_D3DDevice->CreateTexture(BitmapWidth, BitmapHeight, MipLevels+1, 0, D3D_Format, D3DPOOL_MANAGED, &AddedTex);
					NewTexture->Texture = AddedTex;
				}
				else
				{
					Ret = m_D3DDevice->CreateCubeTexture(BitmapWidth, MipLevels+1, 0, D3D_Format, D3DPOOL_MANAGED, &AddedCubeTex);
					NewTexture->Texture = AddedCubeTex;
				}
				if(D3D_OK != Ret)
				{
					LogInfo("Failure while creating texture '%s', w=%d, h=%d\n", Bitmap->GetFilename(), BitmapWidth, BitmapHeight);
					ReportErrorHR(Ret);
					free(NewTexture);
					DEBUGRETURN(deFALSE, "AddBitmap: Create[Cube]Texture failed");
				}
			}
			else
			{
				NewTexture->DepthStencil = NULL;
				NewTexture->Texture = NULL;
				goto Invalid_End;
			}
		}
	}
	else
	{
		// only 1 mip-level if we have a render-target... kinda bad performance-wise
		// recommended that you use point-sampling on a render-target texture
		if (!IsCubeMap)
		{
			Ret = m_D3DDevice->CreateTexture(BitmapWidth, BitmapHeight, 1, Flags, D3D_Format, D3DPOOL_DEFAULT, &AddedTex);
			NewTexture->Texture = AddedTex;
		}
		else
		{
			Ret = m_D3DDevice->CreateCubeTexture(BitmapWidth, 1, Flags, D3D_Format, D3DPOOL_DEFAULT, &AddedCubeTex);
			NewTexture->Texture = AddedCubeTex;
		}
		if(D3D_OK != Ret)
		{
			LogInfo("Failure while creating texture (render-target), w=%d, h=%d\n", BitmapWidth, BitmapHeight);
			ReportErrorHR(Ret);

			free(NewTexture);
			DEBUGMSG0("While creating render-target texture");
			if (!IsPow2(BitmapWidth))
				DEBUGMSG1("Erroneous texture width: %d", BitmapWidth);
			if (!IsPow2(BitmapHeight))
				DEBUGMSG1("Erroneous texture height: %d", BitmapHeight);
			DEBUGMSG1("Erroneous return value: 0x%x", Ret);
			DEBUGRETURN(deFALSE, "AddBitmap: CreateTexture failed");
		}
	}

	if (RenderTarget)
	{
		// now we gotta make ourselves a z-buffer
		LPDIRECT3DSURFACE8 Surface;
		ZPP Zpp;
		int ColorDepth;
		ColorDepth = BitDepth(Bitmap->GetFormat());
		//if (Stencil)
		{
			if (ColorDepth == 16)
				Zpp = ZPP_D15S1;
			else // if(ColorDepth == 32)
				Zpp = ZPP_D24S8;
		}
		
		Ret = m_D3DDevice->CreateDepthStencilSurface(BitmapWidth, BitmapHeight, ToD3DZPP(Zpp), D3DMULTISAMPLE_NONE, &Surface);
		NewTexture->DepthStencil = Surface;
		if (Ret != D3D_OK || !Surface)
		{
			LogInfo("Failure while creating depth-stencil surface, w=%d, h=%d\n", BitmapWidth, BitmapHeight);
			ReportErrorHR(Ret);

			if (!IsCubeMap)
				AddedTex->Release();
			else
				AddedCubeTex->Release();
			free(NewTexture);
			return deFALSE;
		}
	}
	else
	{
		int FaceIndex;
		for (FaceIndex = 0; FaceIndex < NewTexture->NumFaces; ++FaceIndex)
		{
			D3DSURFACE_DESC		SurfaceDesc;
			DWORD				NumLevels;
			DWORD				Counter;

			int					SourceWidth, SourceHeight, SourcePitch;
			int					TargetWidth, TargetHeight, TargetPitch;
			BYTE				*Target, *Source;
			
			if (!IsCubeMap)
				NumLevels = AddedTex->GetLevelCount();
			else
				NumLevels = AddedCubeTex->GetLevelCount();

			if (IsDXTC)
			{
				BitmapHeight /= 4;
			}

			Source = (BYTE*)(Bitmap->GetData(FaceIndex));
			SourceWidth = Bitmap->GetWidth();
			SourceHeight = Bitmap->GetHeight();
			SourcePitch = SourceWidth * Bitmap->GetByteDepth();

			for (Counter = 0; Counter < NumLevels; ++Counter)
			{
				if (!IsCubeMap)
					AddedTex->GetLevelDesc(Counter, &SurfaceDesc);
				else
					AddedCubeTex->GetLevelDesc(Counter, &SurfaceDesc);
				//lock the d3d buffer for the texture
				if (!IsCubeMap)
					Ret = AddedTex->LockRect(Counter, &LockRect, NULL, 0);
				else
					Ret = AddedCubeTex->LockRect((D3DCUBEMAP_FACES)FaceIndex, Counter, &LockRect, NULL, 0);
				if(D3D_OK != Ret)
				{
					LogInfo("Failure while locking texture surface\n");
					ReportErrorHR(Ret);

					//got a prob, cleanup and fail
					if (!IsCubeMap)
						AddedTex->Release();
					else
						AddedCubeTex->Release();
					free(NewTexture);
					return deFALSE;
				}

				//got it locked, do a copy

				Target = (BYTE*)(LockRect.pBits);
				TargetPitch = LockRect.Pitch;
				TargetWidth = SurfaceDesc.Width;
				TargetHeight = SurfaceDesc.Height;
				
				if (Counter == 0)
				{
					deBoolean IsValid = deTRUE;
					if (TexFormat != Bitmap->GetFormat())
						IsValid = deFALSE;
					if (BitmapWidth != Bitmap->GetWidth())
						IsValid = deFALSE;
					if (BitmapHeight != Bitmap->GetHeight() && (BitmapHeight/4 == Bitmap->GetHeight() && (TexFormat < IdeDriver::BPP_DXT1 || TexFormat > IdeDriver::BPP_DXT5)))
						IsValid = deFALSE;

					if (IsValid)
					{
						// on the first level, we can just do a block transfer
						if (SourcePitch == TargetPitch)
						{
							// do a full copy of if there's no extra information
							memcpy(Target, Source, BitmapHeight*SourcePitch);
						}
						else
						{
							int i;
							for (i = 0; i < BitmapHeight; ++i)
							{
								memcpy(&(Target[i*TargetPitch]), &(Source[i*SourcePitch]), SourcePitch);
							}					
						}
					}
					else
					{
						int i = (int)(Bitmap->GetFormat());
						DEBUGMSG2("AddBitmap: Texture has been mangled from original format, was: %i, now: %i", i, TexFormat);
						void * voidsource = Source;
						Bitmap->DownSampleMIP(voidsource, Target, 
							SourceWidth, SourceHeight, SourcePitch, 
							TargetWidth, TargetHeight, TargetPitch, 
							Bitmap->GetFormat(), TexFormat);
						Source = (BYTE*)voidsource;
					}
				}
				else
				{
					// use the utility function in deBitmap to downsample to MIP levels
					void * voidsource = Source;
					Bitmap->DownSampleMIP(voidsource, Target, 
						SourceWidth, SourceHeight, SourcePitch, 
						TargetWidth, TargetHeight, TargetPitch, 
						TexFormat, TexFormat);
					Source = (BYTE*)voidsource;
					
					//unlock previous level, since we are done copying from it
					if (!IsCubeMap)
						Ret = AddedTex->UnlockRect(Counter-1);
					else
						Ret = AddedCubeTex->UnlockRect((D3DCUBEMAP_FACES)FaceIndex, Counter-1);
					if(Ret != D3D_OK)
					{
						LogInfo("Failure while unlocking texture surface\n");
						ReportErrorHR(Ret);

						//something failed, cleanup
						if (!IsCubeMap)
							AddedTex->Release();
						else
							AddedCubeTex->Release();
						free(NewTexture);
						return deFALSE;
					}
				}

				// set params for next level
				if (!IsDXTC)
					Source = Target;
				SourceWidth = TargetWidth;
				SourceHeight = TargetHeight;
				SourcePitch = TargetPitch;
			}

			//unlock last level
			if (!IsCubeMap)
				Ret = AddedTex->UnlockRect(Counter-1);
			else
				Ret = AddedCubeTex->UnlockRect((D3DCUBEMAP_FACES)FaceIndex, Counter-1);
			if(Ret != D3D_OK)
			{
				LogInfo("Failure while unlocking texture surface\n");
				ReportErrorHR(Ret);

				//something failed, cleanup
				if (!IsCubeMap)
					AddedTex->Release();
				else
					AddedCubeTex->Release();
				free(NewTexture);
				return deFALSE;
			}
			
			m_Stats.TextureUploads++;
		}
	}

	if (Flags)
		NewTexture->Texture->PreLoad();

Invalid_End:
	Bitmap->SetDriverData(m_UniqueID, NewTexture, this);

	m_TexturesList.AddElement(Bitmap, (DWORD)Bitmap);

	//texture is added, return success
	return deTRUE;
}

deBoolean deDirect3D8::RemoveBitmap(IdeBitmap *Bitmap)
{
	TextureEntry *	Entry;
	deBoolean RetVal = deTRUE;
	
	//remove a texture from the d3d device

	//make sure the pointer is valid
	if(!Bitmap)
		return deFALSE;

	Entry = (TextureEntry *)Bitmap->GetDriverData(m_UniqueID);
	if (!Entry)
		return deFALSE;

	//make sure it is a valid struct
	if(Entry->ID != TEXTURESTRUCTID)
		return deFALSE;
	
	//release the texture
	if(Entry->Texture->Release() != D3D_OK)
		RetVal = deFALSE;

	if (Entry->DepthStencil)
	{
		if (Entry->DepthStencil->Release() != D3D_OK)
			RetVal = deFALSE;
	}

	if (!Bitmap->SetDriverData(m_UniqueID, NULL, this))
		RetVal = deFALSE;
	
	m_TexturesList.RemoveElement((DWORD) Bitmap);
	
	free(Entry);
	return RetVal;
}

deBoolean deDirect3D8::RenderTo(IdeBitmap *Bitmap, long CubeMapFace)
{
	HRESULT Ret;
	if (Bitmap == NULL)
	{
		// just return if this call is unnecessary
		if (!m_TextureRendered)
			return deTRUE;
		// when called with NULL, this restores the default render-target
		LPDIRECT3DSURFACE8	pBackBuffer;

		// grab the implicitly-created surfaces
		Ret = m_D3DDevice->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer);
		if (D3D_OK != Ret)
		{
			LogInfo("Failure while retrieving backbuffer\n");
			ReportErrorHR(Ret);

			return deFALSE;
		}
		if (!pBackBuffer || !m_pZBuffer)
			return deFALSE;

		D3DSURFACE_DESC Desc;
		pBackBuffer->GetDesc(&Desc);
		m_pZBuffer->GetDesc(&Desc);

		// set the device to use its own stuff
		Ret = m_D3DDevice->SetRenderTarget(pBackBuffer, m_pZBuffer);
		if (Ret != D3D_OK)
		{
			LogInfo("Failure while calling SetRenderTarget (original backbuffer)\n");
			ReportErrorHR(Ret);

			return deFALSE;
		}

		// release to avoid memory leak
		pBackBuffer->Release();

		//optimize a little
		m_TextureRendered = deFALSE;

		if (m_pCurrentTarget)
		{
			m_pCurrentTarget->Release();
			m_pCurrentTarget = NULL;
		}

		//return success
		return deTRUE;
	}
	else
	{
		TextureEntry *		Entry;
		LPDIRECT3DSURFACE8	Surface;
		void * ptr;
		
		Entry = (TextureEntry *)Bitmap->GetDriverData(m_UniqueID);
		if (!Entry || Entry->ID != TEXTURESTRUCTID)
			return deFALSE;
		
		// the way we do it, a render-target texture must have a z-buffer too
		if (!Entry->DepthStencil)
			return deFALSE;
		
		// grab the top-level surface of the texture
		if (Entry->NumFaces == 1)
		{
			if (S_OK == Entry->Texture->QueryInterface(IID_IDirect3DTexture8, &ptr))
			{
				if (ptr)
				{
					LPDIRECT3DTEXTURE8	FlatTex = (LPDIRECT3DTEXTURE8)ptr;
					Ret = FlatTex->GetSurfaceLevel(0, &Surface);
					FlatTex->Release();
					if (Ret != D3D_OK || !Surface)
					{
						LogInfo("Failure retrieving texture surface\n");
						ReportErrorHR(Ret);

						return deFALSE;
					}
				}
				else
					return deFALSE;
			}
			else
				return deFALSE;
		}
		else if (Entry->NumFaces == 6)
		{
			if (S_OK == Entry->Texture->QueryInterface(IID_IDirect3DCubeTexture8, &ptr))
			{
				if (ptr)
				{
					LPDIRECT3DCUBETEXTURE8	CubeTex = (LPDIRECT3DCUBETEXTURE8)ptr;
					Ret = CubeTex->GetCubeMapSurface((D3DCUBEMAP_FACES)(CubeMapFace), 0, &Surface);
					CubeTex->Release();
					if (Ret != D3D_OK || !Surface)
					{
						LogInfo("Failure retrieving cubetexture surface\n");
						ReportErrorHR(Ret);

						return deFALSE;
					}
				}
				else
					return deFALSE;
			}
			else
				return deFALSE;
		}
		else
			return deFALSE;
		
		// set the d3d render-target
		Ret = m_D3DDevice->SetRenderTarget(Surface, Entry->DepthStencil);
		if (Ret != D3D_OK)
		{
			LogInfo("Failure while calling SetRenderTarget (texture)\n");
			ReportErrorHR(Ret);

			return deFALSE;
		}
		
		Surface->Release();
		
		m_pCurrentTarget = Bitmap;
		m_pCurrentTarget->Claim();
		
		m_TextureRendered = deTRUE;

		return deTRUE;
	}
}

deBoolean deDirect3D8::ScreenShot(IdeBitmap * &pBitmap, deBoolean GrabWindow)
{
	if (!s_D3D || !m_D3DDevice)
	{
		DEBUGRETURN(deFALSE, "bad parameter");
	}
	int					ScrWidth, ScrHeight, ImgWidth, ImgHeight, AdapterNum, x, y, offset;
	D3DDISPLAYMODE		DisplayMode;
	IDirect3DSurface8*	pSurface;
	D3DLOCKED_RECT		LockRect;
	u8*					pData;
	u32*				pDW1;
	u32*				pDW2;
	deBoolean			Windowed;
	RECT				WindowRect;
	
	Windowed = m_CurrentDisplay.Windowed;

	AdapterNum = m_CurrentDisplay.Adapter;
	if (D3D_OK != s_D3D->GetAdapterDisplayMode(AdapterNum, &DisplayMode))
	{
		return deFALSE;
	}

	ScrWidth = DisplayMode.Width;
	ScrHeight = DisplayMode.Height;
	if (Windowed)
	{
		GetWindowRect(m_CurWindowHandle, &WindowRect);
		ImgWidth = WindowRect.right - WindowRect.left;
		ImgHeight = WindowRect.bottom - WindowRect.top;
		if (!GrabWindow)
		{
			WindowRect.top  += m_ClientRect.Top;
			WindowRect.left += m_ClientRect.Left;
			WindowRect.right  = WindowRect.left + m_ClientRect.Right;
			WindowRect.bottom = WindowRect.top + m_ClientRect.Bottom;
		}
	}
	else
	{
		ImgWidth = m_CurrentDisplay.Width;
		ImgHeight = m_CurrentDisplay.Height;
		WindowRect.top = WindowRect.left = 0;
		WindowRect.bottom = ImgHeight - 1;
		WindowRect.right = ImgWidth - 1;
	}

	if (D3D_OK != m_D3DDevice->CreateImageSurface(ScrWidth, ScrHeight, D3DFMT_A8R8G8B8, &pSurface))
	{
		return deFALSE;
	}

	if (D3D_OK != m_D3DDevice->GetFrontBuffer(pSurface))
	{
		pSurface->Release();
		return deFALSE;
	}

	if (D3D_OK != pSurface->LockRect(&LockRect, &WindowRect, D3DLOCK_READONLY ))
	{
		pSurface->Release();
		return deFALSE;
	}
	
	pBitmap = IdeBitmap_CreateBitmapWithInfo(ImgWidth, ImgHeight, IdeDriver::BPP_A8R8G8B8);
	if (!pBitmap)
	{
		pSurface->Release();
		return deFALSE;
	}

	pData = (u8*)pBitmap->GetData();
	if (!pData)
	{
		pBitmap->Release();
		pSurface->Release();
		return deFALSE;
	}

	pDW1 = (u32*)(pData);
	pDW2 = (u32*)(LockRect.pBits);
	offset = LockRect.Pitch - (ImgWidth*4);
	for (y = 0; y < ImgHeight; ++y)
	{
		for (x = 0; x < ImgWidth; ++x)
		{
			pDW1[y*ImgWidth + x] = *pDW2;
			++pDW2;
		}
		pDW2 = (u32*)((u8*)pDW2 + offset);
	}

	return deTRUE;
}

deBoolean deDirect3D8::GetVideoTexture(IdeBitmap* pBitmap, long CubeMapFace)
{
	if (!pBitmap)
		return deFALSE;
	if(!m_D3DDevice)
		return deFALSE;

	IUnknown*			TexturePtr = NULL;
	TextureEntry *		DriverTexture = NULL;
	D3DLOCKED_RECT		LockRect;
	D3DSURFACE_DESC		SurfaceDesc;
	HRESULT				Ret;
	
	DriverTexture = (TextureEntry *)(pBitmap->GetDriverData(m_UniqueID));
	if (!DriverTexture)
		return deFALSE;

	if (DriverTexture->NumFaces == 1)
	{
		void * ptr;
		Ret = DriverTexture->Texture->QueryInterface(IID_IDirect3DTexture8, &ptr);
		if (Ret == S_OK)
		{
			if (ptr)
			{
				LPDIRECT3DTEXTURE8 FlatTex = (LPDIRECT3DTEXTURE8)ptr;
				Ret = FlatTex->LockRect(0, &LockRect, NULL, D3DLOCK_READONLY);
				if (Ret != D3D_OK)
				{
					FlatTex->Release();
					return deFALSE;
				}
				Ret = FlatTex->GetLevelDesc(0, &SurfaceDesc);
				if (Ret != D3D_OK)
				{
					FlatTex->Release();
					return deFALSE;
				}
				TexturePtr = FlatTex;
			}
			else
				return deFALSE;
		}
		else
			return deFALSE;
	}
	else if (DriverTexture->NumFaces == 6)
	{
		void * ptr;
		Ret = DriverTexture->Texture->QueryInterface(IID_IDirect3DTexture8, &ptr);
		if (Ret == S_OK)
		{
			if (ptr)
			{
				LPDIRECT3DCUBETEXTURE8 CubeTex = (LPDIRECT3DCUBETEXTURE8)ptr;
				Ret = CubeTex->LockRect((D3DCUBEMAP_FACES)CubeMapFace, 0, &LockRect, NULL, D3DLOCK_READONLY);
				if (D3D_OK != Ret)
				{
					CubeTex->Release();
					return deFALSE;
				}
				Ret = CubeTex->GetLevelDesc(0, &SurfaceDesc);
				if (D3D_OK != Ret)
				{
					CubeTex->Release();
					return deFALSE;
				}
				TexturePtr = CubeTex;
			}
			else
				return deFALSE;
		}
		else
			return deFALSE;
	}
	else
		return deFALSE;

	if (pBitmap->GetWidth() != (long)SurfaceDesc.Width || pBitmap->GetHeight() != (long)SurfaceDesc.Height)
	{
		TexturePtr->Release();
		return deFALSE;
	}

	{
		u8*				pData;
		u8*				pD3DData;
		int				bytedepth, rowspan;
		u32				y;
		IdeDriver::BPP	bpp;
		
		bpp = pBitmap->GetFormat();
		if (bpp != FromD3DBPP(SurfaceDesc.Format))
		{
			TexturePtr->Release();
			return deFALSE;
		}

		pData = (u8*)pBitmap->GetData();
		if (!pData)
		{
			TexturePtr->Release();
			return deFALSE;
		}
		
		bytedepth = IdeDriver_BPPBitDepth(bpp)/8;
		rowspan = SurfaceDesc.Width*bytedepth;
		pD3DData = (u8*)(LockRect.pBits);
		for (y = 0; y < SurfaceDesc.Height; ++y)
		{
			memcpy(pData, pD3DData, rowspan);
			pData += rowspan;
			pD3DData += LockRect.Pitch;
		}
	}

	TexturePtr->Release();
	return deTRUE;
}

deBoolean deDirect3D8::SetFOVClipping(float FOVY, float Aspect, float ClipNear, float ClipFar)
{
	D3DXMATRIX FOVMatrix;

	D3DXMatrixPerspectiveFovLH(&FOVMatrix, FOVY, Aspect, ClipNear, ClipFar);

	//a function to set the Field of View
#pragma todo(test this function - it is based on the D3DXMatrixPerspectiveFovLH documentation - and it doesnt follow very closely)
	
	// some intermediate values
	float H, W;
	FOVY *= 0.5f;
	H = cosf(FOVY) / sinf(FOVY);
	W = H / Aspect;
	
	//setup the matrix
	memset(FOVMatrix.m, 0, 16*sizeof(float));

	FOVMatrix._11 = W;
	FOVMatrix._22 = H;
	FOVMatrix._33 = ClipFar/(ClipFar-ClipNear);
	FOVMatrix._34 = 1.0f;
	FOVMatrix._43 = -ClipNear * FOVMatrix._33;

	//if we can't set, fail
    if(m_D3DDevice->SetTransform(D3DTS_PROJECTION, &FOVMatrix) != D3D_OK)
		return deFALSE;

	//it passed, tell them so
	return deTRUE;
}

deBoolean deDirect3D8::SetOrthoClipping(float Width, float Height, float ClipNear, float ClipFar, deBoolean Invert)
{
	D3DMATRIX OrthoMatrix;
	
	//a function to set the Field of View Orthographically
	
	//setup the matrix
	memset(OrthoMatrix.m, 0, 16*sizeof(float));

	if (Invert)
		Height *= -1.0f;
	
	OrthoMatrix._11 = 2.0f/Width;
	OrthoMatrix._22 = 2.0f/Height;
	OrthoMatrix._33 = 1.0f/(ClipFar-ClipNear);
	OrthoMatrix._44 = 1.0f;
	OrthoMatrix._43 = ClipNear/(ClipNear-ClipFar);
	
	//if we can't set, fail
    if(m_D3DDevice->SetTransform(D3DTS_PROJECTION, &OrthoMatrix) != D3D_OK)
		return deFALSE;
	
	//it passed, tell them so
	return deTRUE;
}

deBoolean deDirect3D8::SetFOVClippingSubRect(float FOVY, float Aspect, float ClipNear, float ClipFar, deRect SubRect)
{
	deFloatRect FSubRect;
	D3DXMATRIX Matrix;
	float right, top;
	float fov2, left2, right2, bottom2, top2;

	if (SubRect.Left == 0 && SubRect.Right == 0 && SubRect.Top == 0 && SubRect.Bottom == 0)
	{
		FSubRect.Top = 0;
		FSubRect.Left = 0;
		FSubRect.Right = 1;
		FSubRect.Bottom = 1;
	}
	else
	{
		FSubRect.Top = (SubRect.Top - m_CurrentViewport.Y)/((deFloat)m_CurrentViewport.Height);
		FSubRect.Left = (SubRect.Left - m_CurrentViewport.X)/((deFloat)m_CurrentViewport.Width);
		FSubRect.Right = (SubRect.Right - m_CurrentViewport.X)/((deFloat)m_CurrentViewport.Width);
		FSubRect.Bottom = (SubRect.Bottom - m_CurrentViewport.Y)/((deFloat)m_CurrentViewport.Height);
	}
	
	fov2 = FOVY * 0.5f;
	
	top = ClipNear/(cosf(fov2)/sinf(fov2));
	right = top * Aspect;
	
	if (FSubRect.Left == 0.5) // dead center
		left2 = 0;
	else
		left2 = 2 * right * (FSubRect.Left - 0.5f);
	
	if (FSubRect.Right == 0.5) // dead center
		right2 = 0;
	else
		right2 = 2 * right * (FSubRect.Right - 0.5f);
	
	if (FSubRect.Bottom == 0.5) // dead center
		bottom2 = 0;
	else
		bottom2 = 2 * top * (FSubRect.Bottom - 0.5f);
	
	if (FSubRect.Top == 0.5) // dead center
		top2 = 0;
	else
		top2 = 2 * top * (FSubRect.Top - 0.5f);
	
	//setup the matrix
	D3DXMatrixPerspectiveOffCenterLH(&Matrix, left2, right2, bottom2, top2, ClipNear, ClipFar);
	
	//if we can't set, fail
    if(m_D3DDevice->SetTransform(D3DTS_PROJECTION, &Matrix) != D3D_OK)
		return deFALSE;
	
	//it passed, tell them so
	return deTRUE;
}

deBoolean deDirect3D8::SetFOVClippingSubRectFloat(float FOVY, float Aspect, float ClipNear, float ClipFar, deFloatRect FSubRect)
{
	D3DXMATRIX Matrix;
	float right, top;
	float fov2, left2, right2, bottom2, top2;

	fov2 = FOVY * 0.5f;

	top = ClipNear/(cosf(fov2)/sinf(fov2));
	right = top * Aspect;

	if (FSubRect.Left == 0.5) // dead center
		left2 = 0;
	else
		left2 = 2 * right * (FSubRect.Left - 0.5f);

	if (FSubRect.Right == 0.5) // dead center
		right2 = 0;
	else
		right2 = 2 * right * (FSubRect.Right - 0.5f);
	
	if (FSubRect.Bottom == 0.5) // dead center
		bottom2 = 0;
	else
		bottom2 = 2 * top * (FSubRect.Bottom - 0.5f);
	
	if (FSubRect.Top == 0.5) // dead center
		top2 = 0;
	else
		top2 = 2 * top * (FSubRect.Top - 0.5f);
	
	//setup the matrix
	D3DXMatrixPerspectiveOffCenterLH(&Matrix, left2, right2, bottom2, top2, ClipNear, ClipFar);

	//if we can't set, fail
    if(m_D3DDevice->SetTransform(D3DTS_PROJECTION, &Matrix) != D3D_OK)
		return deFALSE;
	
	//it passed, tell them so
	return deTRUE;
}

deBoolean deDirect3D8::SetOrthoClippingSubRect(float Width, float Height, float ClipNear, float ClipFar, deBoolean Invert, deRect SubRect)
{
#pragma todo(test this function - it is based on the D3DXMatrixOrthoOffCenterLH documentation)
	deFloatRect FSubRect;
	D3DMATRIX Matrix;
	
	if (SubRect.Left == 0 && SubRect.Right == 0 && SubRect.Top == 0 && SubRect.Bottom == 0)
	{
		FSubRect.Top = 0;
		FSubRect.Left = 0;
		FSubRect.Right = 1;
		FSubRect.Bottom = 1;
	}
	else
	{
		FSubRect.Top = (SubRect.Top - m_CurrentViewport.Y)/((deFloat)m_CurrentViewport.Height);
		FSubRect.Left = (SubRect.Left - m_CurrentViewport.X)/((deFloat)m_CurrentViewport.Width);
		FSubRect.Right = (SubRect.Right - m_CurrentViewport.X)/((deFloat)m_CurrentViewport.Width);
		FSubRect.Bottom = (SubRect.Bottom - m_CurrentViewport.Y)/((deFloat)m_CurrentViewport.Height);
	}
	
	memset(Matrix.m, 0, 16*sizeof(float));
	
	FSubRect.Left *= Width;
	FSubRect.Right *= Width;
	FSubRect.Top *= Height;
	FSubRect.Bottom *= Height;
	
	Matrix._11 = 2/(FSubRect.Right-FSubRect.Left);
	//	Matrix._12 = 0;
	//	Matrix._13 = 0;
	//	Matrix._14 = 0;
	
	//	Matrix._21 = 0;
	Matrix._22 = 2/(FSubRect.Bottom-FSubRect.Top);
	//	Matrix._23 = 0;
	//	Matrix._24 = 0;
	if (Invert)
		Matrix._22 *= -1;
	
	//	Matrix._31 = 0;
	//	Matrix._32 = 0;
	Matrix._33 = 1/(ClipFar-ClipNear);
	//	Matrix._34 = 0;
	
	Matrix._41 = (FSubRect.Left+FSubRect.Right)/(FSubRect.Left-FSubRect.Right);
	Matrix._42 = (FSubRect.Top+FSubRect.Bottom)/(FSubRect.Bottom-FSubRect.Top);
	Matrix._43 = ClipNear/(ClipNear-ClipFar);
	Matrix._44 = 1;
	
	//if we can't set, fail
	if(m_D3DDevice->SetTransform(D3DTS_PROJECTION, &Matrix) != D3D_OK)
		return deFALSE;
	
	//it passed, tell them so
	return deTRUE;
}
	
deBoolean deDirect3D8::SetOrthoClippingSubRectFloat(float Width, float Height, float ClipNear, float ClipFar, deBoolean Invert, deFloatRect FSubRect)
{
	D3DMATRIX Matrix;

	memset(Matrix.m, 0, 16*sizeof(float));

	FSubRect.Left *= Width;
	FSubRect.Right *= Width;
	FSubRect.Top *= Height;
	FSubRect.Bottom *= Height;
	
	Matrix._11 = 2/(FSubRect.Right-FSubRect.Left);
//	Matrix._12 = 0;
//	Matrix._13 = 0;
//	Matrix._14 = 0;

//	Matrix._21 = 0;
	Matrix._22 = 2/(FSubRect.Bottom-FSubRect.Top);
//	Matrix._23 = 0;
//	Matrix._24 = 0;
	if (Invert)
		Matrix._22 *= -1;

//	Matrix._31 = 0;
//	Matrix._32 = 0;
	Matrix._33 = 1/(ClipFar-ClipNear);
//	Matrix._34 = 0;
	
	Matrix._41 = (FSubRect.Left+FSubRect.Right)/(FSubRect.Left-FSubRect.Right);
	Matrix._42 = (FSubRect.Top+FSubRect.Bottom)/(FSubRect.Bottom-FSubRect.Top);
	Matrix._43 = ClipNear/(ClipNear-ClipFar);
	Matrix._44 = 1;

	//if we can't set, fail
    if(m_D3DDevice->SetTransform(D3DTS_PROJECTION, &Matrix) != D3D_OK)
		return deFALSE;
	
	//it passed, tell them so
	return deTRUE;
}

deBoolean deDirect3D8::SetCameraTransform(deTransformInfo *M)
{
	D3DXMATRIX		TransformMatrix;
	D3DXVECTOR3		D3DEye, D3DUp, D3DAt;
	deVec3d			u,i,t;
	((deTransform*)M)->GetUp(u);
	((deTransform*)M)->GetIn(i);
	t = M->Translation;
	D3DEye = D3DXVECTOR3(float(t.x), float(t.y), float(t.z));
	D3DUp = D3DXVECTOR3(float(u.x), float(u.y), float(u.z));
	D3DAt = D3DXVECTOR3(float(t.x+i.x), float(t.y+i.y), float(t.z+i.z));

	//a function to rotate or manipulate the viewport transformation

	//generate a left handed matrix
	D3DXMatrixLookAtLH(&TransformMatrix, &D3DEye, &D3DAt, &D3DUp);

	//set the viewport transform
	if(m_D3DDevice->SetTransform(D3DTS_VIEW, &TransformMatrix) != D3D_OK)
		return deFALSE;
	m_CameraMatrix = TransformMatrix;
	
	return deTRUE;
}

deBoolean deDirect3D8::SetCameraTransform(deVertex *Eye, deVertex *At, deVertex *Up)
{
	D3DXMATRIX		TransformMatrix;
	D3DXVECTOR3		D3DEye = D3DXVECTOR3(Eye->x, Eye->y, Eye->z);
	D3DXVECTOR3		D3DAt = D3DXVECTOR3(At->x, At->y, At->z);
	D3DXVECTOR3		D3DUp = D3DXVECTOR3(Up->x, Up->y, Up->z);

	//a function to rotate or manipulate the viewport transformation

	//generate a left handed matrix
	D3DXMatrixLookAtLH(&TransformMatrix, &D3DEye, &D3DAt, &D3DUp);
	
	//set the viewport transform
	if(m_D3DDevice->SetTransform(D3DTS_VIEW, &TransformMatrix) != D3D_OK)
		return deFALSE;
	m_CameraMatrix = TransformMatrix;
	
	return deTRUE;
}

deBoolean deDirect3D8::SetCameraTranslation(deVertex *Translation)
{
	D3DXMATRIX	CameraMatrix(m_CameraMatrix);
	D3DXMATRIX	TempMatrix;

	//translate the camera

	//move along z, y, then x
	D3DXMatrixTranslation(&TempMatrix, Translation->x, Translation->y, Translation->z);
	D3DXMatrixMultiply(&CameraMatrix, &CameraMatrix, &TempMatrix);

	//set it to the camera
	if(m_D3DDevice->SetTransform(D3DTS_VIEW, &CameraMatrix) != D3D_OK)
		return deFALSE;
	m_CameraMatrix = CameraMatrix;
	
	return deTRUE;
}

deBoolean deDirect3D8::SetCameraRotation(deVertex *Rotation)
{
	D3DXMATRIX	CameraMatrix(m_CameraMatrix);
	D3DXMATRIX	TempMatrix;

	//rotate the camera

	//rotate the camera
	D3DXMatrixRotationYawPitchRoll(&TempMatrix, Rotation->y, Rotation->x, Rotation->z);
	D3DXMatrixMultiply(&CameraMatrix, &CameraMatrix, &TempMatrix);

	//set it to the camera
	if(m_D3DDevice->SetTransform(D3DTS_VIEW, &CameraMatrix) != D3D_OK)
		return deFALSE;
	m_CameraMatrix = CameraMatrix;

	return deTRUE;
}

deBoolean deDirect3D8::ResetCameraIdentity()
{
	D3DXMATRIX	CameraMatrix;

	//reset the camera identity

	//set it as an identity
	D3DXMatrixIdentity(&CameraMatrix);

	//set it to the camera
	if(m_D3DDevice->SetTransform(D3DTS_VIEW, &CameraMatrix) != D3D_OK)
		return deFALSE;

	return deTRUE;
}

deBoolean deDirect3D8::SetWorldTransform(deTransformInfo * M)
{
#if 0
	// disabled because deMatrix4 is meant to have unit-vector axes
	D3DXMATRIX		WorldMatrix, TempMatrix;
	D3DXVECTOR3		D3DEye, D3DUp, D3DAt;
	deVertex		l,u,i,t;
	deDouble		x,y,z;
	deVec3d			tempvec;
	tempvec = M->GetLeft();
	x = Vec3d_Length(tempvec);
	l = Vertex(tempvec);
	tempvec = M->GetUp();
	y = Vec3d_Length(tempvec);
	u = Vertex(tempvec);
	tempvec = M->GetIn();
	z = Vec3d_Length(tempvec);
	i = Vertex(tempvec);
	t = Vertex(M->Translation);

/*	D3DEye = D3DXVECTOR3(0, 0, 0);
	D3DUp = D3DXVECTOR3(u.x, u.y, u.z);
	D3DAt = D3DXVECTOR3(i.x, i.y, i.z);

	D3DXMatrixLookAtLH(&TempMatrix, &D3DEye, &D3DAt, &D3DUp);
	D3DXMatrixTranslation(&WorldMatrix, t.x, t.y, t.z);
	D3DXMatrixMultiply(&WorldMatrix, &TempMatrix, &WorldMatrix);
	D3DXMatrixScaling(&TempMatrix, x, y, z);
	D3DXMatrixMultiply(&WorldMatrix, &TempMatrix, &WorldMatrix);
*/

	D3DXMatrixIdentity(&WorldMatrix);
	WorldMatrix._11 = l.x;
	WorldMatrix._12 = l.y;
	WorldMatrix._13 = l.z;
	WorldMatrix._21 = u.x;
	WorldMatrix._22 = u.y;
	WorldMatrix._23 = u.z;
	WorldMatrix._31 = i.x;
	WorldMatrix._32 = i.y;
	WorldMatrix._33 = i.z;
	WorldMatrix._41 = FLOAT(t.x*x);
	WorldMatrix._42 = FLOAT(t.y*y);
	WorldMatrix._43 = FLOAT(t.z*z);
	
#else
	D3DMATRIX		WorldMatrix;

	((deTransform*)M)->ToFloat44(WorldMatrix.m, deTRUE);
	
#endif
	
	if(m_D3DDevice->SetTransform(D3DTS_WORLD, &WorldMatrix) != D3D_OK)
		return deFALSE;
	m_WorldMatrix = WorldMatrix;
	
	return deTRUE;
}

deBoolean deDirect3D8::SetWorldTransform(deVertex *Eye, deVertex *At, deVertex *Up)
{
	D3DXMATRIX		TransformMatrix;
	D3DXVECTOR3		D3DEye = D3DXVECTOR3(Eye->x, Eye->y, Eye->z);
	D3DXVECTOR3		D3DAt = D3DXVECTOR3(At->x, At->y, At->z);
	D3DXVECTOR3		D3DUp = D3DXVECTOR3(Up->x, Up->y, Up->z);

	//a function to rotate or manipulate the viewport transformation

	//generate a left handed matrix (wonder what effects would happen if RH was used)
	D3DXMatrixLookAtLH(&TransformMatrix, &D3DEye, &D3DAt, &D3DUp);
	
	//set the viewport transform
	if(m_D3DDevice->SetTransform(D3DTS_WORLD, &TransformMatrix) != D3D_OK)
		return deFALSE;
	m_WorldMatrix = TransformMatrix;
	
	return deTRUE;
}

deBoolean deDirect3D8::SetWorldTranslation(deVertex *Translation)
{
	D3DXMATRIX	WorldMatrix;
	D3DXMATRIX	TempMatrix;

	//translate the world

	//move along z, y, then x
	D3DXMatrixTranslation(&TempMatrix, Translation->x, Translation->y, Translation->z);
	D3DXMatrixMultiply(&WorldMatrix, (D3DXMATRIX*)&m_WorldMatrix, &TempMatrix);

	//set it to the world
	if(m_D3DDevice->SetTransform(D3DTS_WORLD, &WorldMatrix) != D3D_OK)
		return deFALSE;
	m_WorldMatrix = WorldMatrix;
	
	return deTRUE;
}

deBoolean deDirect3D8::SetWorldRotation(deVertex *Rotation)
{
	D3DXMATRIX		WorldMatrix;
	D3DXMATRIX		TempMatrix;

	//rotate the world

	//rotate the world
	D3DXMatrixRotationYawPitchRoll(&TempMatrix, Rotation->y, Rotation->x, Rotation->z);
	D3DXMatrixMultiply(&WorldMatrix, (D3DXMATRIX*)&m_WorldMatrix, &TempMatrix);

	//set it to the world
	if(m_D3DDevice->SetTransform(D3DTS_WORLD, &WorldMatrix) != D3D_OK)
		return deFALSE;
	m_WorldMatrix = WorldMatrix;

	return deTRUE;
}

deBoolean deDirect3D8::ResetWorldIdentity()
{
	D3DXMATRIX	WorldMatrix;

	//reset the world identity

	//set it as an identity
	D3DXMatrixIdentity(&WorldMatrix);

	//set it to the world
	if(m_D3DDevice->SetTransform(D3DTS_WORLD, &WorldMatrix) != D3D_OK)
		return deFALSE;

	return deTRUE;
}

deBoolean deDirect3D8::SetTextureTransform(long StageNum, deTransformInfo * Mat)
{
	D3DMATRIX				TextureMatrix;
	D3DTRANSFORMSTATETYPE	Stage;

	switch (StageNum)
	{
	default: return deFALSE;
	case 0: Stage = D3DTS_TEXTURE0; break;
	case 1: Stage = D3DTS_TEXTURE1; break;
	case 2: Stage = D3DTS_TEXTURE2; break;
	case 3: Stage = D3DTS_TEXTURE3; break;
	case 4: Stage = D3DTS_TEXTURE4; break;
	case 5: Stage = D3DTS_TEXTURE5; break;
	case 6: Stage = D3DTS_TEXTURE6; break;
	case 7: Stage = D3DTS_TEXTURE7; break;
	}

	((deTransform*)Mat)->ToFloat44(TextureMatrix.m, deTRUE);
	
	if(D3D_OK != m_D3DDevice->SetTransform(Stage, &TextureMatrix))
		return deFALSE;

	return deTRUE;
}

deBoolean deDirect3D8::SetViewport(long X, long Y, long Width, long Height, float MinZ, float MaxZ)
{
	D3DVIEWPORT8	ViewPort;

	//adjust the s_D3D viewport

	deViewport * VP = &m_CurrentViewport;
	if (m_DeviceLost)
	{
		// it'll do this every frame, but that's still cheaper than rendering
		memset(&m_CurrentViewport, 0, sizeof(m_CurrentViewport));
		return deTRUE;
	}

	if (VP->X == X && VP->Y == Y && VP->Width == Width && VP->Height == Height &&
		VP->MinZ == MinZ && VP->MaxZ == MaxZ)
		return deTRUE;

	ViewPort.X = X;
	ViewPort.Y = Y;
	ViewPort.Width = Width;
	ViewPort.Height = Height;
	ViewPort.MinZ = MinZ;
	ViewPort.MaxZ = MaxZ;

	if(m_D3DDevice->SetViewport(&ViewPort) != D3D_OK)
		return deFALSE;

	VP->X = X;
	VP->Y = Y;
	VP->Width = Width;
	VP->Height = Height;
	VP->MinZ = MinZ;
	VP->MaxZ = MaxZ;

	return deTRUE;
}

deBoolean deDirect3D8::SetDepthRange(deFloat MinZ, deFloat MaxZ)
{
	D3DVIEWPORT8	ViewPort;
	
	//adjust the s_D3D viewport
	
	deViewport * VP = &m_CurrentViewport;
	if (m_DeviceLost)
	{
		return deTRUE;
	}
	
	if (VP->MinZ == MinZ && VP->MaxZ == MaxZ)
		return deTRUE;
	
	ViewPort.X = VP->X;
	ViewPort.Y = VP->Y;
	ViewPort.Width = VP->Width;
	ViewPort.Height = VP->Height;
	ViewPort.MinZ = MinZ;
	ViewPort.MaxZ = MaxZ;
	
	if(m_D3DDevice->SetViewport(&ViewPort) != D3D_OK)
		return deFALSE;
	
	VP->MinZ = MinZ;
	VP->MaxZ = MaxZ;
	
	return deTRUE;
}

deBoolean deDirect3D8::ValidFormat(long Adapter, Resource ResourceType, BPP BPPType, ZPP ZPPType, BPP FrontBuffer, long ExtraFlag) const
{
	HRESULT		Ret;

	//return if a BPP can be used for the requested resource

	switch(ResourceType)
	{
		case RESOURCE_DISPLAY:
			//want to know if a BPP can be used for the backbuffer
			Ret = s_D3D->CheckDeviceType(Adapter, D3D_DEV_TYPE, ToD3DBPP(FrontBuffer), ToD3DBPP(BPPType), FALSE);
			if (ZPPType != ZPP_UNKNOWN && Ret == D3D_OK)
			{
				Ret = s_D3D->CheckDeviceFormat(Adapter, D3D_DEV_TYPE, ToD3DBPP(FrontBuffer), D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, ToD3DZPP(ZPPType));
				if (Ret == D3D_OK)
					Ret = s_D3D->CheckDepthStencilMatch(Adapter, D3D_DEV_TYPE, ToD3DBPP(FrontBuffer), ToD3DBPP(BPPType), ToD3DZPP(ZPPType));
			}
			break;

		case RESOURCE_DISPLAYWINDOWED:
			//want to know if a BPP can be used for the backbuffer in windowed mode
			Ret = s_D3D->CheckDeviceType(Adapter, D3D_DEV_TYPE, ToD3DBPP(FrontBuffer), ToD3DBPP(BPPType), TRUE);
			if (ZPPType != ZPP_UNKNOWN && Ret == D3D_OK)
			{
				Ret = s_D3D->CheckDeviceFormat(Adapter, D3D_DEV_TYPE, ToD3DBPP(FrontBuffer), D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, ToD3DZPP(ZPPType));
				if (Ret == D3D_OK)
					Ret = s_D3D->CheckDepthStencilMatch(Adapter, D3D_DEV_TYPE, ToD3DBPP(FrontBuffer), ToD3DBPP(BPPType), ToD3DZPP(ZPPType));
			}
			break;

		case RESOURCE_TEXTURE:
			Ret = s_D3D->CheckDeviceFormat(Adapter, D3D_DEV_TYPE, ToD3DBPP(m_CurrentDisplay.FrontBpp), 0, D3DRTYPE_TEXTURE, ToD3DBPP(BPPType));
			break;

		case RESOURCE_RENDERTARGET:
			Ret = s_D3D->CheckDeviceFormat(Adapter, D3D_DEV_TYPE, ToD3DBPP(m_CurrentDisplay.FrontBpp), D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE, ToD3DBPP(BPPType));
			break;

		case RESOURCE_DEPTHSTENCIL:
			Ret = s_D3D->CheckDeviceFormat(Adapter, D3D_DEV_TYPE, ToD3DBPP(FrontBuffer), D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, ToD3DZPP(ZPPType));
			if (Ret == D3D_OK)
				Ret = s_D3D->CheckDepthStencilMatch(Adapter, D3D_DEV_TYPE, ToD3DBPP(FrontBuffer), ToD3DBPP(BPPType), ToD3DZPP(ZPPType));
			break;

		case RESOURCE_FSAA:
			if (ExtraFlag == 0 || (ExtraFlag >= 2 && ExtraFlag <= 16))
			{
				Ret = s_D3D->CheckDeviceMultiSampleType(Adapter, D3D_DEV_TYPE, 
					ToD3DBPP(BPPType), FALSE, (D3DMULTISAMPLE_TYPE)ExtraFlag);
			}
			else
				Ret = D3DERR_INVALIDCALL;
			break;
			
		case RESOURCE_FSAAWINDOWED:
			if (ExtraFlag == 0 || (ExtraFlag >= 2 && ExtraFlag <= 16))
			{
				Ret = s_D3D->CheckDeviceMultiSampleType(Adapter, D3D_DEV_TYPE, 
					ToD3DBPP(BPPType), TRUE, (D3DMULTISAMPLE_TYPE)ExtraFlag);
			}
			else
				Ret = D3DERR_INVALIDCALL;
			break;
			
		default:
			Ret = D3DERR_NOTAVAILABLE;
			break;
	}

	// D3DERR_NOTAVAILABLE if not a supported format
	return (Ret == D3D_OK) ? deTRUE : deFALSE;
}

deDirect3D8::VertexShaderEntry * deDirect3D8::GetFixedFunctionShader(IdeVertexBuffer *Vertex)
{
	if (!Vertex)
		return NULL;

	long							BufferFlags;
	IdeVertexBuffer::BufferType		Counter;
	VertexShaderEntry *				RetVal;
	IdeVertexBuffer *				StreamHolder;

	StreamHolder = Vertex->GetBoundDataBuffer();
	if (!StreamHolder)
		StreamHolder = Vertex;

	BufferFlags = 0;
	for (Counter = IdeVertexBuffer::BUFFER_POSITION; Counter < IdeVertexBuffer::BUFFER_INDICES16; ((int&)Counter)++)
	{
		if (StreamHolder->BufferExists(Counter))
			BufferFlags |= (1 << Counter);
	}

	// look for an already-created shader for this combination of streams
	RetVal = LookUpFFShader(BufferFlags);

	// if one doesn't exist, make one
	if (RetVal == NULL)
		RetVal = MakeFFShader(BufferFlags);
	
	// might be NULL if wasn't able to make a shader
	return RetVal;
}

deDirect3D8::VertexShaderEntry * deDirect3D8::LookUpFFShader(long BufferFlags) const
{
	VertexShaderEntry * ShaderEntry;
	void * ptr;
	
	ptr = m_ShaderList.GetNext(NULL, ShaderEntry);
	while (ptr)
	{
		if (ShaderEntry->BufferFlags == BufferFlags)
			return ShaderEntry;
		
		ptr = m_ShaderList.GetNext(ptr, ShaderEntry);
	}
	
	return NULL;
}

deDirect3D8::VertexShaderEntry * deDirect3D8::MakeFFShader(long BufferFlags)
{
	long							Mask;
	long							ShaderSizeFlag;
	long							ShaderType;
	long							ShaderSize;
	IdeVertexBuffer::BufferDataType	BufferDataType;
	IdeVertexBuffer::BufferType		Counter;
	VertexShaderEntry				ShaderEntry;
	VertexShaderEntry*				RetVal;
	
	//now, allocate some memory for the shader info
	//2 commands per buffer + ending command
	Mask = IdeVertexBuffer::BUFFER_COUNT - 1;
	ShaderEntry.ShaderInfo = (unsigned long *)malloc(((Mask * 2) + 1) * sizeof(long));
	if(!ShaderEntry.ShaderInfo)
	{
		return NULL;
	}
	
	//wipe out the shader info. NOP entries are not compiled in when it is compiled
	for(Mask = 0; Mask < (IdeVertexBuffer::BUFFER_COUNT - 1) * 2; Mask++)
	{
		ShaderEntry.ShaderInfo[Mask] = D3DVSD_NOP();
	}
	
	//fill in the last entry with the end flag
	ShaderEntry.ShaderInfo[(IdeVertexBuffer::BUFFER_COUNT - 1) * 2] = D3DVSD_END();

	for (Counter = IdeVertexBuffer::BUFFER_POSITION; Counter < IdeVertexBuffer::BUFFER_INDICES16; ((int&)Counter)++)
	{
		Mask = (1 << Counter);
		if (BufferFlags & Mask)
		{
			//first, figure out the size of the shader
			BufferDataType = IdeVertexBuffer_FillBufferDataType(Counter, &ShaderSize);
			switch(BufferDataType)
			{
			case IdeVertexBuffer::BUFFER_DATALONG:
				ShaderSizeFlag = D3DVSDT_D3DCOLOR;
				break;
				
			case IdeVertexBuffer::BUFFER_DATAFLOAT:
				ShaderSizeFlag = D3DVSDT_FLOAT1;
				break;
				
			case IdeVertexBuffer::BUFFER_DATAFLOAT2:
				ShaderSizeFlag = D3DVSDT_FLOAT2;
				break;
				
			case IdeVertexBuffer::BUFFER_DATAFLOAT3:
				ShaderSizeFlag = D3DVSDT_FLOAT3;
				break;
				
			default:
				//don't know it, fail
				return NULL;
			}
			
			//figure out the type for the shader
			switch(Counter)
			{
			case IdeVertexBuffer::BUFFER_POSITION:
				ShaderType = D3DVSDE_POSITION;
				break;
				
			case IdeVertexBuffer::BUFFER_NORMAL:
				ShaderType = D3DVSDE_NORMAL;
				break;
				
			case IdeVertexBuffer::BUFFER_DIFFUSE:
				ShaderType = D3DVSDE_DIFFUSE;
				break;
				
			case IdeVertexBuffer::BUFFER_SPECULAR:
				ShaderType = D3DVSDE_SPECULAR;
				break;
				
			case IdeVertexBuffer::BUFFER_TEXTURECOORDS0:
				ShaderType = D3DVSDE_TEXCOORD0;
				break;
				
			case IdeVertexBuffer::BUFFER_TEXTURECOORDS1:
				ShaderType = D3DVSDE_TEXCOORD1;
				break;
				
			case IdeVertexBuffer::BUFFER_TEXTURECOORDS2:
				ShaderType = D3DVSDE_TEXCOORD2;
				break;
				
			case IdeVertexBuffer::BUFFER_TEXTURECOORDS3:
				ShaderType = D3DVSDE_TEXCOORD3;
				break;
				
			case IdeVertexBuffer::BUFFER_TEXTURECOORDS4:
				ShaderType = D3DVSDE_TEXCOORD4;
				break;
				
			case IdeVertexBuffer::BUFFER_TEXTURECOORDS5:
				ShaderType = D3DVSDE_TEXCOORD5;
				break;
				
			case IdeVertexBuffer::BUFFER_TEXTURECOORDS6:
				ShaderType = D3DVSDE_TEXCOORD6;
				break;
				
			case IdeVertexBuffer::BUFFER_TEXTURECOORDS7:
				ShaderType = D3DVSDE_TEXCOORD7;
				break;
				
			default:
				//unknown, fail
				return NULL;
			}
			
			//set the info
			ShaderEntry.ShaderInfo[Counter * 2] = D3DVSD_STREAM(Counter);
			ShaderEntry.ShaderInfo[(Counter * 2) + 1] = D3DVSD_REG(ShaderType, ShaderSizeFlag);
			
		}
		else
		{
			ShaderEntry.ShaderInfo[Counter * 2] = D3DVSD_NOP();
			ShaderEntry.ShaderInfo[(Counter * 2) + 1] = D3DVSD_NOP();
		}
	}

	ShaderEntry.ID = m_UniqueID;
	ShaderEntry.BufferFlags = BufferFlags;

	HRESULT result;
	result = m_D3DDevice->CreateVertexShader(ShaderEntry.ShaderInfo, NULL, &ShaderEntry.VertexShader, 0);
	//create the vertex shader
	if(result != D3D_OK)
	{
		LogInfo("Failure while creating vertex shader\n");
		ReportErrorHR(result);
		
		return NULL;
	} 
	
	m_ShaderList.AddElement(ShaderEntry);
	m_ShaderList.GetPrev(NULL, RetVal);
	return RetVal;
}

long deDirect3D8::DeleteShaders()
{
	VertexShaderEntry *	ShaderEntry;
	HRESULT				result;
	int					failed;
	void *				ptr;
	
	failed = 0;
	ptr = m_ShaderList.GetNext(NULL, ShaderEntry);
	while (ptr)
	{
		//free the shader info
		if(ShaderEntry->ShaderInfo)
			free(ShaderEntry->ShaderInfo);

		//free the shader itself
		result = m_D3DDevice->DeleteVertexShader(ShaderEntry->VertexShader);
		if (result != D3D_OK)
			failed++;
		
		ptr = m_ShaderList.RemoveElementGetNext(ptr, ShaderEntry);
	}

	return failed;
}

void deDirect3D8::InvalidateRenderStates(deBoolean Valid)
{
	m_RenderStatesValid = Valid;
}

D3DFORMAT deDirect3D8::ToD3DBPP(IdeDriver::BPP BPPType) const
{
	//convert our BPP type to a D3DBPP type
	switch(BPPType)
	{
		case IdeDriver::BPP_X1R5G5B5:
			return D3DFMT_X1R5G5B5;

		case IdeDriver::BPP_A1R5G5B5:
			return D3DFMT_A1R5G5B5;

		case IdeDriver::BPP_A4R4G4B4:
			return D3DFMT_A4R4G4B4;

		case IdeDriver::BPP_X4R4G4B4:
			return D3DFMT_X4R4G4B4;

		case IdeDriver::BPP_A8:
			return D3DFMT_A8;

		case IdeDriver::BPP_R3G3B2:
			return D3DFMT_R3G3B2;

		case IdeDriver::BPP_A8R3G3B2:
			return D3DFMT_A8R3G3B2;

		case IdeDriver::BPP_A2B10G10R10:
			return D3DFMT_A2B10G10R10;

		case IdeDriver::BPP_G16R16:
			return D3DFMT_G16R16;

		case IdeDriver::BPP_R5G6B5:
			return D3DFMT_R5G6B5;

		case IdeDriver::BPP_R8G8B8:
			return D3DFMT_R8G8B8;

		case IdeDriver::BPP_A8R8G8B8:
			return D3DFMT_A8R8G8B8;

		case IdeDriver::BPP_X8R8G8B8:
			return D3DFMT_X8R8G8B8;

		case IdeDriver::BPP_P8:
			return D3DFMT_P8;

		case IdeDriver::BPP_A8P8:
			return D3DFMT_A8P8;

		case IdeDriver::BPP_L8:
			return D3DFMT_L8;

		case IdeDriver::BPP_A8L8:
			return D3DFMT_A8L8;

		case IdeDriver::BPP_A4L4:
			return D3DFMT_A4L4;

		case IdeDriver::BPP_V8U8:
			return D3DFMT_V8U8;

		case IdeDriver::BPP_Q8W8V8U8:
			return D3DFMT_Q8W8V8U8;

		case IdeDriver::BPP_V16U16:
			return D3DFMT_V16U16;

		case IdeDriver::BPP_W11V11U10:
			return D3DFMT_W11V11U10;

		case IdeDriver::BPP_L6V5U5:
			return D3DFMT_L6V5U5;

		case IdeDriver::BPP_X8L8V8U8:
			return D3DFMT_X8L8V8U8;

		case IdeDriver::BPP_A2W10V10U10:
			return D3DFMT_A2W10V10U10;

		case IdeDriver::BPP_UYVY:
			return D3DFMT_UYVY;

		case IdeDriver::BPP_YUY2:
			return D3DFMT_YUY2;

		case IdeDriver::BPP_DXT1:
			return D3DFMT_DXT1;

		case IdeDriver::BPP_DXT2:
			return D3DFMT_DXT2;

		case IdeDriver::BPP_DXT3:
			return D3DFMT_DXT3;

		case IdeDriver::BPP_DXT4:
			return D3DFMT_DXT4;

		case IdeDriver::BPP_DXT5:
			return D3DFMT_DXT5;

		default:
			return D3DFMT_UNKNOWN;
	}
}

IdeDriver::BPP deDirect3D8::FromD3DBPP(D3DFORMAT BPPType) const
{
	//convert from s_D3D's BPP to our's
	switch(BPPType)
	{
		case D3DFMT_X1R5G5B5:
			return IdeDriver::BPP_X1R5G5B5;

		case D3DFMT_A1R5G5B5:
			return IdeDriver::BPP_A1R5G5B5;

		case D3DFMT_A4R4G4B4:
			return IdeDriver::BPP_A4R4G4B4;

		case D3DFMT_X4R4G4B4:
			return IdeDriver::BPP_X4R4G4B4;

		case D3DFMT_A8:
			return IdeDriver::BPP_A8;

		case D3DFMT_R3G3B2:
			return IdeDriver::BPP_R3G3B2;

		case D3DFMT_A8R3G3B2:
			return IdeDriver::BPP_A8R3G3B2;

		case D3DFMT_A2B10G10R10:
			return IdeDriver::BPP_A2B10G10R10;

		case D3DFMT_G16R16:
			return IdeDriver::BPP_G16R16;

		case D3DFMT_R5G6B5:
			return IdeDriver::BPP_R5G6B5;

		case D3DFMT_R8G8B8:
			return IdeDriver::BPP_R8G8B8;

		case D3DFMT_A8R8G8B8:
			return IdeDriver::BPP_A8R8G8B8;

		case D3DFMT_X8R8G8B8:
			return IdeDriver::BPP_X8R8G8B8;

		case D3DFMT_P8:
			return IdeDriver::BPP_P8;

		case D3DFMT_A8P8:
			return IdeDriver::BPP_A8P8;

		case D3DFMT_L8:
			return IdeDriver::BPP_L8;

		case D3DFMT_A8L8:
			return IdeDriver::BPP_A8L8;

		case D3DFMT_A4L4:
			return IdeDriver::BPP_A4L4;

		case D3DFMT_V8U8:
			return IdeDriver::BPP_V8U8;

		case D3DFMT_Q8W8V8U8:
			return IdeDriver::BPP_Q8W8V8U8;

		case D3DFMT_V16U16:
			return IdeDriver::BPP_V16U16;

		case D3DFMT_W11V11U10:
			return IdeDriver::BPP_W11V11U10;

		case D3DFMT_L6V5U5:
			return IdeDriver::BPP_L6V5U5;

		case D3DFMT_X8L8V8U8:
			return IdeDriver::BPP_X8L8V8U8;

		case D3DFMT_A2W10V10U10:
			return IdeDriver::BPP_A2W10V10U10;

		case D3DFMT_UYVY:
			return IdeDriver::BPP_UYVY;

		case D3DFMT_YUY2:
			return IdeDriver::BPP_YUY2;

		case D3DFMT_DXT1:
			return IdeDriver::BPP_DXT1;

		case D3DFMT_DXT2:
			return IdeDriver::BPP_DXT2;

		case D3DFMT_DXT3:
			return IdeDriver::BPP_DXT3;

		case D3DFMT_DXT4:
			return IdeDriver::BPP_DXT4;

		case D3DFMT_DXT5:
			return IdeDriver::BPP_DXT5;

		default:
			return IdeDriver::BPP_UNKNOWN;
	}
}

D3DFORMAT deDirect3D8::ToD3DZPP(IdeDriver::ZPP ZPPType) const
{
	//convert our BPP type to a D3DBPP type
	switch(ZPPType)
	{
	case IdeDriver::ZPP_D16:
		return D3DFMT_D16;

	case IdeDriver::ZPP_D15S1:
		return D3DFMT_D15S1;
		
	case IdeDriver::ZPP_D24:
		return D3DFMT_D24X8;
		
	case IdeDriver::ZPP_D24S4:
		return D3DFMT_D24X4S4;
		
	case IdeDriver::ZPP_D24S8:
		return D3DFMT_D24S8;
		
	case IdeDriver::ZPP_D32:
		return D3DFMT_D32;
		
	default:
		return D3DFMT_UNKNOWN;
	}
}

IdeDriver::ZPP deDirect3D8::FromD3DZPP(D3DFORMAT ZPPType) const
{
	//convert from s_D3D's BPP to our's
	switch(ZPPType)
	{
	case D3DFMT_D16:
		return IdeDriver::ZPP_D16;
		
	case D3DFMT_D15S1:
		return IdeDriver::ZPP_D15S1;
		
	case D3DFMT_D24X8:
		return IdeDriver::ZPP_D24;
		
	case D3DFMT_D24X4S4:
		return IdeDriver::ZPP_D24S4;
		
	case D3DFMT_D24S8:
		return IdeDriver::ZPP_D24S8;
		
	case D3DFMT_D32:
		return IdeDriver::ZPP_D32;
		
	default:
		return IdeDriver::ZPP_UNKNOWN;
	}
}

D3DMATERIALCOLORSOURCE deDirect3D8::ToD3DMatSource(IdeRenderMaterial::MatSource_t source) const
{
	switch (source)
	{
	case IdeRenderMaterial::MATSOURCE_MATERIAL:
		return D3DMCS_MATERIAL;

	case IdeRenderMaterial::MATSOURCE_DIFFUSE:
		return D3DMCS_COLOR1;
		
	case IdeRenderMaterial::MATSOURCE_SPECULAR:
		return D3DMCS_COLOR2;
		
	default: 
		return D3DMCS_MATERIAL;
	}
}

#define HR2StringCase(Case_)	case Case_:\
		strcpy(szHR, #Case_); break
 
char* __cdecl HR2String(HRESULT hr, char* szHR)
{
	switch(hr)
	{
	HR2StringCase(S_OK);
	HR2StringCase(S_FALSE);
	
	HR2StringCase(REGDB_E_CLASSNOTREG);
	
	HR2StringCase(E_UNEXPECTED); // catastrophic failure
	HR2StringCase(E_FAIL);
	HR2StringCase(E_NOTIMPL);
	HR2StringCase(E_OUTOFMEMORY);
	HR2StringCase(E_INVALIDARG);
	HR2StringCase(E_NOINTERFACE);
	HR2StringCase(E_POINTER);
	HR2StringCase(E_HANDLE);
	HR2StringCase(E_ABORT);
	HR2StringCase(E_PENDING);

	//HR2StringCase_(0x80000000 | ERROR_NOT_FOUND, ERROR_NOT_FOUND);

	//CoInit
	HR2StringCase(CO_E_FIRST);
	HR2StringCase(CO_E_LAST);
	HR2StringCase(CO_S_FIRST);
	HR2StringCase(CO_S_LAST);

	//Direct3D
	HR2StringCase(D3DERR_WRONGTEXTUREFORMAT               );
	HR2StringCase(D3DERR_UNSUPPORTEDCOLOROPERATION        );
	HR2StringCase(D3DERR_UNSUPPORTEDCOLORARG              );
	HR2StringCase(D3DERR_UNSUPPORTEDALPHAOPERATION        );
	HR2StringCase(D3DERR_UNSUPPORTEDALPHAARG              );
	HR2StringCase(D3DERR_TOOMANYOPERATIONS                );
	HR2StringCase(D3DERR_CONFLICTINGTEXTUREFILTER         );
	HR2StringCase(D3DERR_UNSUPPORTEDFACTORVALUE           );
	HR2StringCase(D3DERR_CONFLICTINGRENDERSTATE           );
	HR2StringCase(D3DERR_UNSUPPORTEDTEXTUREFILTER         );
	HR2StringCase(D3DERR_CONFLICTINGTEXTUREPALETTE        );
	HR2StringCase(D3DERR_DRIVERINTERNALERROR              );
	HR2StringCase(D3DERR_DEVICELOST                       );
	HR2StringCase(D3DERR_DEVICENOTRESET                   );
	HR2StringCase(D3DERR_NOTAVAILABLE                     );
	HR2StringCase(D3DERR_OUTOFVIDEOMEMORY                 );
	HR2StringCase(D3DERR_INVALIDDEVICE                    );
	HR2StringCase(D3DERR_INVALIDCALL                      );
	HR2StringCase(D3DERR_DRIVERINVALIDCALL                );

	default:
		if(hr>0)
		{
			//strcpy(szHR, "Unknown Warning: 0x");
			FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, hr, 0, szHR, 80, 0);
		}
		else
		{
			strcpy(szHR, "Unknown Failure: 0x");
			itoa(hr, &szHR[strlen(szHR)], 16);
			strcpy(&szHR[strlen(szHR)], " (");
			itoa(hr, &szHR[strlen(szHR)], 10);
			strcpy(&szHR[strlen(szHR)], ")");
		}
		break;
	}

	return szHR;
}

#define Format2StringCase(Case_)	case Case_:\
			strcpy(szFmt, #Case_); break

char* __cdecl Format2String(IdeDriver::BPP format, char* szFmt)
{
	switch(format)
	{
		Format2StringCase(IdeDriver::BPP_UNKNOWN);
		Format2StringCase(IdeDriver::BPP_X1R5G5B5);
		Format2StringCase(IdeDriver::BPP_A1R5G5B5);
		Format2StringCase(IdeDriver::BPP_A4R4G4B4);
		Format2StringCase(IdeDriver::BPP_X4R4G4B4);
		Format2StringCase(IdeDriver::BPP_A8);
		Format2StringCase(IdeDriver::BPP_R3G3B2);
		Format2StringCase(IdeDriver::BPP_A8R3G3B2);
		Format2StringCase(IdeDriver::BPP_A2B10G10R10);
		Format2StringCase(IdeDriver::BPP_G16R16);
		Format2StringCase(IdeDriver::BPP_R5G6B5);
		Format2StringCase(IdeDriver::BPP_R8G8B8);
		Format2StringCase(IdeDriver::BPP_A8R8G8B8);
		Format2StringCase(IdeDriver::BPP_X8R8G8B8);
		Format2StringCase(IdeDriver::BPP_P8);
		Format2StringCase(IdeDriver::BPP_A8P8);
		Format2StringCase(IdeDriver::BPP_L8);
		Format2StringCase(IdeDriver::BPP_A8L8);
		Format2StringCase(IdeDriver::BPP_A4L4);
		Format2StringCase(IdeDriver::BPP_V8U8);
		Format2StringCase(IdeDriver::BPP_Q8W8V8U8);
		Format2StringCase(IdeDriver::BPP_V16U16);
		Format2StringCase(IdeDriver::BPP_W11V11U10);
		Format2StringCase(IdeDriver::BPP_L6V5U5);
		Format2StringCase(IdeDriver::BPP_X8L8V8U8);
		Format2StringCase(IdeDriver::BPP_A2W10V10U10);
		Format2StringCase(IdeDriver::BPP_UYVY);
		Format2StringCase(IdeDriver::BPP_YUY2);
		Format2StringCase(IdeDriver::BPP_DXT1);
		Format2StringCase(IdeDriver::BPP_DXT2);
		Format2StringCase(IdeDriver::BPP_DXT3);
		Format2StringCase(IdeDriver::BPP_DXT4);
		Format2StringCase(IdeDriver::BPP_DXT5);
		
	default:
		strcpy(szFmt, "Unknown Texture Format: 0x");
		itoa(format, &szFmt[strlen(szFmt)], 16);
		strcpy(&szFmt[strlen(szFmt)], " (");
		itoa(format, &szFmt[strlen(szFmt)], 10);
		strcpy(&szFmt[strlen(szFmt)], ")");
		break;
	}
	
	return szFmt;
}

deBoolean deDirect3D8::AddShader(IdeShader * Shader)
{
	Shader = 0;
	return deFALSE;
}

deBoolean deDirect3D8::SetShader(IdeShader * Shader)
{
	Shader = 0;
	return deFALSE;
}

deBoolean deDirect3D8::RemoveShader(IdeShader * Shader)
{
	Shader = 0;
	return deFALSE;
}

