//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Vertex/Pixel Shaders
//
//===========================================================================//
#define DISABLE_PROTECTED_THINGS
#if ( defined(_WIN32) && !defined( _X360 ) )
#elif POSIX
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <errno.h>
#include <sys/ioctl.h>
#define closesocket close
#define WSAGetLastError() errno
#undef SOCKET
typedef int SOCKET;
#define SOCKET_ERROR (-1)
#define SD_SEND 0x01
#define INVALID_SOCKET (~0)
#endif

#include "togl/rendermechanism.h"
#include "vertexshaderdx8.h"
#include "tier1/utlsymbol.h"
#include "tier1/utlvector.h"
#include "tier1/utldict.h"
#include "tier1/utllinkedlist.h"
#include "tier1/utlbuffer.h"
#include "tier1/UtlStringMap.h"
#include "locald3dtypes.h"
#include "shaderapidx8_global.h"
#include "recording.h"
#include "tier0/vprof.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/imaterialsystemhardwareconfig.h"
#include "KeyValues.h"
#include "shaderapidx8.h"
#include "materialsystem/IShader.h"
#include "IShaderSystem.h"
#include "tier0/fasttimer.h"
#include <sys/stat.h>
#include <time.h>
#include <stdlib.h>
#include "filesystem.h"
#include "convar.h"
#include "materialsystem/shader_vcs_version.h"
#include "tier1/lzmaDecoder.h"
#include "tier1/utlmap.h"

#include "datacache/idatacache.h"
#include "tier1/diff.h"
#include "shaderdevicedx8.h"
#include "filesystem/IQueuedLoader.h"
#include "tier2/tier2.h"
#include "shaderapi/ishaderutil.h"
#include "tier0/icommandline.h"

#include "Color.h"
#include "tier0/dbg.h"

#ifdef REMOTE_DYNAMIC_SHADER_COMPILE

# if defined (POSIX)

#  include <sys/types.h>
#  include <sys/socket.h>

# else

#  include <winsock2.h>
#  include <ws2tcpip.h>

# endif

#endif

// NOTE: This has to be the last file included!
#include "tier0/memdbgon.h"


// It currently includes windows.h and we don't want that.
#ifdef USE_ACTUAL_DX

#include "../utils/bzip2/bzlib.h"

#else

int BZ2_bzBuffToBuffDecompress( 
      char*         dest, 
      unsigned int* destLen,
      char*         source, 
      unsigned int  sourceLen,
      int           small, 
      int           verbosity 
   )
{
	return 0;
}

#endif

static ConVar mat_remoteshadercompile( "mat_remoteshadercompile", "127.0.0.1", FCVAR_CHEAT );

//#define PROFILE_SHADER_CREATE

//#define NO_AMBIENT_CUBE
#define MAX_BONES 3

// debugging aid
#define MAX_SHADER_HISTORY	16

#if !defined( _X360 )
#define SHADER_FNAME_EXTENSION	".vcs"
#else
#define SHADER_FNAME_EXTENSION	".360.vcs"
#endif

#ifdef DYNAMIC_SHADER_COMPILE
volatile static char s_ShaderCompileString[]="dynamic_shader_compile_is_on";
#endif

#ifdef DYNAMIC_SHADER_COMPILE
static void MatFlushShaders( void );
#endif

// D3D to OpenGL translator
//static D3DToGL_ASM sg_D3DToOpenGLTranslator;	// Remove the _ASM to switch to the new translator
//static D3DToGL sg_NewD3DToOpenGLTranslator;		// Remove the _ASM to switch to the new translator

static const char *GetLightTypeName( VertexShaderLightTypes_t type )
{
	static const char *s_VertexShaderLightTypeNames[] = 
	{
		"LIGHT_NONE",
		"LIGHT_SPOT",
		"LIGHT_POINT",
		"LIGHT_DIRECTIONAL",
		"LIGHT_STATIC",
		"LIGHT_AMBIENTCUBE",
	};
	return s_VertexShaderLightTypeNames[type+1];
}

#ifdef PROFILE_SHADER_CREATE
static FILE *GetDebugFileHandle( void )
{
	static FILE *fp = NULL;
	if( !fp )
	{
		fp = fopen( "shadercreate.txt", "w" );
		Assert( fp );
	}
	return fp;
}
#endif // PROFILE_SHADER_CREATE

#ifdef DX_TO_GL_ABSTRACTION
	// mat_autoload_glshaders instructs the engine to load a cached shader table at startup
	// it will try for glshaders.cfg first, then fall back to glbaseshaders.cfg if not found
	// mat_autosave_glshaders instructs the engine to save out the shader table at key points
	// to the filename glshaders.cfg
	//

	ConVar mat_autosave_glshaders( "mat_autosave_glshaders", "1" );
	ConVar mat_autoload_glshaders( "mat_autoload_glshaders", "1" );
#endif
//-----------------------------------------------------------------------------
// Explicit instantiation of shader buffer implementation
//-----------------------------------------------------------------------------
template class CShaderBuffer< ID3DXBuffer >;


//-----------------------------------------------------------------------------
// Used to find unique shaders
//-----------------------------------------------------------------------------
#ifdef MEASURE_DRIVER_ALLOCATIONS
static CUtlMap< CRC32_t, int, int > s_UniqueVS( 0, 0, DefLessFunc( CRC32_t ) );
static CUtlMap< CRC32_t, int, int > s_UniquePS( 0, 0, DefLessFunc( CRC32_t ) );
static CUtlMap< IDirect3DVertexShader9*, CRC32_t, int > s_VSLookup( 0, 0, DefLessFunc( IDirect3DVertexShader9* ) );
static CUtlMap< IDirect3DPixelShader9*, CRC32_t, int > s_PSLookup( 0, 0, DefLessFunc( IDirect3DPixelShader9* ) );
#endif

static int s_NumPixelShadersCreated = 0;
static int s_NumVertexShadersCreated = 0;

static void RegisterVS( const void* pShaderBits, int nShaderSize, IDirect3DVertexShader9* pShader )
{
#ifdef MEASURE_DRIVER_ALLOCATIONS
	CRC32_t crc;
	CRC32_Init( &crc );
	CRC32_ProcessBuffer( &crc, pShaderBits, nShaderSize );
	CRC32_Final( &crc );

	s_VSLookup.Insert( pShader, crc );

	int nIndex = s_UniqueVS.Find( crc );
	if ( nIndex != s_UniqueVS.InvalidIndex() )
	{
		++s_UniqueVS[nIndex];
	}
	else
	{
		int nMemUsed = 23 * 1024;
		s_UniqueVS.Insert( crc, 1 );
		VPROF_INCREMENT_GROUP_COUNTER( "unique vs count", COUNTER_GROUP_NO_RESET, 1 );
		VPROF_INCREMENT_GROUP_COUNTER( "vs driver mem", COUNTER_GROUP_NO_RESET, nMemUsed );
		VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, nMemUsed );
	}
#endif
}

static void RegisterPS( const void* pShaderBits, int nShaderSize, IDirect3DPixelShader9* pShader )
{
#ifdef MEASURE_DRIVER_ALLOCATIONS
	CRC32_t crc;
	CRC32_Init( &crc );
	CRC32_ProcessBuffer( &crc, pShaderBits, nShaderSize );
	CRC32_Final( &crc );

	s_PSLookup.Insert( pShader, crc );

	int nIndex = s_UniquePS.Find( crc );
	if ( nIndex != s_UniquePS.InvalidIndex() )
	{
		++s_UniquePS[nIndex];
	}
	else
	{
		int nMemUsed = 400;
		s_UniquePS.Insert( crc, 1 );
		VPROF_INCREMENT_GROUP_COUNTER( "unique ps count", COUNTER_GROUP_NO_RESET, 1 );
		VPROF_INCREMENT_GROUP_COUNTER( "ps driver mem", COUNTER_GROUP_NO_RESET, nMemUsed );
		VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, nMemUsed );
	}
#endif
}

static void UnregisterVS( IDirect3DVertexShader9* pShader )
{
#ifdef MEASURE_DRIVER_ALLOCATIONS
	int nCRCIndex = s_VSLookup.Find( pShader );
	if ( nCRCIndex == s_VSLookup.InvalidIndex() )
		return;

	CRC32_t crc = s_VSLookup[nCRCIndex];
	s_VSLookup.RemoveAt( nCRCIndex );

	int nIndex = s_UniqueVS.Find( crc );
	if ( nIndex != s_UniqueVS.InvalidIndex() )
	{
		if ( --s_UniqueVS[nIndex] <= 0 )
		{
			int nMemUsed = 23 * 1024;
			VPROF_INCREMENT_GROUP_COUNTER( "unique vs count", COUNTER_GROUP_NO_RESET, -1 );
			VPROF_INCREMENT_GROUP_COUNTER( "vs driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed );
			VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed );
			s_UniqueVS.Remove( nIndex );
		}
	}
#endif
}

static void UnregisterPS( IDirect3DPixelShader9* pShader )
{
#ifdef MEASURE_DRIVER_ALLOCATIONS
	int nCRCIndex = s_PSLookup.Find( pShader );
	if ( nCRCIndex == s_PSLookup.InvalidIndex() )
		return;

	CRC32_t crc = s_PSLookup[nCRCIndex];
	s_PSLookup.RemoveAt( nCRCIndex );

	int nIndex = s_UniquePS.Find( crc );
	if ( nIndex != s_UniquePS.InvalidIndex() )
	{
		if ( --s_UniquePS[nIndex] <= 0 )
		{
			int nMemUsed = 400;
			VPROF_INCREMENT_GROUP_COUNTER( "unique ps count", COUNTER_GROUP_NO_RESET, -1 );
			VPROF_INCREMENT_GROUP_COUNTER( "ps driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed );
			VPROF_INCREMENT_GROUP_COUNTER( "total driver mem", COUNTER_GROUP_NO_RESET, -nMemUsed );
			s_UniquePS.Remove( nIndex );
		}
	}
#endif
}

//-----------------------------------------------------------------------------
// The lovely low-level dx call to create a vertex shader
//-----------------------------------------------------------------------------
static HardwareShader_t CreateD3DVertexShader( DWORD *pByteCode, int numBytes, const char *pShaderName, char *debugLabel = NULL )
{
	MEM_ALLOC_D3D_CREDIT();

	if ( !pByteCode )
	{
		Assert( 0 );
		return INVALID_HARDWARE_SHADER;
	}

	// Compute the vertex specification
	HardwareShader_t hShader;

	#ifdef DX_TO_GL_ABSTRACTION	
		HRESULT hr = Dx9Device()->CreateVertexShader( pByteCode, (IDirect3DVertexShader9 **)&hShader, pShaderName, debugLabel );
	#else
		if ( IsEmulatingGL() )
		{
			DWORD dwVersion = D3DXGetShaderVersion(	pByteCode );
			REFERENCE( dwVersion );
			Assert ( D3DSHADER_VERSION_MAJOR( dwVersion ) == 2 );
		}

	#if defined(_X360) || !defined(DX_TO_GL_ABSTRACTION)
		HRESULT hr = Dx9Device()->CreateVertexShader( pByteCode, (IDirect3DVertexShader9 **)&hShader );
	#else
		HRESULT hr = Dx9Device()->CreateVertexShader( pByteCode, (IDirect3DVertexShader9 **)&hShader, pShaderName );
#endif

	#endif

	// NOTE: This isn't recorded before the CreateVertexShader because
	// we don't know the value of shader until after the CreateVertexShader.
	RECORD_COMMAND( DX8_CREATE_VERTEX_SHADER, 3 );
	RECORD_INT( ( int )hShader ); // hack hack hack
	RECORD_INT( numBytes );
	RECORD_STRUCT( pByteCode, numBytes );

	if ( FAILED( hr ) )
	{
		Assert( 0 );
		hShader = INVALID_HARDWARE_SHADER;
	}
	else
	{
		s_NumVertexShadersCreated++;
		RegisterVS( pByteCode, numBytes, (IDirect3DVertexShader9 *)hShader );
	}
	return hShader;
}

static void PatchPixelShaderForAtiMsaaHack(DWORD *pShader, DWORD dwTexCoordMask) 
{ 
	if ( IsPC() )
	{
		bool bIsSampler, bIsTexCoord; 
		
		// Should be able to patch only ps2.0 
		if (*pShader != 0xFFFF0200) 
			return; 
		
		pShader++; 
		
		while (pShader) 
		{ 
			switch (*pShader & D3DSI_OPCODE_MASK) 
			{ 
			case D3DSIO_COMMENT: 
				// Process comment 
				pShader = pShader + (*pShader >> 16) + 1; 
				break; 
				
			case D3DSIO_END: 
				// End of shader 
				return; 
				
			case D3DSIO_DCL: 
				bIsSampler = (*(pShader + 1) & D3DSP_TEXTURETYPE_MASK) != D3DSTT_UNKNOWN; 
				bIsTexCoord = (((*(pShader + 2) & D3DSP_REGTYPE_MASK) >> D3DSP_REGTYPE_SHIFT) + 
					((*(pShader + 2) & D3DSP_REGTYPE_MASK2) >> D3DSP_REGTYPE_SHIFT2)) == D3DSPR_TEXTURE; 
				
				if (!bIsSampler && bIsTexCoord) 
				{ 
					DWORD dwTexCoord = *(pShader + 2) & D3DSP_REGNUM_MASK; 
					DWORD mask = 0x01; 
					for (DWORD i = 0; i < 16; i++) 
					{ 
						if (((dwTexCoordMask & mask) == mask) && (dwTexCoord == i)) 
						{ 
							// If found -- patch and get out 
	//						*(pShader + 2) |= D3DSPDM_PARTIALPRECISION; 
							*(pShader + 2) |= D3DSPDM_MSAMPCENTROID; 
							break; 
						} 
						mask <<= 1; 
					} 
				} 
				// Intentionally fall through... 
				
			default: 
				// Skip instruction 
				pShader = pShader + ((*pShader & D3DSI_INSTLENGTH_MASK) >> D3DSI_INSTLENGTH_SHIFT) + 1; 
			} 
		}
	}
} 

static ConVar mat_force_ps_patch( "mat_force_ps_patch", "0" );
static ConVar mat_disable_ps_patch( "mat_disable_ps_patch", "0", FCVAR_ALLOWED_IN_COMPETITIVE );

//-----------------------------------------------------------------------------
// The lovely low-level dx call to create a pixel shader
//-----------------------------------------------------------------------------
static HardwareShader_t CreateD3DPixelShader( DWORD *pByteCode, unsigned int nCentroidMask, int numBytes, const char* pShaderName, char *debugLabel = NULL )
{
	MEM_ALLOC_D3D_CREDIT();
	
	if ( !pByteCode )
		return INVALID_HARDWARE_SHADER;

	if ( IsPC() && nCentroidMask && 
		( HardwareConfig()->NeedsATICentroidHack()  ||
		  mat_force_ps_patch.GetInt() ) )
	{
		if ( !mat_disable_ps_patch.GetInt() )
		{
			PatchPixelShaderForAtiMsaaHack( pByteCode, nCentroidMask );
		}
	}

	HardwareShader_t shader;
	#if defined( DX_TO_GL_ABSTRACTION ) 
		#if defined( OSX ) 
			HRESULT hr = Dx9Device()->CreatePixelShader( pByteCode, ( IDirect3DPixelShader ** )&shader, pShaderName, debugLabel );
		#else
			HRESULT hr = Dx9Device()->CreatePixelShader( pByteCode, ( IDirect3DPixelShader ** )&shader, pShaderName, debugLabel, &nCentroidMask );
		#endif
	#else
		if ( IsEmulatingGL() )
		{
			DWORD dwVersion;
			dwVersion = D3DXGetShaderVersion( pByteCode );
			Assert ( D3DSHADER_VERSION_MAJOR( dwVersion ) == 2 );
		}
#if defined(_X360) || !defined(DX_TO_GL_ABSTRACTION)
		HRESULT hr = Dx9Device()->CreatePixelShader( pByteCode, ( IDirect3DPixelShader ** )&shader );
#else
		HRESULT hr = Dx9Device()->CreatePixelShader( pByteCode, ( IDirect3DPixelShader ** )&shader, pShaderName );
	#endif
	#endif
	
	// NOTE: We have to do this after creating the pixel shader since we don't know
	// lookup.m_PixelShader yet!!!!!!!
	RECORD_COMMAND( DX8_CREATE_PIXEL_SHADER, 3 );
	RECORD_INT( ( int )shader );  // hack hack hack
	RECORD_INT( numBytes );
	RECORD_STRUCT( pByteCode, numBytes );
	
	if ( FAILED( hr ) )
	{
		Assert(0);
		shader = INVALID_HARDWARE_SHADER;
	}
	else
	{
		s_NumPixelShadersCreated++;
		RegisterPS( pByteCode, numBytes, ( IDirect3DPixelShader9* )shader );
	}

	return shader;
}

template<class T> int BinarySearchCombos( uint32 nStaticComboID, int nCombos, T const *pRecords )
{
	// Use binary search - data is sorted
	int nLowerIdx = 1;
	int nUpperIdx = nCombos;
	for (;;)
	{
		if ( nUpperIdx < nLowerIdx )
			return -1;

		int nMiddleIndex = ( nLowerIdx + nUpperIdx ) / 2;
		uint32 nProbe = pRecords[nMiddleIndex-1].m_nStaticComboID;
		if ( nStaticComboID < nProbe )
		{
			nUpperIdx = nMiddleIndex - 1;
		}
		else
		{
			if ( nStaticComboID > nProbe )
				nLowerIdx = nMiddleIndex + 1;
			else
				return nMiddleIndex - 1;
		}
	}
}

inline int FindShaderStaticCombo( uint32 nStaticComboID, const ShaderHeader_t& header, StaticComboRecord_t *pRecords )
{
	if ( header.m_nVersion < 5 )
		return -1;

	return BinarySearchCombos( nStaticComboID, header.m_nNumStaticCombos, pRecords );
}

// cache redundant i/o fetched components of the vcs files
struct ShaderFileCache_t
{
	CUtlSymbol			m_Name;
	CUtlSymbol			m_Filename;
	ShaderHeader_t		m_Header;
	bool				m_bVertexShader;

	// valid for diff version only - contains the microcode used as the reference for diff algorithm
	CUtlBuffer			m_ReferenceCombo;

	// valid for ver5 only - contains the directory
	CUtlVector< StaticComboRecord_t >	m_StaticComboRecords;
	CUtlVector< StaticComboAliasRecord_t > m_StaticComboDupRecords;

	ShaderFileCache_t()
	{
		// invalid until version established
		m_Header.m_nVersion = 0;
	}

	bool IsValid() const
	{
		return m_Header.m_nVersion != 0;
	}

	bool IsOldVersion() const
	{
		return m_Header.m_nVersion < 5;
	}

	int IsVersion6() const
	{
		return ( m_Header.m_nVersion == 6 );
	}

	int FindCombo( uint32 nStaticComboID )
	{
		int nSearchAliases = BinarySearchCombos( nStaticComboID, m_StaticComboDupRecords.Count(), m_StaticComboDupRecords.Base() );
		if ( nSearchAliases != -1 )
			nStaticComboID = m_StaticComboDupRecords[nSearchAliases].m_nSourceStaticCombo;
		return FindShaderStaticCombo( nStaticComboID, m_Header, m_StaticComboRecords.Base() );
	}

	bool operator==( const ShaderFileCache_t& a ) const
	{
		return m_Name == a.m_Name && m_bVertexShader == a.m_bVertexShader;
	}
};


//-----------------------------------------------------------------------------
// Vertex + pixel shader manager
//-----------------------------------------------------------------------------
class CShaderManager : public IShaderManager
{
public:
	CShaderManager();
	virtual ~CShaderManager();

	// Methods of IShaderManager
	virtual void				Init();
	virtual void				Shutdown();
	virtual IShaderBuffer		*CompileShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion );
	virtual VertexShaderHandle_t CreateVertexShader( IShaderBuffer* pShaderBuffer );
	virtual void				DestroyVertexShader( VertexShaderHandle_t hShader );
	virtual PixelShaderHandle_t CreatePixelShader( IShaderBuffer* pShaderBuffer );
	virtual void				DestroyPixelShader( PixelShaderHandle_t hShader );
	virtual VertexShader_t		CreateVertexShader( const char *pVertexShaderFile, int nStaticVshIndex = 0, char *debugLabel = NULL );
	virtual PixelShader_t		CreatePixelShader( const char *pPixelShaderFile, int nStaticPshIndex = 0, char *debugLabel = NULL );
	virtual void				SetVertexShader( VertexShader_t shader );
	virtual void				SetPixelShader( PixelShader_t shader );
	virtual void				BindVertexShader( VertexShaderHandle_t shader );
	virtual void				BindPixelShader( PixelShaderHandle_t shader );
	virtual void				*GetCurrentVertexShader();
	virtual void				*GetCurrentPixelShader();
	virtual void				ResetShaderState();
	void						FlushShaders();
	virtual void				ClearVertexAndPixelShaderRefCounts();
	virtual void				PurgeUnusedVertexAndPixelShaders();
	void						SpewVertexAndPixelShaders();
	const char					*GetActiveVertexShaderName();
	const char					*GetActivePixelShaderName();
	bool						CreateDynamicCombos_Ver4( void *pContext, uint8 *pComboBuffer );
	bool						CreateDynamicCombos_Ver5( void *pContext, uint8 *pComboBuffer, char *debugLabel = NULL );

#if defined( DX_TO_GL_ABSTRACTION )
	virtual void				DoStartupShaderPreloading();
#endif

	static void					QueuedLoaderCallback( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError );

private:
	typedef CUtlFixedLinkedList< IDirect3DVertexShader9* >::IndexType_t VertexShaderIndex_t;
	typedef CUtlFixedLinkedList< IDirect3DPixelShader9* >::IndexType_t PixelShaderIndex_t;
	
	struct ShaderStaticCombos_t
	{
		int					m_nCount;

		// Can't use CUtlVector here since you CUtlLinkedList<CUtlVector<>> doesn't work.
		HardwareShader_t	*m_pHardwareShaders;
		struct ShaderCreationData_t
		{
			CUtlVector<uint8> 	ByteCode;
			uint32 				iCentroidMask;
		};

		ShaderCreationData_t *m_pCreationData;
	};
	
	struct ShaderLookup_t
	{
		CUtlSymbol				m_Name;
		int						m_nStaticIndex;
		ShaderStaticCombos_t	m_ShaderStaticCombos;
		DWORD					m_Flags;
		int						m_nRefCount;
		uintp			m_hShaderFileCache;

		// for queued loading, bias an aligned optimal buffer forward to correct location
		int						m_nDataOffset;

		// diff version, valid during load only
		ShaderDictionaryEntry_t	*m_pComboDictionary;

		ShaderLookup_t()
		{
			m_Flags = 0;
			m_nRefCount = 0;
			m_ShaderStaticCombos.m_nCount = 0;
			m_ShaderStaticCombos.m_pHardwareShaders = 0;
			m_ShaderStaticCombos.m_pCreationData = 0;
			m_pComboDictionary = NULL;
		}
		void IncRefCount()
		{
			m_nRefCount++;
		}
		bool operator==( const ShaderLookup_t& a ) const
		{
			return m_Name == a.m_Name && m_nStaticIndex == a.m_nStaticIndex;
		}
	};

#ifdef DYNAMIC_SHADER_COMPILE
	struct Combo_t
	{
		CUtlSymbol m_ComboName;
		int m_nMin;
		int m_nMax;
	};

	struct ShaderCombos_t
	{
		CUtlVector<Combo_t> m_StaticCombos;
		CUtlVector<Combo_t> m_DynamicCombos;
		int GetNumDynamicCombos( void ) const
		{
			int combos = 1;
			int i;
			for( i = 0; i < m_DynamicCombos.Count(); i++ )
			{
				combos *= ( m_DynamicCombos[i].m_nMax - m_DynamicCombos[i].m_nMin + 1 );
			}
			return combos;
		}
		int GetNumStaticCombos( void ) const
		{
			int combos = 1;
			int i;
			for( i = 0; i < m_StaticCombos.Count(); i++ )
			{
				combos *= ( m_StaticCombos[i].m_nMax - m_StaticCombos[i].m_nMin + 1 );
			}
			return combos;
		}
	};
#endif

private:
	void					CreateStaticShaders();
	void					DestroyStaticShaders();

#if defined ( DYNAMIC_SHADER_COMPILE ) && defined( REMOTE_DYNAMIC_SHADER_COMPILE )
	void					InitRemoteShaderCompile();
	void					DeinitRemoteShaderCompile();
#endif

	// The low-level dx call to set the vertex shader state
	void					SetVertexShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader = DC_INVALID_HANDLE );

	// The low-level dx call to set the pixel shader state
	void					SetPixelShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader = DC_INVALID_HANDLE );

	// Destroys all shaders
	void					DestroyAllShaders();

	// Destroy a particular vertex shader
	void					DestroyVertexShader( VertexShader_t shader );
	// Destroy a particular pixel shader
	void					DestroyPixelShader( PixelShader_t shader );

	bool					LoadAndCreateShaders( ShaderLookup_t &lookup, bool bVertexShader, char *debugLabel = NULL );
	FileHandle_t			OpenFileAndLoadHeader( const char *pFileName, ShaderHeader_t *pHeader );

#ifdef DYNAMIC_SHADER_COMPILE
	bool					LoadAndCreateShaders_Dynamic( ShaderLookup_t &lookup, bool bVertexShader );
	const ShaderCombos_t	*FindOrCreateShaderCombos( const char *pShaderName );
	HardwareShader_t		CompileShader( const char *pShaderName, int nStaticIndex, int nDynamicIndex, bool bVertexShader );
#endif

	void					DisassembleShader( ShaderLookup_t *pLookup, int dynamicCombo, uint8 *pByteCode );
	void					WriteTranslatedFile( ShaderLookup_t *pLookup, int dynamicCombo, char *pFileContents, char *pFileExtension );

	// DX_TO_GL_ABSTRACTION only, no-op otherwise
	
	void					SaveShaderCache( char *cacheName );	// query GLM pair cache for all active shader pairs and write them to disk in named file
	bool					LoadShaderCache( char *cacheName );	// read named file, establish compiled shader sets for each vertex+static and pixel+static, then link pairs as listed in table
																// return true on success, false if file not found

	// old void					WarmShaderCache();

	CUtlFixedLinkedList< ShaderLookup_t > m_VertexShaderDict;
	CUtlFixedLinkedList< ShaderLookup_t > m_PixelShaderDict;

	CUtlSymbolTable m_ShaderSymbolTable;

#ifdef DYNAMIC_SHADER_COMPILE	
	typedef HRESULT (__stdcall *ShaderCompileFromFileFunc_t)( LPCSTR pSrcFile, CONST D3DXMACRO* pDefines,
		LPD3DXINCLUDE pInclude,	LPCSTR pFunctionName, LPCSTR pProfile, DWORD Flags,
		LPD3DXBUFFER* ppShader, LPD3DXBUFFER * ppErrorMsgs,	LPD3DXCONSTANTTABLE * ppConstantTable );
	CUtlStringMap<ShaderCombos_t>	 m_ShaderNameToCombos;
	CSysModule						*m_pShaderCompiler30;
	ShaderCompileFromFileFunc_t		m_ShaderCompileFileFunc30;
#endif
	
	// The current vertex and pixel shader
	HardwareShader_t	m_HardwareVertexShader;
	HardwareShader_t	m_HardwarePixelShader;

	CUtlFixedLinkedList< IDirect3DVertexShader9* > m_RawVertexShaderDict;
	CUtlFixedLinkedList< IDirect3DPixelShader9* > m_RawPixelShaderDict;

	CUtlFixedLinkedList< ShaderFileCache_t > m_ShaderFileCache;

	// false, creates during init.
	// true, creates on access, helps reduce d3d memory for tools, but causes i/o hitches.
	bool m_bCreateShadersOnDemand;

#if defined( _DEBUG )
	// for debugging (can't resolve UtlSym)
	// need some history because 360 d3d has rips related to sequencing
	char	vshDebugName[MAX_SHADER_HISTORY][64];
	int		vshDebugIndex;
	char	pshDebugName[MAX_SHADER_HISTORY][64];
	int		pshDebugIndex;
#endif

#if defined ( DYNAMIC_SHADER_COMPILE ) && defined( REMOTE_DYNAMIC_SHADER_COMPILE )
	SOCKET m_RemoteShaderCompileSocket;
#endif

};


//-----------------------------------------------------------------------------
// Singleton accessor
//-----------------------------------------------------------------------------
static CShaderManager s_ShaderManager;
IShaderManager *g_pShaderManager = &s_ShaderManager;

//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CShaderManager::CShaderManager() : 
	m_ShaderSymbolTable( 0, 32, true /* caseInsensitive */ ),
	m_VertexShaderDict( 32 ),
	m_PixelShaderDict( 32 ),
	m_ShaderFileCache( 32 )
{
	m_bCreateShadersOnDemand = false;

#ifdef DYNAMIC_SHADER_COMPILE
	m_pShaderCompiler30 = 0;
	m_ShaderCompileFileFunc30 = 0;
#ifdef REMOTE_DYNAMIC_SHADER_COMPILE
	m_RemoteShaderCompileSocket = INVALID_SOCKET;
#endif
#endif

#ifdef _DEBUG
	vshDebugIndex = 0;
	pshDebugIndex = 0;
#endif
}

CShaderManager::~CShaderManager()
{
#if defined ( DYNAMIC_SHADER_COMPILE ) && defined( REMOTE_DYNAMIC_SHADER_COMPILE )
	DeinitRemoteShaderCompile();
#endif
}

#define REMOTE_SHADER_COMPILE_PORT "20000"

#if defined ( DYNAMIC_SHADER_COMPILE ) && defined( REMOTE_DYNAMIC_SHADER_COMPILE )
void CShaderManager::InitRemoteShaderCompile()
{
	DeinitRemoteShaderCompile();
	
	int nResult = 0;
	#ifdef _WIN32	
		WSADATA wsaData;
		nResult = WSAStartup( 0x101, &wsaData );
		if ( nResult != 0 )
		{
			Warning( "CShaderManager::Init - Could not init socket for remote dynamic shader compilation\n" );
		}
	#endif

	struct addrinfo hints;
	ZeroMemory( &hints, sizeof(hints) );
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;

	// Resolve the server address and port
	struct addrinfo *result = NULL;
	nResult = getaddrinfo( mat_remoteshadercompile.GetString(), REMOTE_SHADER_COMPILE_PORT, &hints, &result );
	if ( nResult != 0 )
	{
		Warning( "getaddrinfo failed: %d\n", nResult );
		#ifdef _WIN32
			WSACleanup();
		#endif
		Assert( 0 );
	}

	// Attempt to connect to an address until one succeeds
	for( struct addrinfo *ptr = result; ptr != NULL; ptr = ptr->ai_next )
	{
		// Create a SOCKET for connecting to remote shader compilation server
		m_RemoteShaderCompileSocket = socket( ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol );
		if ( m_RemoteShaderCompileSocket == INVALID_SOCKET )
		{
			Warning( "Error at socket(): %ld\n", WSAGetLastError() );
			freeaddrinfo( result );
			#ifdef _WIN32
				WSACleanup();
			#endif
			Assert( 0 );
			continue;
		}

		// Connect to server.
		nResult = connect( m_RemoteShaderCompileSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
		if ( nResult == SOCKET_ERROR )
		{
			closesocket( m_RemoteShaderCompileSocket );
			m_RemoteShaderCompileSocket = INVALID_SOCKET;
			continue;
		}
		break;
	}
	
	freeaddrinfo( result );

	if ( m_RemoteShaderCompileSocket == INVALID_SOCKET )
	{
		Warning( "Unable to connect to remote shader compilation server!\n" );
		#ifdef _WIN32
			WSACleanup();
		#endif
		Assert ( 0 );
	}
}

void CShaderManager::DeinitRemoteShaderCompile()
{
	if ( m_RemoteShaderCompileSocket != INVALID_SOCKET )
	{
		if ( shutdown( m_RemoteShaderCompileSocket, SD_SEND ) == SOCKET_ERROR )
		{
			Warning( "Remote shader compilation shutdown failed: %d\n", WSAGetLastError() );
		}
		closesocket( m_RemoteShaderCompileSocket );
		m_RemoteShaderCompileSocket = INVALID_SOCKET;
	}
}
#endif // defined ( DYNAMIC_SHADER_COMPILE ) && defined( REMOTE_DYNAMIC_SHADER_COMPILE )

//-----------------------------------------------------------------------------
// Initialization, shutdown
//-----------------------------------------------------------------------------
void CShaderManager::Init()
{
	// incompatible with the 360, violates loading system
	// only used by PC to help tools reduce d3d footprint
	m_bCreateShadersOnDemand = IsPC() && ( ShaderUtil()->InEditorMode() || CommandLine()->CheckParm( "-shadersondemand" ) );

#ifdef DYNAMIC_SHADER_COMPILE
	if( !IsX360() )
	{

#ifdef REMOTE_DYNAMIC_SHADER_COMPILE
	InitRemoteShaderCompile();
#else // REMOTE_DYNAMIC_SHADER_COMPILE

#ifdef _DEBUG
	m_pShaderCompiler30 = Sys_LoadModule( "d3dx9d_33.dll" );
#endif
	if (!m_pShaderCompiler30)
	{
		m_pShaderCompiler30 = Sys_LoadModule( "d3dx9_33.dll" );
	}

	if ( m_pShaderCompiler30 )
	{
		m_ShaderCompileFileFunc30 = (ShaderCompileFromFileFunc_t)GetProcAddress( (HMODULE)m_pShaderCompiler30, "D3DXCompileShaderFromFileA" );
	}

#endif

	}
#endif // DYNAMIC_SHADER_COMPILE

	CreateStaticShaders();
}

void CShaderManager::Shutdown()
{
#ifdef DYNAMIC_SHADER_COMPILE
	if ( m_pShaderCompiler30 )
	{
		Sys_UnloadModule( m_pShaderCompiler30 );
		m_pShaderCompiler30 = 0;
		m_ShaderCompileFileFunc30 = 0;
	}
#endif

#if defined (DX_TO_GL_ABSTRACTION) && !defined (ANDROID)

	if (mat_autosave_glshaders.GetInt())
	{
		SaveShaderCache("glshaders.cfg");
	}
#endif

	DestroyAllShaders();
	DestroyStaticShaders();
}


//-----------------------------------------------------------------------------
// Compiles shaders
//-----------------------------------------------------------------------------
IShaderBuffer *CShaderManager::CompileShader( const char *pProgram, size_t nBufLen, const char *pShaderVersion )
{
	int nCompileFlags = D3DXSHADER_AVOID_FLOW_CONTROL;

#ifdef _DEBUG
	nCompileFlags |= D3DXSHADER_DEBUG;
#endif

	LPD3DXBUFFER pCompiledShader, pErrorMessages;
	HRESULT hr = D3DXCompileShader( pProgram, nBufLen,
		NULL, NULL, "main", pShaderVersion, nCompileFlags, 
		&pCompiledShader, &pErrorMessages, NULL );

	if ( FAILED( hr ) )
	{
		if ( pErrorMessages )
		{
			const char *pErrorMessage = (const char *)pErrorMessages->GetBufferPointer();
			Warning( "Shader compilation failed! Reported the following errors:\n%s\n", pErrorMessage );
			pErrorMessages->Release();
		}
		return NULL;
	}

	// NOTE: This uses small block heap allocator; so I'm not going
	// to bother creating a memory pool.
	CShaderBuffer< ID3DXBuffer > *pShaderBuffer = new CShaderBuffer< ID3DXBuffer >( pCompiledShader );
	if ( pErrorMessages )
	{
		pErrorMessages->Release();
	}

	return pShaderBuffer;
}


VertexShaderHandle_t CShaderManager::CreateVertexShader( IShaderBuffer* pShaderBuffer )
{
	// Create the vertex shader
	IDirect3DVertexShader9 *pVertexShader = NULL;

#if defined(_X360) || !defined(DX_TO_GL_ABSTRACTION)
	HRESULT hr = Dx9Device()->CreateVertexShader( (const DWORD*)pShaderBuffer->GetBits(), &pVertexShader );
#else
	HRESULT hr = Dx9Device()->CreateVertexShader( (const DWORD*)pShaderBuffer->GetBits(), &pVertexShader, NULL );
#endif

	if ( FAILED( hr ) || !pVertexShader )
		return VERTEX_SHADER_HANDLE_INVALID;

	s_NumVertexShadersCreated++;
	RegisterVS( pShaderBuffer->GetBits(), pShaderBuffer->GetSize(), pVertexShader );

	// Insert the shader into the dictionary of shaders
	VertexShaderIndex_t i = m_RawVertexShaderDict.AddToTail( pVertexShader );
	return (VertexShaderHandle_t)i;
}

void CShaderManager::DestroyVertexShader( VertexShaderHandle_t hShader )
{
	if ( hShader == VERTEX_SHADER_HANDLE_INVALID )
		return;

	VertexShaderIndex_t i = (VertexShaderIndex_t)(uintp)hShader;
	IDirect3DVertexShader9 *pVertexShader = m_RawVertexShaderDict[ i ];

	UnregisterVS( pVertexShader );

	VerifyEquals( (int)pVertexShader->Release(), 0 );
	m_RawVertexShaderDict.Remove( i );
}

PixelShaderHandle_t CShaderManager::CreatePixelShader( IShaderBuffer* pShaderBuffer )
{
	// Create the vertex shader
	IDirect3DPixelShader9 *pPixelShader = NULL;
#if defined(_X360) || !defined(DX_TO_GL_ABSTRACTION)
	HRESULT hr = Dx9Device()->CreatePixelShader( (const DWORD*)pShaderBuffer->GetBits(), &pPixelShader );
#else
	HRESULT hr = Dx9Device()->CreatePixelShader( (const DWORD*)pShaderBuffer->GetBits(), &pPixelShader, NULL );
#endif

	if ( FAILED( hr ) || !pPixelShader )
		return PIXEL_SHADER_HANDLE_INVALID;

	s_NumPixelShadersCreated++;

	RegisterPS( pShaderBuffer->GetBits(), pShaderBuffer->GetSize(), pPixelShader );

	// Insert the shader into the dictionary of shaders
	PixelShaderIndex_t i = m_RawPixelShaderDict.AddToTail( pPixelShader );
	return (PixelShaderHandle_t)i;
}

void CShaderManager::DestroyPixelShader( PixelShaderHandle_t hShader )
{
	if ( hShader == PIXEL_SHADER_HANDLE_INVALID )
		return;

	PixelShaderIndex_t i = (PixelShaderIndex_t)(uintp)hShader;
	IDirect3DPixelShader9 *pPixelShader = m_RawPixelShaderDict[ i ];

	UnregisterPS( pPixelShader );

	VerifyEquals( (int)pPixelShader->Release(), 0 );
	m_RawPixelShaderDict.Remove( i );
}


//-----------------------------------------------------------------------------
// Globals
//-----------------------------------------------------------------------------
HardwareShader_t s_pIllegalMaterialPS = INVALID_HARDWARE_SHADER;

//-----------------------------------------------------------------------------
// Static methods
//-----------------------------------------------------------------------------
void CShaderManager::CreateStaticShaders()
{
	MEM_ALLOC_D3D_CREDIT();

	if ( !HardwareConfig()->SupportsVertexAndPixelShaders() )
	{
		return;
	}

	if ( IsPC() )
	{
		// GR - hack for illegal materials
		const DWORD psIllegalMaterial[] =
		{
			#ifdef DX_TO_GL_ABSTRACTION
				// Use a PS 2.0 binary shader on DX_TO_GL_ABSTRACTION
				0xffff0200, 0x05000051, 0xa00f0000, 0x3f800000,
				0x00000000, 0x3f800000, 0x3f800000, 0x02000001,
				0x800f0000, 0xa0e40000, 0x02000001, 0x800f0800,
				0x80e40000, 0x0000ffff
			#else
				0xffff0101, 0x00000051, 0xa00f0000, 0x00000000, 0x3f800000, 0x00000000, 
				0x3f800000, 0x00000001, 0x800f0000, 0xa0e40000, 0x0000ffff
			#endif
		};
		// create default shader
#if defined(_X360) || !defined(DX_TO_GL_ABSTRACTION)
		Dx9Device()->CreatePixelShader( psIllegalMaterial, ( IDirect3DPixelShader9 ** )&s_pIllegalMaterialPS );
#else
		Dx9Device()->CreatePixelShader( psIllegalMaterial, ( IDirect3DPixelShader9 ** )&s_pIllegalMaterialPS, NULL );
#endif
	}
}

void CShaderManager::DestroyStaticShaders()
{
	// GR - invalid material hack
	// destroy internal shader
	if ( s_pIllegalMaterialPS != INVALID_HARDWARE_SHADER )
	{
		( ( IDirect3DPixelShader9 * )s_pIllegalMaterialPS )->Release();
		s_pIllegalMaterialPS = INVALID_HARDWARE_SHADER;
	}
}

#ifdef DYNAMIC_SHADER_COMPILE
static const char *GetShaderSourcePath( void )
{
	static char shaderDir[MAX_PATH];
	// GR - just in case init this...
	static bool bHaveShaderDir = false;
	if( !bHaveShaderDir )
	{
		bHaveShaderDir = true;
#		if ( defined( DYNAMIC_SHADER_COMPILE_CUSTOM_PATH ) )
		{
			Q_strncpy( shaderDir, DYNAMIC_SHADER_COMPILE_CUSTOM_PATH, MAX_PATH );
		}
#		else
		{
#			if ( defined( _X360 ) )
			{
				char hostName[128] = "";
				const char *pHostName = CommandLine()->ParmValue( "-host" );
				if ( !pHostName )
				{
					// the 360 machine name must be <HostPC>_360
					DWORD length = sizeof( hostName );
					DmGetXboxName( hostName, &length );
					char *p = strstr( hostName, "_360" );
					*p = '\0';
					pHostName = hostName;
				}

				Q_snprintf( shaderDir, MAX_PATH, "net:\\smb\\%s\\stdshaders", pHostName );
			}
#			else
			{
				Q_strncpy( shaderDir, __FILE__, MAX_PATH );
				Q_StripFilename( shaderDir );
				Q_StripLastDir( shaderDir, MAX_PATH );
				Q_strncat( shaderDir, "stdshaders", MAX_PATH, COPY_ALL_CHARACTERS );
			}
#			endif
		}
#		endif
	}
	return shaderDir;
}
#endif

#ifdef DYNAMIC_SHADER_COMPILE
const CShaderManager::ShaderCombos_t *CShaderManager::FindOrCreateShaderCombos( const char *pShaderName )
{
	if( m_ShaderNameToCombos.Defined( pShaderName ) )
	{
		return &m_ShaderNameToCombos[pShaderName];
	}
	ShaderCombos_t &combos = m_ShaderNameToCombos[pShaderName];
	char filename[MAX_PATH];
	// try the vsh dir first.
	Q_strncpy( filename, GetShaderSourcePath(), MAX_PATH );
	Q_strncat( filename, "\\", MAX_PATH, COPY_ALL_CHARACTERS );
	Q_strncat( filename, pShaderName, MAX_PATH, COPY_ALL_CHARACTERS );
	Q_strncat( filename, ".vsh", MAX_PATH, COPY_ALL_CHARACTERS );
	CUtlInplaceBuffer bffr( 0, 0, CUtlInplaceBuffer::TEXT_BUFFER );
	
	bool bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr );
	
	if ( bOpenResult )
	{
		NULL;
	}
	else
	{
		// try the fxc dir.
		Q_strncpy( filename, GetShaderSourcePath(), MAX_PATH );
		Q_strncat( filename, "\\", MAX_PATH, COPY_ALL_CHARACTERS );
		Q_strncat( filename, pShaderName, MAX_PATH, COPY_ALL_CHARACTERS );
		Q_strncat( filename, ".fxc", MAX_PATH, COPY_ALL_CHARACTERS );
		bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr );

		if ( !bOpenResult )
		{
			// Maybe this is a specific version [20 & 20b] -> [2x]
			if ( Q_strlen( pShaderName ) >= 3 )
			{
				char *pszEndFilename = filename + strlen( filename );
				if ( !Q_stricmp( pszEndFilename - 6, "30.fxc" ) )
				{
					// Total hack. Who knows what builds that 30 shader?
					strcpy( pszEndFilename - 6, "20b.fxc" );
					bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr );
					if ( !bOpenResult )
					{
						strcpy( pszEndFilename - 6, "2x.fxc" );
						bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr );
					}
					if ( !bOpenResult )
					{
						strcpy( pszEndFilename - 6, "20.fxc" );
						bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr );
					}
				}
				else
				{
					if ( !stricmp( pszEndFilename - 6, "20.fxc" ) )
					{
						pszEndFilename[ -5 ] = 'x';
					}
					else if ( !stricmp( pszEndFilename - 7, "20b.fxc" ) )
					{
						strcpy( pszEndFilename - 7, "2x.fxc" );
						--pszEndFilename;
					}
					else if ( !stricmp( pszEndFilename - 6, "11.fxc" ) )
					{
						strcpy( pszEndFilename - 6, "xx.fxc" );
					}

					bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr );
					if ( !bOpenResult )
					{
						if ( !stricmp( pszEndFilename - 6, "2x.fxc" ) )
						{
							pszEndFilename[ -6 ] = 'x';
							bOpenResult = g_pFullFileSystem->ReadFile( filename, NULL, bffr );
						}
					}
				}
			}
		}

		if ( !bOpenResult )
		{
			Assert( 0 );
			return NULL;
		}
	}

	while( char *line = bffr.InplaceGetLinePtr() )
	{
		// dear god perl is better at this kind of shit!
		int begin = 0;
		int end = 0;

		// check if the line starts with '//'
		if( line[0] != '/' || line[1] != '/' )
		{
			continue;
		}

		// Check if line intended for platform lines
		if( IsX360() )
		{
			if ( Q_stristr( line, "[PC]" ) )
				continue;
		}
		else
		{
			if ( Q_stristr( line, "[360]" ) || Q_stristr( line, "[XBOX]" ) )
				continue;
		}

		// Skip any lines intended for other shader version
		if ( Q_stristr( pShaderName, "_ps20" ) && !Q_stristr( pShaderName, "_ps20b" ) &&
			 Q_stristr( line, "[ps" ) && !Q_stristr( line, "[ps20]" ) )
			 continue;
		if ( Q_stristr( pShaderName, "_ps20b" ) &&
			 Q_stristr( line, "[ps" ) && !Q_stristr( line, "[ps20b]" ) )
			 continue;
		if ( Q_stristr( pShaderName, "_ps30" ) &&
			Q_stristr( line, "[ps" ) &&	 !Q_stristr( line, "[ps30]" ) )
			continue;
		if ( Q_stristr( pShaderName, "_vs20" ) &&
			Q_stristr( line, "[vs" ) &&	 !Q_stristr( line, "[vs20]" ) )
			continue;
		if ( Q_stristr( pShaderName, "_vs30" ) &&
			Q_stristr( line, "[vs" ) &&	 !Q_stristr( line, "[vs30]" ) )
			continue;

		char *pScan = &line[2];
		while( *pScan == ' ' || *pScan == '\t' )
		{
			pScan++;
		}

		bool bDynamic;
		if( Q_strncmp( pScan, "DYNAMIC", 7 ) == 0 )
		{
			bDynamic = true;
			pScan += 7;
		}
		else if( Q_strncmp( pScan, "STATIC", 6 ) == 0 )
		{
			bDynamic = false;
			pScan += 6;
		}
		else
		{
			continue;
		}

		// skip whitespace
		while( *pScan == ' ' || *pScan == '\t' )
		{
			pScan++;
		}

		// check for colon
		if( *pScan != ':' )
		{
			continue;
		}
		pScan++;

		// skip whitespace
		while( *pScan == ' ' || *pScan == '\t' )
		{
			pScan++;
		}

		// check for quote
		if( *pScan != '\"' )
		{
			continue;
		}
		pScan++;

		char *pBeginningOfName = pScan;
		while( 1 )
		{
			if( *pScan == '\0' )
			{
				break;
			}
			if( *pScan == '\"' )
			{
				break;
			}
			pScan++;
		}

		if( *pScan == '\0' )
		{
			continue;
		}

		// must have hit a quote. .done with string.
		// slam a NULL at the end quote of the string so that we have the string at pBeginningOfName.
		*pScan = '\0';
		pScan++;

		// skip whitespace
		while( *pScan == ' ' || *pScan == '\t' )
		{
			pScan++;
		}

		// check for quote
		if( *pScan != '\"' )
		{
			continue;
		}
		pScan++;

		// make sure that we have a number after the quote.
		if( !isdigit( *pScan ) )
		{
			continue;
		}

		while( isdigit( *pScan ) )
		{
			begin = begin * 10 + ( *pScan - '0' );
			pScan++;
		}

		if( pScan[0] != '.' || pScan[1] != '.' )
		{
			continue;
		}
		pScan += 2;

		// make sure that we have a number
		if( !isdigit( *pScan ) )
		{
			continue;
		}

		while( isdigit( *pScan ) )
		{
			end = end * 10 + ( *pScan - '0' );
			pScan++;
		}

		if( pScan[0] != '\"' )
		{
			continue;
		}

		// sweet freaking jesus. .done parsing the line.
//		char buf[1024];
//		sprintf( buf, "\"%s\" \"%s\" %d %d\n", bDynamic ? "DYNAMIC" : "STATIC", pBeginningOfName, begin, end );
//		Plat_DebugString( buf );

		Combo_t *pCombo = NULL;
		if( bDynamic )
		{
			pCombo = &combos.m_DynamicCombos[combos.m_DynamicCombos.AddToTail()];
		}
		else
		{
			pCombo = &combos.m_StaticCombos[combos.m_StaticCombos.AddToTail()];
		}

		pCombo->m_ComboName = m_ShaderSymbolTable.AddString( pBeginningOfName );
		pCombo->m_nMin = begin;
		pCombo->m_nMax = end;
	}
	
	return &combos;
}
#endif // DYNAMIC_SHADER_COMPILE

#ifdef DYNAMIC_SHADER_COMPILE
#ifndef DX_TO_GL_ABSTRACTION
//-----------------------------------------------------------------------------
// Used to deal with include files
//-----------------------------------------------------------------------------
class CDxInclude : public ID3DXInclude
{
public:
	CDxInclude( const char *pMainFileName );

#if defined( _X360 )
	virtual HRESULT WINAPI Open( D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID * ppData, UINT * pBytes, LPSTR pFullPath, DWORD cbFullPath );
#else
	STDMETHOD(Open)(THIS_ D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes);
#endif

	STDMETHOD(Close)(THIS_ LPCVOID pData);

private:
	char m_pBasePath[MAX_PATH];
	
#if defined( _X360 )
	char m_pFullPath[MAX_PATH];
#endif
};

CDxInclude::CDxInclude( const char *pMainFileName )
{
	Q_ExtractFilePath( pMainFileName, m_pBasePath, sizeof(m_pBasePath) );
}


#if defined( _X360 )
HRESULT CDxInclude::Open( D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID * ppData, UINT * pBytes, LPSTR pFullPath, DWORD cbFullPath )
#else
HRESULT CDxInclude::Open( D3DXINCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID * ppData, UINT * pBytes )
#endif
{
	char pTemp[MAX_PATH];
	if ( !Q_IsAbsolutePath( pFileName ) && ( IncludeType == D3DXINC_LOCAL ) )
	{
		Q_ComposeFileName( m_pBasePath, pFileName, pTemp, sizeof(pTemp) );
		pFileName = pTemp;
	}

	CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER );
	if ( !g_pFullFileSystem->ReadFile( pFileName, NULL, buf ) )
		return E_FAIL;

	*pBytes = buf.TellMaxPut();
	void *pMem = malloc( *pBytes );
	memcpy( pMem, buf.Base(), *pBytes );
	*ppData = pMem;

#	if ( defined( _X360 ) )
	{
		Q_ComposeFileName( m_pBasePath, pFileName, m_pFullPath, sizeof(m_pFullPath) );
		pFullPath = m_pFullPath;
		cbFullPath = MAX_PATH;
	}
#	endif

	return S_OK;
}

HRESULT CDxInclude::Close( LPCVOID pData )
{
	void *pMem = const_cast<void*>( pData );
	free( pMem );
	return S_OK;
}
#endif // not DX_TO_GL_ABSTRACTION

static const char *FileNameToShaderModel( const char *pShaderName, bool bVertexShader )
{
	// Figure out the shader model
	const char *pShaderModel = NULL;
	if( bVertexShader )
	{
		if( Q_stristr( pShaderName, "vs20" ) )
		{
			pShaderModel = "vs_2_0";
			bVertexShader = true;
		}
		else if( Q_stristr( pShaderName, "vs11" ) )
		{
			pShaderModel = "vs_1_1";
			bVertexShader = true;
		}
		else if( Q_stristr( pShaderName, "vs14" ) )
		{
			pShaderModel = "vs_1_1";
			bVertexShader = true;
		}
		else if( Q_stristr( pShaderName, "vs30" ) )
		{
			pShaderModel = "vs_3_0";
			bVertexShader = true;
		}
		else
		{
#ifdef _DEBUG
			Error( "Failed dynamic shader compiled\nBuild shaderapidx9.dll in debug to find problem\n" );
#else
			Assert( 0 );
#endif
		}
	}
	else
	{
		if( Q_stristr( pShaderName, "ps20b" ) )
		{
			pShaderModel = "ps_2_b";
		}
		else if( Q_stristr( pShaderName, "ps20" ) )
		{
			pShaderModel = "ps_2_0";
		}
		else if( Q_stristr( pShaderName, "ps11" ) )
		{
			pShaderModel = "ps_1_1";
		}
		else if( Q_stristr( pShaderName, "ps14" ) )
		{
			pShaderModel = "ps_1_4";
		}
		else if( Q_stristr( pShaderName, "ps30" ) )
		{
			pShaderModel = "ps_3_0";
		}
		else
		{
#ifdef _DEBUG
			Error( "Failed dynamic shader compiled\nBuild shaderapidx9.dll in debug to find problem\n" );
#else
			Assert( 0 );
#endif
		}
	}
	return pShaderModel;
}
#endif

#ifdef DYNAMIC_SHADER_COMPILE

#if defined( _X360 )
static ConVar mat_flushshaders_generate_updbs( "mat_flushshaders_generate_updbs", "0", 0, "Generates UPDBs whenever you flush shaders." );
#endif

HardwareShader_t CShaderManager::CompileShader( const char *pShaderName, 
												int nStaticIndex, int nDynamicIndex, bool bVertexShader )
{
	VPROF_BUDGET( "CompileShader", "CompileShader" );
	Assert( m_ShaderNameToCombos.Defined( pShaderName ) );
	if( !m_ShaderNameToCombos.Defined( pShaderName ) )
	{
		return INVALID_HARDWARE_SHADER;
	}
	const ShaderCombos_t &combos = m_ShaderNameToCombos[pShaderName];
#ifdef _DEBUG
	int numStaticCombos = combos.GetNumStaticCombos();
	int numDynamicCombos = combos.GetNumDynamicCombos();
#endif
	Assert( nStaticIndex % numDynamicCombos == 0 );
	Assert( ( nStaticIndex % numDynamicCombos ) >= 0 && ( nStaticIndex % numDynamicCombos ) < numStaticCombos );
	Assert( nDynamicIndex >= 0 && nDynamicIndex < numDynamicCombos );

#	ifdef DYNAMIC_SHADER_COMPILE_VERBOSE

	//Warning( "Compiling %s %s\n\tdynamic:", bVertexShader ? "vsh" : "psh", pShaderName );
	Warning( "Compiling " );
	if ( bVertexShader )
		ConColorMsg( Color( 0, 255, 0, 255 ), "vsh - %s ", pShaderName );
	else
		ConColorMsg( Color( 0, 255, 255, 255 ), "psh - %s ", pShaderName );
	Warning( "\n\tdynamic:" );

#	endif

	CUtlVector<D3DXMACRO> macros;
	// plus 1 for null termination, plus 1 for #define SHADER_MODEL_*, and plus 1 for #define _X360 on 360
	macros.SetCount( combos.m_DynamicCombos.Count() + combos.m_StaticCombos.Count() + 2 + ( IsX360() ? 1 : 0 ) );

	int nCombo = nStaticIndex + nDynamicIndex;
	int macroIndex = 0;
	int i;
	for( i = 0; i < combos.m_DynamicCombos.Count(); i++ )
	{
		int countForCombo = combos.m_DynamicCombos[i].m_nMax - combos.m_DynamicCombos[i].m_nMin + 1;
		int val = nCombo % countForCombo + combos.m_DynamicCombos[i].m_nMin;
		nCombo /= countForCombo;
		macros[macroIndex].Name = m_ShaderSymbolTable.String( combos.m_DynamicCombos[i].m_ComboName );
		char buf[16];
		sprintf( buf, "%d", val );
		CUtlSymbol valSymbol( buf );
		macros[macroIndex].Definition = valSymbol.String();
#	ifdef DYNAMIC_SHADER_COMPILE_VERBOSE
		Warning( " %s=%s", macros[macroIndex].Name, macros[macroIndex].Definition );
#	endif
		macroIndex++;
	}

#	ifdef DYNAMIC_SHADER_COMPILE_VERBOSE
	Warning( "\n\tstatic:" );
#	endif
	for( i = 0; i < combos.m_StaticCombos.Count(); i++ )
	{
		int countForCombo = combos.m_StaticCombos[i].m_nMax - combos.m_StaticCombos[i].m_nMin + 1;
		int val = nCombo % countForCombo + combos.m_StaticCombos[i].m_nMin;
		nCombo /= countForCombo;
		macros[macroIndex].Name = m_ShaderSymbolTable.String( combos.m_StaticCombos[i].m_ComboName );
		char buf[16];
		sprintf( buf, "%d", val );
		CUtlSymbol valSymbol( buf );
		macros[macroIndex].Definition = valSymbol.String();
#	ifdef DYNAMIC_SHADER_COMPILE_VERBOSE
		Warning( " %s=%s", macros[macroIndex].Name, macros[macroIndex].Definition );
#	endif
		macroIndex++;
	}

#	ifdef DYNAMIC_SHADER_COMPILE_VERBOSE
	Warning( "\n" );
#	endif

	char filename[MAX_PATH];
	Q_strncpy( filename, GetShaderSourcePath(), MAX_PATH );
	Q_strncat( filename, "\\", MAX_PATH, COPY_ALL_CHARACTERS );
	Q_strncat( filename, pShaderName, MAX_PATH, COPY_ALL_CHARACTERS );
	Q_strncat( filename, ".fxc", MAX_PATH, COPY_ALL_CHARACTERS );
	
	const char *pShaderModel = FileNameToShaderModel( pShaderName, bVertexShader );
	
	// define the shader model
	char shaderModelDefineString[1024];
	Q_snprintf( shaderModelDefineString, 1024, "SHADER_MODEL_%s", pShaderModel );
	Q_strupr( shaderModelDefineString );
	macros[macroIndex].Name = shaderModelDefineString;
	macros[macroIndex].Definition = "1";
	macroIndex++;

	char x360DefineString[1024];
	if( IsX360() )
	{
		Q_snprintf( x360DefineString, 1024, "_X360", pShaderModel );
		Q_strupr( x360DefineString );
		macros[macroIndex].Name = x360DefineString;
		macros[macroIndex].Definition = "1";
		macroIndex++;
	}

	// NULL terminate.
	macros[macroIndex].Name = NULL;
	macros[macroIndex].Definition = NULL;

	// Instead of erroring out, infinite-loop on shader compilation
	// (i.e. give developers a chance to fix the shader code w/out restarting the game)
#ifndef _DEBUG
	int retriesLeft = 20;
retry_compile:
#endif

	// Try and open the file to see if it exists
	FileHandle_t fp = g_pFullFileSystem->Open( filename, "r" );

	if ( fp == FILESYSTEM_INVALID_HANDLE )
	{
		// Maybe this is a specific version [20 & 20b] -> [2x]
		if ( strlen( pShaderName ) >= 3 )
		{
			char *pszEndFilename = filename + strlen( filename );
			if ( !Q_stricmp( pszEndFilename - 6, "30.fxc" ) )
			{
				strcpy( pszEndFilename - 6, "20b.fxc" );
				fp = g_pFullFileSystem->Open( filename, "r" );
				if ( fp == FILESYSTEM_INVALID_HANDLE )
				{
					strcpy( pszEndFilename - 6, "2x.fxc" );
					fp = g_pFullFileSystem->Open( filename, "r" );
				}
				if ( fp == FILESYSTEM_INVALID_HANDLE )
				{
					strcpy( pszEndFilename - 6, "20.fxc" );
					fp = g_pFullFileSystem->Open( filename, "r" );
				}
			}
			else
			{
				if ( !Q_stricmp( pszEndFilename - 6, "20.fxc" ) )
				{
					pszEndFilename[ -5 ] = 'x';
					fp = g_pFullFileSystem->Open( filename, "r" );
				}
				else if ( !Q_stricmp( pszEndFilename - 7, "20b.fxc" ) )
				{
					strcpy( pszEndFilename - 7, "2x.fxc" );
					fp = g_pFullFileSystem->Open( filename, "r" );
				}
				else if ( !stricmp( pszEndFilename - 6, "11.fxc" ) )
				{
					strcpy( pszEndFilename - 6, "xx.fxc" );
					fp = g_pFullFileSystem->Open( filename, "r" );
				}

				if ( fp == FILESYSTEM_INVALID_HANDLE )
				{
					if ( !stricmp( pszEndFilename - 6, "2x.fxc" ) )
					{
						pszEndFilename[ -6 ] = 'x';
						fp = g_pFullFileSystem->Open( filename, "r" );
					}
				}
			}
		}
	}

	if ( fp != FILESYSTEM_INVALID_HANDLE )
	{
		g_pFullFileSystem->Close( fp );
	}

#ifdef REMOTE_DYNAMIC_SHADER_COMPILE
	#define SEND_BUF_SIZE 40000
	#define RECV_BUF_SIZE 40000

	// Remotely-compiled shader code
	uint32 *pRemotelyCompiledShader = NULL;
	uint32 nRemotelyCompiledShaderLength = 0;

	if ( m_RemoteShaderCompileSocket == INVALID_SOCKET )
	{
		InitRemoteShaderCompile();
	}

	// In this case, we're going to use a remote service to do our compiling
	if ( m_RemoteShaderCompileSocket != INVALID_SOCKET )
	{
		// Build up command list for remote shader compiler
		char pSendbuf[SEND_BUF_SIZE], pRecvbuf[RECV_BUF_SIZE], pFixedFilename[MAX_PATH], buf[MAX_PATH];
		V_FixupPathName( pFixedFilename, MAX_PATH, filename );
		V_FileBase( pFixedFilename, buf, MAX_PATH ); // Just find base filename
		V_strncat( buf, ".fxc", MAX_PATH );
		V_snprintf( pSendbuf, SEND_BUF_SIZE, "%s\n", buf );
		V_strncat( pSendbuf, pShaderModel, SEND_BUF_SIZE );
		V_strncat( pSendbuf, "\n", SEND_BUF_SIZE );
		V_snprintf( buf, MAX_PATH, "%d\n", macros.Count() );
		V_strncat( pSendbuf, buf, SEND_BUF_SIZE );
		for ( int i=0; i < macros.Count(); i++ )
		{
			V_snprintf( buf, MAX_PATH, "%s\n%s\n", macros[i].Name, macros[i].Definition );
			V_strncat( pSendbuf, buf, SEND_BUF_SIZE );
		}
		V_strncat( pSendbuf, "", SEND_BUF_SIZE );

		// Send commands to remote shader compiler
		int nResult = send( m_RemoteShaderCompileSocket, pSendbuf, (int)strlen( pSendbuf ), 0 );
		if ( nResult == SOCKET_ERROR )
		{
			Warning( "send failed: %d\n", WSAGetLastError() );
			DeinitRemoteShaderCompile();
		}

		if ( m_RemoteShaderCompileSocket != INVALID_SOCKET )
		{
			// Block here until we get a result back from the server
			nResult = recv( m_RemoteShaderCompileSocket, pRecvbuf, RECV_BUF_SIZE, 0 );
			if ( nResult == 0 )
			{
				Warning( "Connection closed\n" );
				DeinitRemoteShaderCompile();
			}
			else if ( nResult < 0 )
			{
				Warning( "recv failed: %d\n", WSAGetLastError() );
				DeinitRemoteShaderCompile();
			}

			if ( m_RemoteShaderCompileSocket != INVALID_SOCKET )
			{
				// Grab the first 32 bits, which tell us what the rest of the data is
				uint32 nCompileResultCode;
				memcpy( &nCompileResultCode, pRecvbuf, sizeof( nCompileResultCode ) );

				// If is zero, we have an error, so the rest of the data is a text string from the compiler
				if ( nCompileResultCode == 0x00000000 )
				{
					Warning( "Remote shader compile error: %s\n", pRecvbuf+4 );
				}
				else // we have an actual binary shader blob coming back
				{
					nRemotelyCompiledShaderLength = nCompileResultCode;
					pRemotelyCompiledShader = (uint32 *) pRecvbuf;
					pRemotelyCompiledShader++;
				}
			}
		}
	} // End using remote compile service
#endif // REMOTE_DYNAMIC_SHADER_COMPILE

#if defined( DYNAMIC_SHADER_COMPILE )
	bool bShadersNeedFlush = false;
#endif

#if defined( DYNAMIC_SHADER_COMPILE ) && !defined( REMOTE_DYNAMIC_SHADER_COMPILE )
	LPD3DXBUFFER pShader = NULL;
	LPD3DXBUFFER pErrorMessages = NULL;
	HRESULT hr = S_OK;
	bool b30Shader = !Q_stricmp( pShaderModel, "vs_3_0" ) || !Q_stricmp( pShaderModel, "ps_3_0" );

	if ( m_ShaderCompileFileFunc30 && b30Shader )
	{
		CDxInclude dxInclude( filename );
		hr = m_ShaderCompileFileFunc30( filename, macros.Base(), &dxInclude,
			"main",	pShaderModel, 0 /* DWORD Flags */, 	&pShader, &pErrorMessages, NULL /* LPD3DXCONSTANTTABLE *ppConstantTable */ );
	}
	else
	{
#		if ( !defined( _X360 ) )
		{
			if ( b30Shader )
			{
				Warning( "Compiling with a stale version of d3dx. Should have d3d9x_33.dll installed (Apr 2007)\n" );
			}
			hr = D3DXCompileShaderFromFile( filename, macros.Base(), NULL /* LPD3DXINCLUDE */,
				"main",	pShaderModel, 0 /* DWORD Flags */, 	&pShader, &pErrorMessages, NULL /* LPD3DXCONSTANTTABLE *ppConstantTable */ );


#ifdef REMOTE_DYNAMIC_SHADER_COMPILE
			// If we're using the remote compiling service, let's double-check against a local compile
			if ( ( m_RemoteShaderCompileSocket != INVALID_SOCKET ) && pRemotelyCompiledShader )
			{
				if ( ( memcmp( pRemotelyCompiledShader, pShader->GetBufferPointer(), pShader->GetBufferSize() ) != 0 ) ||
					( pShader->GetBufferSize() != nRemotelyCompiledShaderLength) )
				{
					Warning( "Remote and local shaders don't match!\n" );
					return INVALID_HARDWARE_SHADER;
				}
			}
#endif // REMOTE_DYNAMIC_SHADER_COMPILE

		}
#		else
		{
			D3DXSHADER_COMPILE_PARAMETERS compileParams;
			memset( &compileParams, 0, sizeof( compileParams ) );
			
			char pUPDBOutputFile[MAX_PATH] = ""; //where we write the file
			char pUPDBPIXLookup[MAX_PATH] = ""; //where PIX (on a pc) looks for the file

			compileParams.Flags |= D3DXSHADEREX_OPTIMIZE_UCODE;

			if( mat_flushshaders_generate_updbs.GetBool() )
			{
				//UPDB generation for PIX debugging
				compileParams.Flags |= D3DXSHADEREX_GENERATE_UPDB;
				compileParams.UPDBPath = pUPDBPIXLookup;

				Q_snprintf( pUPDBOutputFile, MAX_PATH, "%s\\UPDB_X360\\%s_S%d_D%d.updb", GetShaderSourcePath(), pShaderName, nStaticIndex, nDynamicIndex );
			
				//replace "net:\smb" with another "\" turning the xbox network address format into the pc network address format
				V_strcpy_safe( pUPDBPIXLookup, &pUPDBOutputFile[7] );
				pUPDBPIXLookup[0] = '\\';
			}

			hr = D3DXCompileShaderFromFileEx( filename, macros.Base(), NULL /* LPD3DXINCLUDE */,
				"main",	pShaderModel, 0 /* DWORD Flags */, 	&pShader, &pErrorMessages, NULL /* LPD3DXCONSTANTTABLE *ppConstantTable */, &compileParams );
		
			if( (pUPDBOutputFile[0] != '\0') && compileParams.pUPDBBuffer ) //Did we generate a updb?
			{
				CUtlBuffer outbuffer;
				DWORD dataSize = compileParams.pUPDBBuffer->GetBufferSize();
				outbuffer.EnsureCapacity( dataSize );
				memcpy( outbuffer.Base(), compileParams.pUPDBBuffer->GetBufferPointer(), dataSize );
				outbuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, dataSize );				
				g_pFullFileSystem->WriteFile( pUPDBOutputFile, NULL, outbuffer );

				compileParams.pUPDBBuffer->Release();
			}
		}
#		endif		
	}

	if ( hr != D3D_OK )
	{
		const char *pErrorMessageString = ( const char * )pErrorMessages->GetBufferPointer();
		Plat_DebugString( pErrorMessageString );
		Plat_DebugString( "\n" );

#ifndef _DEBUG
		if ( retriesLeft-- > 0 )
		{
			DevMsg( 0, "Failed dynamic shader compiled - fix the shader while the debugger is at the breakpoint, then continue\n" );
			DebuggerBreakIfDebugging();
#if defined( DYNAMIC_SHADER_COMPILE )
			bShadersNeedFlush = true;
#endif
			goto retry_compile;
		}
		if( !IsX360() ) //errors make the 360 puke and die. We have a better solution for this particular error
			Error( "Failed dynamic shader compile\nBuild shaderapidx9.dll in debug to find problem\n" );
#else
		Assert( 0 );

#endif // _DEBUG

		return INVALID_HARDWARE_SHADER;
	}
	else
#endif // #if defined( DYNAMIC_SHADER_COMPILE ) && !defined( REMOTE_DYNAMIC_SHADER_COMPILE )
		
	{
#ifdef DYNAMIC_SHADER_COMPILE_WRITE_ASSEMBLY
		// enable to dump the disassembly for shader validation
		char exampleCommandLine[2048];
		Q_strncpy( exampleCommandLine, "// Run from stdshaders\n// ..\\..\\dx9sdk\\utilities\\fxc.exe ", sizeof( exampleCommandLine ) );
		int i;
		for( i = 0; macros[i].Name; i++ )
		{
			Q_strncat( exampleCommandLine, "/D", sizeof( exampleCommandLine ) );
			Q_strncat( exampleCommandLine, macros[i].Name, sizeof( exampleCommandLine ) );
			Q_strncat( exampleCommandLine, "=", sizeof( exampleCommandLine ) );
			Q_strncat( exampleCommandLine, macros[i].Definition, sizeof( exampleCommandLine ) );
			Q_strncat( exampleCommandLine, " ", sizeof( exampleCommandLine ) );
		}

		Q_strncat( exampleCommandLine, "/T", sizeof( exampleCommandLine ) );
		Q_strncat( exampleCommandLine, pShaderModel, sizeof( exampleCommandLine ) );
		Q_strncat( exampleCommandLine, " ", sizeof( exampleCommandLine ) );
		Q_strncat( exampleCommandLine, filename, sizeof( exampleCommandLine ) );
		Q_strncat( exampleCommandLine, "\n", sizeof( exampleCommandLine ) );

		ID3DXBuffer *pd3dxBuffer;
		HRESULT hr;
		hr = D3DXDisassembleShader( ( DWORD* )pShader->GetBufferPointer(), false, NULL, &pd3dxBuffer );
		Assert( hr == D3D_OK );
		CUtlBuffer tempBuffer;
		tempBuffer.SetBufferType( true, false );
		int exampleCommandLineLength = strlen( exampleCommandLine );
		tempBuffer.EnsureCapacity( pd3dxBuffer->GetBufferSize() + exampleCommandLineLength );
		memcpy( tempBuffer.Base(), exampleCommandLine, exampleCommandLineLength );
		memcpy( ( char * )tempBuffer.Base() + exampleCommandLineLength, pd3dxBuffer->GetBufferPointer(), pd3dxBuffer->GetBufferSize() );
		tempBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, pd3dxBuffer->GetBufferSize() + exampleCommandLineLength );
		char filename[MAX_PATH];
		sprintf( filename, "%s_%d_%d.asm", pShaderName, nStaticIndex, nDynamicIndex );
		g_pFullFileSystem->WriteFile( filename, "DEFAULT_WRITE_PATH", tempBuffer );
#endif

#ifdef REMOTE_DYNAMIC_SHADER_COMPILE
		if ( bVertexShader )
		{
			return CreateD3DVertexShader( ( DWORD * )pRemotelyCompiledShader, nRemotelyCompiledShaderLength, pShaderName );
		}
		else
		{
			return CreateD3DPixelShader( ( DWORD * )pRemotelyCompiledShader, 0, nRemotelyCompiledShaderLength, pShaderName ); // hack hack hack!  need to get centroid info from the source
		}
#else // local compile, not remote
		if ( bVertexShader )
		{
			return CreateD3DVertexShader( ( DWORD * )pShader->GetBufferPointer(), pShader->GetBufferSize(), pShaderName );
		}
		else
		{
			return CreateD3DPixelShader( ( DWORD * )pShader->GetBufferPointer(), 0, pShader->GetBufferSize(), pShaderName ); // hack hack hack!  need to get centroid info from the source
		}
#endif

#if defined( DYNAMIC_SHADER_COMPILE )
		// We keep up with whether we hit a compile error above.  If we did, then we likely need to recompile everything again since we could have changed global code.
		if ( bShadersNeedFlush )
		{
			MatFlushShaders();
		}
#endif
	}

#ifndef REMOTE_DYNAMIC_SHADER_COMPILE	
	if ( pShader )
	{
		pShader->Release();
	}
#endif

#if defined( DYNAMIC_SHADER_COMPILE ) && !defined( REMOTE_DYNAMIC_SHADER_COMPILE )
	if ( pErrorMessages )
	{
		pErrorMessages->Release();
	}
#endif
	
}
#endif

#ifdef DYNAMIC_SHADER_COMPILE	
bool CShaderManager::LoadAndCreateShaders_Dynamic( ShaderLookup_t &lookup, bool bVertexShader )
{	
	const char *pName = m_ShaderSymbolTable.String( lookup.m_Name );
	const ShaderCombos_t *pCombos = FindOrCreateShaderCombos( pName );
	if ( !pCombos )
	{
		return false;
	}

	int numDynamicCombos = pCombos->GetNumDynamicCombos();
	lookup.m_ShaderStaticCombos.m_pHardwareShaders = new HardwareShader_t[numDynamicCombos];
	lookup.m_ShaderStaticCombos.m_nCount = numDynamicCombos;
	lookup.m_ShaderStaticCombos.m_pCreationData = new ShaderStaticCombos_t::ShaderCreationData_t[numDynamicCombos];

	int i;
	for( i = 0; i < numDynamicCombos; i++ )
	{
		lookup.m_ShaderStaticCombos.m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER;
	}
	return true;
}
#endif

//-----------------------------------------------------------------------------
// Open the shader file, optionally gets the header
//-----------------------------------------------------------------------------
FileHandle_t CShaderManager::OpenFileAndLoadHeader( const char *pFileName, ShaderHeader_t *pHeader )
{
	FileHandle_t fp = g_pFullFileSystem->Open( pFileName, "rb", "GAME" );
	if ( fp == FILESYSTEM_INVALID_HANDLE )
	{
		return FILESYSTEM_INVALID_HANDLE;
	}

	if ( pHeader )
	{
		// read the header 
		g_pFullFileSystem->Read( pHeader, sizeof( ShaderHeader_t ), fp );

		switch ( pHeader->m_nVersion )
		{
			case 4:
				// version with combos done as diffs vs a reference combo
				// vsh/psh or older fxc
				break;

			case 5:
			case 6:
				// version with optimal dictionary and compressed combo block
				break;

			default:
				Assert( 0 );
				Warning( "Shader %s is the wrong version %d, expecting %d\n", pFileName, pHeader->m_nVersion, SHADER_VCS_VERSION_NUMBER );
				g_pFullFileSystem->Close( fp );
				return FILESYSTEM_INVALID_HANDLE;
		}
	}

	return fp;
}

//---------------------------------------------------------------------------------------------------------
// Writes text files named for looked-up shaders.  Used by GL shader translator to dump code for debugging
//---------------------------------------------------------------------------------------------------------
void CShaderManager::WriteTranslatedFile( ShaderLookup_t *pLookup, int dynamicCombo, char *pFileContents, char *pFileExtension )
{
	const char *pName = m_ShaderSymbolTable.String( pLookup->m_Name );
	int nNumChars = V_strlen( pFileContents );

	CUtlBuffer tempBuffer;
	tempBuffer.SetBufferType( true, false );
	tempBuffer.EnsureCapacity( nNumChars );
	memcpy( ( char * )tempBuffer.Base(), pFileContents, nNumChars );
	tempBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, nNumChars );

	char filename[MAX_PATH];
	sprintf( filename, "%s_%d_%d.%s", pName, pLookup->m_nStaticIndex, dynamicCombo, pFileExtension );
	g_pFullFileSystem->WriteFile( filename, "DEFAULT_WRITE_PATH", tempBuffer );
}

//-----------------------------------------------------------------------------
// Disassemble a shader for debugging. Writes .asm files.
//-----------------------------------------------------------------------------
void CShaderManager::DisassembleShader( ShaderLookup_t *pLookup, int dynamicCombo, uint8 *pByteCode )
{
#if defined( WRITE_ASSEMBLY )
	const char *pName = m_ShaderSymbolTable.String( pLookup->m_Name );

	ID3DXBuffer *pd3dxBuffer;
	HRESULT hr;
	hr = D3DXDisassembleShader( (DWORD*)pByteCode, false, NULL, &pd3dxBuffer );
	Assert( hr == D3D_OK );

	CUtlBuffer tempBuffer;
	tempBuffer.SetBufferType( true, false );
	tempBuffer.EnsureCapacity( pd3dxBuffer->GetBufferSize() );
	memcpy( ( char * )tempBuffer.Base(), pd3dxBuffer->GetBufferPointer(), pd3dxBuffer->GetBufferSize() );
	tempBuffer.SeekPut( CUtlBuffer::SEEK_CURRENT, pd3dxBuffer->GetBufferSize() );

	char filename[MAX_PATH];
	sprintf( filename, "%s_%d_%d.asm", pName, pLookup->m_nStaticIndex, dynamicCombo );
	g_pFullFileSystem->WriteFile( filename, "DEFAULT_WRITE_PATH", tempBuffer );
#endif
}

//-----------------------------------------------------------------------------
// Create dynamic combos
//-----------------------------------------------------------------------------
bool CShaderManager::CreateDynamicCombos_Ver4( void *pContext, uint8 *pComboBuffer )
{
	ShaderLookup_t* pLookup = (ShaderLookup_t *)pContext;

	ShaderFileCache_t *pFileCache = &m_ShaderFileCache[pLookup->m_hShaderFileCache];
	ShaderHeader_t *pHeader = &pFileCache->m_Header;

	int nReferenceComboSizeForDiffs = ((ShaderHeader_t_v4 *)pHeader)->m_nDiffReferenceSize;

	uint8 *pReferenceShader = NULL;
	uint8 *pDiffOutputBuffer = NULL;
	if ( nReferenceComboSizeForDiffs )
	{
		// reference combo is *always* the largest combo, so safe worst case size for uncompression buffer
		pReferenceShader = (uint8 *)pFileCache->m_ReferenceCombo.Base();
		pDiffOutputBuffer = (uint8 *)_alloca( nReferenceComboSizeForDiffs ); 
	}

	// build this shader's dynamic combos
	bool bOK = true;
	int nStartingOffset = 0;
	for ( int i = 0; i < pHeader->m_nDynamicCombos; i++ )
	{
		if ( pLookup->m_pComboDictionary[i].m_Offset == -1 )
		{
			// skipped
			continue;
		}

		if ( !nStartingOffset )
		{
			nStartingOffset = pLookup->m_pComboDictionary[i].m_Offset;
		}

		// offsets better be sequentially ascending
		Assert( nStartingOffset <= pLookup->m_pComboDictionary[i].m_Offset );

		if ( pLookup->m_pComboDictionary[i].m_Size <= 0 )
		{
			// skipped
			continue;
		}

		// get the right byte code from the monolithic buffer
		uint8 *pByteCode = (uint8 *)pComboBuffer + pLookup->m_nDataOffset + pLookup->m_pComboDictionary[i].m_Offset - nStartingOffset;
		int nByteCodeSize = pLookup->m_pComboDictionary[i].m_Size;

		if ( pReferenceShader )
		{
			// reference combo better be the largest combo, otherwise memory corruption
			Assert( nReferenceComboSizeForDiffs >= nByteCodeSize );

			// use the differencing algorithm to recover the full shader
			int nOriginalSize;
			ApplyDiffs( 
				pReferenceShader, 
				pByteCode,
				nReferenceComboSizeForDiffs,
				nByteCodeSize,
				nOriginalSize,
				pDiffOutputBuffer,
				nReferenceComboSizeForDiffs );

			pByteCode = pDiffOutputBuffer;
			nByteCodeSize = nOriginalSize;
		}

#if defined( WRITE_ASSEMBLY )
		DisassembleShader( pLookup, i, pByteCode );
#endif
		HardwareShader_t hardwareShader = INVALID_HARDWARE_SHADER;

		if ( IsPC() && m_bCreateShadersOnDemand )
		{
			// cache the code off for later
			pLookup->m_ShaderStaticCombos.m_pCreationData[i].ByteCode.SetSize( nByteCodeSize );
			V_memcpy( pLookup->m_ShaderStaticCombos.m_pCreationData[i].ByteCode.Base(), pByteCode, nByteCodeSize );
			pLookup->m_ShaderStaticCombos.m_pCreationData[i].iCentroidMask = pFileCache->m_bVertexShader ? 0 : pHeader->m_nCentroidMask;
		}
		else
		{
			const char *pShaderName = m_ShaderSymbolTable.String( pLookup->m_Name );
			if ( pFileCache->m_bVertexShader )
			{
				hardwareShader = CreateD3DVertexShader( reinterpret_cast< DWORD *>( pByteCode ), nByteCodeSize, pShaderName );
			}
			else
			{
				hardwareShader = CreateD3DPixelShader( reinterpret_cast< DWORD *>( pByteCode ), pHeader->m_nCentroidMask, nByteCodeSize, pShaderName );
			}

			if ( hardwareShader == INVALID_HARDWARE_SHADER )
			{
				Assert( 0 );
				bOK = false;
				break;
			}
		}
		pLookup->m_ShaderStaticCombos.m_pHardwareShaders[i] = hardwareShader;
	}

	delete [] pLookup->m_pComboDictionary;
	pLookup->m_pComboDictionary = NULL;

	return bOK;
}

//-----------------------------------------------------------------------------
// Create dynamic combos
//-----------------------------------------------------------------------------
static uint32 NextULONG( uint8 * &pData )
{
	// handle unaligned read
	uint32 nRet;
	memcpy( &nRet, pData, sizeof( nRet ) );
	pData += sizeof( nRet );
	return nRet;
}


bool CShaderManager::CreateDynamicCombos_Ver5( void *pContext, uint8 *pComboBuffer, char *debugLabel )
{
	ShaderLookup_t* pLookup = (ShaderLookup_t *)pContext;
	ShaderFileCache_t *pFileCache = &m_ShaderFileCache[pLookup->m_hShaderFileCache];
	uint8 *pCompressedShaders = pComboBuffer + pLookup->m_nDataOffset;

	uint8 *pUnpackBuffer = new uint8[MAX_SHADER_UNPACKED_BLOCK_SIZE];

	char *debugLabelPtr = debugLabel;	// can be moved to point at something else if need be
	
	// now, loop through all blocks
	bool bOK = true;
	while ( bOK )
	{
		uint32 nBlockSize = NextULONG( pCompressedShaders );
		if ( nBlockSize == 0xffffffff )	
		{
			// any more blocks?
			break;
		}

		switch( nBlockSize  & 0xc0000000 )
		{
			case 0:											// bzip2
			{
				// uncompress
				uint32 nOutsize = MAX_SHADER_UNPACKED_BLOCK_SIZE;
				int nRslt = BZ2_bzBuffToBuffDecompress( 
					reinterpret_cast<char *>( pUnpackBuffer ),
					&nOutsize,
					reinterpret_cast<char *>( pCompressedShaders ),
					nBlockSize, 1, 0 );
				if ( nRslt < 0 )
				{
					// errors are negative for bzip
					Assert( 0 );
					Warning( "BZIP Error (%d) decompressing shader", nRslt );
					bOK = false;
				}
				
				pCompressedShaders += nBlockSize;
				nBlockSize = nOutsize;		// how much data there is
			}
			break;

			case 0x80000000:								// uncompressed
			{
				// not compressed, as is
				nBlockSize &= 0x3fffffff;
				memcpy( pUnpackBuffer, pCompressedShaders, nBlockSize );
				pCompressedShaders += nBlockSize;
			}
			break;

			case 0x40000000:								// lzma compressed
			{
				nBlockSize &= 0x3fffffff;

				size_t nOutsize = CLZMA::Uncompress(
					reinterpret_cast<uint8 *>( pCompressedShaders ),
					pUnpackBuffer );
				pCompressedShaders += nBlockSize;
				nBlockSize = nOutsize;		// how much data there is
			}
			break;
			
			default:
			{
				Assert( 0 );
				Error(" unrecognized shader compression type = file corrupt?");
				bOK = false;
			}
		}
		
		uint8 *pReadPtr = pUnpackBuffer;
		while ( pReadPtr < pUnpackBuffer+nBlockSize )
		{
			uint32 nCombo_ID = NextULONG( pReadPtr );
			uint32 nShaderSize = NextULONG( pReadPtr );
			
#if defined( WRITE_ASSEMBLY )
			DisassembleShader( pLookup, nCombo_ID, pReadPtr );
#endif
			HardwareShader_t hardwareShader = INVALID_HARDWARE_SHADER;

			int iIndex = nCombo_ID;
			if ( iIndex >= pLookup->m_nStaticIndex )
				iIndex -= pLookup->m_nStaticIndex;			// ver5 stores combos as full combo, ver6 as dynamic combo # only
			if ( IsPC() && m_bCreateShadersOnDemand )
			{
				// cache the code off for later
				pLookup->m_ShaderStaticCombos.m_pCreationData[iIndex].ByteCode.SetSize( nShaderSize );
				V_memcpy( pLookup->m_ShaderStaticCombos.m_pCreationData[iIndex].ByteCode.Base(), pReadPtr, nShaderSize );
				pLookup->m_ShaderStaticCombos.m_pCreationData[iIndex].iCentroidMask = pFileCache->m_bVertexShader ? 0 : pFileCache->m_Header.m_nCentroidMask;
			}
			else
			{
				const char *pShaderName = m_ShaderSymbolTable.String( pLookup->m_Name );

				if ( pFileCache->m_bVertexShader )
				{
#if 0
					// this is all test code
					CUtlBuffer bufGLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER );
					CUtlBuffer bufNewGLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER );
					CUtlBuffer bufGLSLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER );
					bool bVertexShader;

					uint32 nOptions = 0;
					nOptions |= D3DToGL_OptionUseEnvParams;
					nOptions |= D3DToGL_OptionDoFixupZ;
					nOptions |= D3DToGL_OptionDoFixupY;					
					//options |= D3DToGL_OptionSpew;
					
//					sg_D3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, (char *)bufGLCode.Base(), bufGLCode.Size(), &bVertexShader, nOptions, -1, debugLabel );
//					sg_NewD3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, &bufNewGLCode, &bVertexShader, nOptions, -1, debugLabel );


//					bool bDumpGLSL = false;
//					if ( !stricmp( "vs-file vertexlit_and_unlit_generic_bump_vs20 vs-index 144", debugLabel ) && ( iIndex == 0 ) )
//					{
//						DisassembleShader( pLookup, iIndex, pReadPtr );									// Direct3D
//						bDumpGLSL = true;
//					}


					// GLSL options
					nOptions |= D3DToGL_OptionGLSL; // | D3DToGL_AddHexComments | D3DToGL_PutHexCommentsAfterLines;
					if ( !IsOSX() )
					{
						nOptions |= D3DToGL_OptionAllowStaticControlFlow;
					}
					sg_NewD3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, &bufGLSLCode, &bVertexShader, nOptions, -1, 0, debugLabel );
					Assert( bVertexShader );

					// Test to make sure these are identical
//					if ( bDumpGLSL )//V_strcmp( (char *)bufGLCode.Base(), (char *)bufNewGLCode.Base() ) )
//					{
//						WriteTranslatedFile( pLookup, iIndex, (char *)bufGLCode.Base(), "avp" );		// Old
//						WriteTranslatedFile( pLookup, iIndex, (char *)bufNewGLCode.Base(), "avp2" );	// New
						WriteTranslatedFile( pLookup, iIndex, (char *)bufGLSLCode.Base(), "glsl_v" );	// GLSL
//						DisassembleShader( pLookup, iIndex, pReadPtr );									// Direct3D
//					}

					#if defined( WRITE_ASSEMBLY )
						WriteTranslatedFile( pLookup, iIndex, (char *)bufGLCode.Base(), "avp" );
					#endif
#endif // 0

#ifdef DX_TO_GL_ABSTRACTION
					// munge the debug label a bit to aid in decoding... catenate the iIndex on the end
					char temp[1024];
					sprintf(temp, "%s vs-combo %d", (debugLabel)?debugLabel:"none", iIndex );
					debugLabelPtr = temp;
#endif
					// pass binary code to d3d interface, on GL it will invoke the translator back to asm
					hardwareShader = CreateD3DVertexShader( reinterpret_cast< DWORD *>( pReadPtr ), nShaderSize, pShaderName, debugLabelPtr );
				}
				else
				{
#if 0
					// this is all test code
//					CUtlBuffer bufGLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER );
//					CUtlBuffer bufNewGLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER );
					CUtlBuffer bufGLSLCode( 1000, 50000, CUtlBuffer::TEXT_BUFFER );
					bool bVertexShader;


					uint32 nOptions = D3DToGL_OptionUseEnvParams;

//					sg_D3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, (char *)bufGLCode.Base(), bufGLCode.Size(), &bVertexShader, D3DToGL_OptionUseEnvParams, -1, debugLabel );
//					sg_NewD3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, &bufNewGLCode, &bVertexShader, D3DToGL_OptionUseEnvParams, -1, debugLabel );

					// GLSL options
					nOptions |= D3DToGL_OptionGLSL;// | D3DToGL_OptionSRGBWriteSuffix | D3DToGL_AddHexComments | D3DToGL_PutHexCommentsAfterLines;
					if ( !IsOSX() )
					{
						nOptions |= D3DToGL_OptionAllowStaticControlFlow;
					}
					sg_NewD3DToOpenGLTranslator.TranslateShader( (uint32 *) pReadPtr, &bufGLSLCode, &bVertexShader, nOptions, -1, 0, debugLabel );

					Assert( !bVertexShader );

					// Test to make sure these are identical
//					if ( V_strcmp( (char *)bufGLCode.Base(), (char *)bufNewGLCode.Base() ) )
//					{
//						WriteTranslatedFile( pLookup, iIndex, (char *)bufGLCode.Base(), "afp" );		// Old
//						WriteTranslatedFile( pLookup, iIndex, (char *)bufNewGLCode.Base(), "afp2" );	// New
						WriteTranslatedFile( pLookup, iIndex, (char *)bufGLSLCode.Base(), "glsl_p" );	// GLSL
//						DisassembleShader( pLookup, iIndex, pReadPtr );									// Direct3D
//					}

					#if defined( WRITE_ASSEMBLY )
						WriteTranslatedFile( pLookup, iIndex, (char *)bufGLCode.Base(), "afp" );
					#endif
#endif // 0

#ifdef DX_TO_GL_ABSTRACTION
					// munge the debug label a bit to aid in decoding... catenate the iIndex on the end
					char temp[1024];
					sprintf(temp, "%s ps-combo %d", (debugLabel)?debugLabel:"", iIndex );
					debugLabelPtr = temp;
#endif

					// pass binary code to d3d interface, on GL it will invoke the translator back to asm
					hardwareShader = CreateD3DPixelShader( reinterpret_cast< DWORD *>( pReadPtr ), pFileCache->m_Header.m_nCentroidMask, nShaderSize, pShaderName, debugLabelPtr );
				}
				if ( hardwareShader == INVALID_HARDWARE_SHADER )
				{
					Warning( "failed to create shader\n" );
					Assert( 0 );
					bOK = false;
					break;
				}
			}
			pLookup->m_ShaderStaticCombos.m_pHardwareShaders[iIndex] = hardwareShader;
			pReadPtr += nShaderSize;
		}
	}

	delete[] pUnpackBuffer;

	return bOK;
}

//-----------------------------------------------------------------------------
// Static method, called by thread, don't call anything non-threadsafe from handler!!!
//-----------------------------------------------------------------------------
void CShaderManager::QueuedLoaderCallback( void *pContext, void *pContext2, const void *pData, int nSize, LoaderError_t loaderError )
{
	ShaderLookup_t* pLookup = (ShaderLookup_t *)pContext;

	bool bOK = ( loaderError == LOADERERROR_NONE );
	if ( bOK )
	{
		if ( pContext2 )
		{
			// presence denotes diff version
			bOK = s_ShaderManager.CreateDynamicCombos_Ver4( pContext, (uint8 *)pData );
		}
		else
		{
			bOK = s_ShaderManager.CreateDynamicCombos_Ver5( pContext, (uint8 *)pData );
		}
	}
	if ( !bOK )
	{
		pLookup->m_Flags |= SHADER_FAILED_LOAD;
	}
}

//-----------------------------------------------------------------------------
// Loads all shaders
//-----------------------------------------------------------------------------
bool CShaderManager::LoadAndCreateShaders( ShaderLookup_t &lookup, bool bVertexShader, char *debugLabel )
{
	const char *pName = m_ShaderSymbolTable.String( lookup.m_Name );

	// find it in the cache
	// a cache hit prevents costly i/o for static components, i.e. header, ref combo, etc.
	ShaderFileCache_t fileCacheLookup;
	fileCacheLookup.m_Name = lookup.m_Name;
	fileCacheLookup.m_bVertexShader = bVertexShader;
	intp fileCacheIndex = m_ShaderFileCache.Find( fileCacheLookup );
	if ( fileCacheIndex == m_ShaderFileCache.InvalidIndex() )
	{
		// not found, create a new entry
		fileCacheIndex = m_ShaderFileCache.AddToTail();
	}

	lookup.m_hShaderFileCache = fileCacheIndex;

	// fetch from cache
	ShaderFileCache_t *pFileCache = &m_ShaderFileCache[fileCacheIndex];
	ShaderHeader_t *pHeader = &pFileCache->m_Header;

	FileHandle_t hFile = FILESYSTEM_INVALID_HANDLE;
	if ( pFileCache->IsValid() )
	{
		// using cached header, just open file, no read of header needed
		hFile = OpenFileAndLoadHeader( m_ShaderSymbolTable.String( pFileCache->m_Filename ), NULL );
		if ( hFile == FILESYSTEM_INVALID_HANDLE )
		{
			// shouldn't happen
			Assert( 0 );
			return false;
		}
	}
	else
	{
		V_memset( pHeader, 0, sizeof( ShaderHeader_t ) );

		// try the vsh/psh dir first
		char filename[MAX_PATH];
		Q_snprintf( filename, MAX_PATH, "shaders\\%s\\%s" SHADER_FNAME_EXTENSION, bVertexShader ? "vsh" : "psh", pName );
		hFile = OpenFileAndLoadHeader( filename, pHeader );
		if ( hFile == FILESYSTEM_INVALID_HANDLE )
		{
#ifdef DYNAMIC_SHADER_COMPILE
			// Dynamically compile if it's HLSL.
			if ( LoadAndCreateShaders_Dynamic( lookup, bVertexShader ) )
			{
				return true;
			}
			else
			{
				return false;
			}
#endif
			// next, try the fxc dir
			Q_snprintf( filename, MAX_PATH, "shaders\\fxc\\%s" SHADER_FNAME_EXTENSION, pName );
			hFile = OpenFileAndLoadHeader( filename, pHeader );
			if ( hFile == FILESYSTEM_INVALID_HANDLE )
			{
				lookup.m_Flags |= SHADER_FAILED_LOAD;
				Warning( "Couldn't load %s shader %s\n", bVertexShader ? "vertex" : "pixel", pName );
				return false;
			}
		}

		lookup.m_Flags = pHeader->m_nFlags;

		pFileCache->m_Name = lookup.m_Name;
		pFileCache->m_Filename = m_ShaderSymbolTable.AddString( filename );
		pFileCache->m_bVertexShader = bVertexShader;

		if ( pFileCache->IsOldVersion() )
		{ 
			int referenceComboSize = ((ShaderHeader_t_v4 *)pHeader)->m_nDiffReferenceSize;
			if ( referenceComboSize )
			{
				// cache the reference combo
				pFileCache->m_ReferenceCombo.EnsureCapacity( referenceComboSize );
				g_pFullFileSystem->Read( pFileCache->m_ReferenceCombo.Base(), referenceComboSize, hFile );
			}
		}
		else
		{
			// cache the dictionary
			pFileCache->m_StaticComboRecords.EnsureCount( pHeader->m_nNumStaticCombos );
			g_pFullFileSystem->Read( pFileCache->m_StaticComboRecords.Base(), pHeader->m_nNumStaticCombos * sizeof( StaticComboRecord_t ), hFile );
			if ( pFileCache->IsVersion6() )
			{
				// read static combo alias records
				int nNumDups;
				g_pFullFileSystem->Read( &nNumDups, sizeof( nNumDups ), hFile );
				if ( nNumDups )
				{
					pFileCache->m_StaticComboDupRecords.EnsureCount( nNumDups );
					g_pFullFileSystem->Read( pFileCache->m_StaticComboDupRecords.Base(), nNumDups * sizeof( StaticComboAliasRecord_t ), hFile );
				}
			}

		}
	}

	// FIXME: should make lookup and ShaderStaticCombos_t are pool allocated.
	int i;
	lookup.m_ShaderStaticCombos.m_nCount = pHeader->m_nDynamicCombos;
	lookup.m_ShaderStaticCombos.m_pHardwareShaders = new HardwareShader_t[pHeader->m_nDynamicCombos];
	if ( IsPC() && m_bCreateShadersOnDemand )
	{
		lookup.m_ShaderStaticCombos.m_pCreationData = new ShaderStaticCombos_t::ShaderCreationData_t[pHeader->m_nDynamicCombos];
	}
	for ( i = 0; i < pHeader->m_nDynamicCombos; i++ )
	{
		lookup.m_ShaderStaticCombos.m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER;
	}

	int nStartingOffset = 0;
	int nEndingOffset = 0;

	if ( pFileCache->IsOldVersion() )
	{
		int nDictionaryOffset = sizeof( ShaderHeader_t ) + ((ShaderHeader_t_v4 *)pHeader)->m_nDiffReferenceSize;

		// read in shader's dynamic combos directory
		lookup.m_pComboDictionary = new ShaderDictionaryEntry_t[pHeader->m_nDynamicCombos];
		g_pFullFileSystem->Seek( hFile, nDictionaryOffset + lookup.m_nStaticIndex * sizeof( ShaderDictionaryEntry_t ), FILESYSTEM_SEEK_HEAD );
		g_pFullFileSystem->Read( lookup.m_pComboDictionary, pHeader->m_nDynamicCombos * sizeof( ShaderDictionaryEntry_t ), hFile );

		// want single read of all this shader's dynamic combos into a target buffer
		// shaders are written sequentially, determine starting offset and length
		for ( i = 0; i < pHeader->m_nDynamicCombos; i++ )
		{
			if ( lookup.m_pComboDictionary[i].m_Offset == -1 )
			{
				// skipped
				continue;
			}

			// ensure offsets are in fact sequentially ascending 
			Assert( lookup.m_pComboDictionary[i].m_Offset >= nStartingOffset && lookup.m_pComboDictionary[i].m_Size >= 0 );

			if ( !nStartingOffset )
			{
				nStartingOffset = lookup.m_pComboDictionary[i].m_Offset;
			}
			nEndingOffset = lookup.m_pComboDictionary[i].m_Offset + lookup.m_pComboDictionary[i].m_Size;
		}
		if ( !nStartingOffset )
		{
			g_pFullFileSystem->Close( hFile );
			Warning( "Shader '%s' - All dynamic combos skipped. This is bad!\n", m_ShaderSymbolTable.String( pFileCache->m_Filename ) );
			return false;
		}
	}
	else
	{
		int nStaticComboIdx = pFileCache->FindCombo( lookup.m_nStaticIndex / pFileCache->m_Header.m_nDynamicCombos );
		if ( nStaticComboIdx == -1 )
		{
			g_pFullFileSystem->Close( hFile );
			lookup.m_Flags |= SHADER_FAILED_LOAD;
			Warning( "Shader '%s' - Couldn't load combo %d of shader (dyn=%d)\n", m_ShaderSymbolTable.String( pFileCache->m_Filename ), lookup.m_nStaticIndex, pFileCache->m_Header.m_nDynamicCombos );
			return false;
		}

		nStartingOffset = pFileCache->m_StaticComboRecords[nStaticComboIdx].m_nFileOffset;
		nEndingOffset = pFileCache->m_StaticComboRecords[nStaticComboIdx+1].m_nFileOffset;
	}

	// align offsets for unbuffered optimal i/o - fastest i/o possible
	unsigned nOffsetAlign, nSizeAlign, nBufferAlign;
	g_pFullFileSystem->GetOptimalIOConstraints( hFile, &nOffsetAlign, &nSizeAlign, &nBufferAlign );
	unsigned int nAlignedOffset = AlignValue( ( nStartingOffset - nOffsetAlign ) + 1, nOffsetAlign );
	unsigned int nAlignedBytesToRead = AlignValue( nEndingOffset - nAlignedOffset, nSizeAlign );

	// used for adjusting provided buffer to actual data
	lookup.m_nDataOffset = nStartingOffset - nAlignedOffset;

	bool bOK = true;
	if ( IsX360() && g_pQueuedLoader->IsMapLoading() )
	{
		LoaderJob_t loaderJob;
		loaderJob.m_pFilename = m_ShaderSymbolTable.String( pFileCache->m_Filename );
		loaderJob.m_pPathID = "GAME";
		loaderJob.m_pCallback = QueuedLoaderCallback;
		loaderJob.m_pContext = (void *)&lookup;
		loaderJob.m_pContext2 = (void *)pFileCache->IsOldVersion();
		loaderJob.m_Priority = LOADERPRIORITY_DURINGPRELOAD;
		loaderJob.m_nBytesToRead = nAlignedBytesToRead;
		loaderJob.m_nStartOffset = nAlignedOffset;
		g_pQueuedLoader->AddJob( &loaderJob );
	}
	else
	{
		//printf("\n CShaderManager::LoadAndCreateShaders - reading %d bytes from file offset %d", nAlignedBytesToRead, nAlignedOffset);
		// single optimal read of all dynamic combos into monolithic buffer
		uint8 *pOptimalBuffer = (uint8 *)g_pFullFileSystem->AllocOptimalReadBuffer( hFile, nAlignedBytesToRead, nAlignedOffset );
		g_pFullFileSystem->Seek( hFile, nAlignedOffset, FILESYSTEM_SEEK_HEAD );
		g_pFullFileSystem->Read( pOptimalBuffer, nAlignedBytesToRead, hFile );

		if ( pFileCache->IsOldVersion() )
		{
			bOK = CreateDynamicCombos_Ver4( &lookup, pOptimalBuffer );
		}
		else
		{
			bOK = CreateDynamicCombos_Ver5( &lookup, pOptimalBuffer, debugLabel );
		}

		g_pFullFileSystem->FreeOptimalReadBuffer( pOptimalBuffer );
	}

	g_pFullFileSystem->Close( hFile );

	if ( !bOK )
	{
		lookup.m_Flags |= SHADER_FAILED_LOAD;
	}

	return bOK;
}


//----------------------------------------------------------------------------------old code

#if 0

	// Set this convar internally to build or add to the shader cache file
	// We really only expect this to work on DX_TO_GL_ABSTRACTION
	ConVar mat_cacheshaders( "mat_cacheshaders", "0", FCVAR_DEVELOPMENTONLY );

	#define SHADER_CACHE_FILE "shader_cache.cfg"
	#define PROGRAM_CACHE_FILE "program_cache.cfg"

	static void WriteToShaderCache( const char *pShaderName, const int nIndex )
	{
#ifndef DX_TO_GL_ABSTRACTION
			return;
#endif

		KeyValues *pShaderCache = new KeyValues( "shadercache" );
				// we don't load anything, it starts empty..  pShaderCache->LoadFromFile( g_pFullFileSystem, SHADER_CACHE_FILE, "MOD" );

		if ( !pShaderCache )
		{
			Warning( "Could not write to shader cache file!\n" );
			return;
		}

		// Subkey for specific shader
		KeyValues *pShaderKey = pShaderCache->FindKey( pShaderName, true );
		Assert( pShaderKey );

		bool bFound = false;
		int nKeys = 0;
		char szIndex[8];
		FOR_EACH_VALUE( pShaderKey, pValues )
		{
			if ( pValues->GetInt() == nIndex )
			{
				bFound = true;
			}
			nKeys++;
		}

		if ( !bFound )
		{
			V_snprintf( szIndex, 8, "%d", nKeys );
			pShaderKey->SetInt( szIndex, nIndex );
		}

		pShaderCache->SaveToFile( g_pFullFileSystem, SHADER_CACHE_FILE, "MOD" );
		pShaderCache->deleteThis();
	}

void CShaderManager::WarmShaderCache()
	{
#ifndef DX_TO_GL_ABSTRACTION
			return;
#endif

		// Don't access the cache if we're building it!
		if ( mat_cacheshaders.GetBool() )
			return;

		// Don't warm the cache if we're just going to monkey with the shaders anyway
	#ifdef DYNAMIC_SHADER_COMPILE
		return;
	#endif

		double st = Sys_FloatTime();


		//
		// First we warm SHADERS  ===============================================
		//

		KeyValues *pShaderCache = new KeyValues( "shadercache" );
		pShaderCache->LoadFromFile( g_pFullFileSystem, SHADER_CACHE_FILE, "MOD" );

		if ( !pShaderCache )
		{
			Warning( "Could not find shader cache file!\n" );
			return;
		}

		// Run through each shader in the cache
		FOR_EACH_SUBKEY( pShaderCache, pShaderKey )
		{
			const char *pShaderName = pShaderKey->GetName();
			bool bVertexShader = Q_stristr( pShaderName, "_vs20" ) || Q_stristr( pShaderName, "_vs30" );

			FOR_EACH_VALUE( pShaderKey, pValue )
			{
				char	temp[1024];
				int		staticIndex = pValue->GetInt();
				
				if ( bVertexShader )
				{
					V_snprintf( temp, sizeof(temp), "vs-file %s vs-index %d", pShaderName, staticIndex );
					CreateVertexShader( pShaderName, staticIndex, temp );
				}
				else
				{
					V_snprintf( temp, sizeof(temp), "ps-file %s ps-index %d", pShaderName, staticIndex );
					CreatePixelShader( pShaderName, staticIndex, temp );
				}
			}
		}

		pShaderCache->deleteThis();


		//
		// Next, we warm PROGRAMS (which are pairs of shaders)  =================
		//

		KeyValues *pProgramCache = new KeyValues( "programcache" );
		pProgramCache->LoadFromFile( g_pFullFileSystem, PROGRAM_CACHE_FILE, "MOD" );

		if ( !pProgramCache )
		{
			Warning( "Could not find program cache file!\n" );
			return;
		}

		// Run through each program in the cache
		FOR_EACH_SUBKEY( pProgramCache, pProgramKey )
		{
			KeyValues *pValue = pProgramKey->GetFirstValue();
			const char *pVertexShaderName = pValue->GetString();
			pValue = pValue->GetNextValue();
			const char *pPixelShaderName = pValue->GetString();
			pValue = pValue->GetNextValue();
			int nVertexShaderStaticIndex = pValue->GetInt();
			pValue = pValue->GetNextValue();
			int nPixelShaderStaticIndex = pValue->GetInt();
			pValue = pValue->GetNextValue();
			int nVertexShaderDynamicIndex = pValue->GetInt();
			pValue = pValue->GetNextValue();
			int nPixelShaderDynamicIndex = pValue->GetInt();

			ShaderLookup_t vshLookup;
			vshLookup.m_Name = m_ShaderSymbolTable.AddString( pVertexShaderName ); // TODO: use String() here and catch this odd case
			vshLookup.m_nStaticIndex = nVertexShaderStaticIndex;
			VertexShader_t vertexShader = m_VertexShaderDict.Find( vshLookup );

			ShaderLookup_t pshLookup;
			pshLookup.m_Name = m_ShaderSymbolTable.AddString( pPixelShaderName );
			pshLookup.m_nStaticIndex = nPixelShaderStaticIndex;
			PixelShader_t pixelShader = m_PixelShaderDict.Find( pshLookup );

			// If we found both shaders, do the link!
			if ( ( vertexShader != m_VertexShaderDict.InvalidIndex() ) && ( pixelShader != m_PixelShaderDict.InvalidIndex() ) )
			{
	#ifdef DX_TO_GL_ABSTRACTION
				//HardwareShader_t hardwareVertexShader = vshLookup.m_ShaderStaticCombos.m_pHardwareShaders[nVertexShaderDynamicIndex];
				//HardwareShader_t hardwarePixelShader = pshLookup.m_ShaderStaticCombos.m_pHardwareShaders[nPixelShaderDynamicIndex];

				HardwareShader_t hardwareVertexShader = m_VertexShaderDict[vertexShader].m_ShaderStaticCombos.m_pHardwareShaders[nVertexShaderDynamicIndex];
				HardwareShader_t hardwarePixelShader = m_PixelShaderDict[pixelShader].m_ShaderStaticCombos.m_pHardwareShaders[nPixelShaderDynamicIndex];

				if ( ( hardwareVertexShader != INVALID_HARDWARE_SHADER ) && ( hardwarePixelShader != INVALID_HARDWARE_SHADER ) )
				{
					if ( S_OK != Dx9Device()->LinkShaderPair( (IDirect3DVertexShader9 *)hardwareVertexShader, (IDirect3DPixelShader9 *)hardwarePixelShader ) )
					{
						Warning( "Could not link OpenGL shaders: %s (%d, %d) : %s (%d, %d)\n", pVertexShaderName, nVertexShaderStaticIndex, nVertexShaderDynamicIndex, pPixelShaderName, nPixelShaderStaticIndex, nPixelShaderDynamicIndex );
					}
				}
	#endif
			}
			else
			{
				Warning( "Invalid shader linkage: %s (%d, %d) : %s (%d, %d)\n", pVertexShaderName, nVertexShaderStaticIndex, nVertexShaderDynamicIndex, pPixelShaderName, nPixelShaderStaticIndex, nPixelShaderDynamicIndex );
			}
		}

		pProgramCache->deleteThis();

		float elapsed = ( float )( Sys_FloatTime() - st ) * 1000.0;
		DevMsg( "WarmShaderCache took %.3f msec\n", elapsed );
	}

#endif
//----------------------------------------------------------------------------------old code

#ifdef DX_TO_GL_ABSTRACTION
// if shaders are changed in a way that requires the client-side cache to be invalidated,
// increment this string - such changes include combo changes (skips, adding combos)
const char *k_pszShaderCacheRootKey = "glshadercachev002";
#endif

void	CShaderManager::SaveShaderCache( char *cacheName )
{
#ifdef DX_TO_GL_ABSTRACTION	// must ifdef, it uses calls which don't exist in the real DX9 interface

	KeyValues *pProgramCache = new KeyValues( k_pszShaderCacheRootKey );

	if ( !pProgramCache )
	{
		Warning( "Could not write to program cache file!\n" );
		return;
	}

	int i=0;
	GLMShaderPairInfo info;

	do
	{
		Dx9Device()->QueryShaderPair( i, &info );
		
		if (info.m_status==1)
		{
			// found one
			// extract values of interest which represent a pair of shaders
			
			if (info.m_vsName[0] && info.m_psName[0] && (info.m_vsDynamicIndex > -1) && (info.m_psDynamicIndex > -1) )
			{
				// make up a key - this thing is really a list of tuples, so need not be keyed by anything particular
				KeyValues *pProgramKey = pProgramCache->CreateNewKey();
				Assert( pProgramKey );

				pProgramKey->SetString	( "vs", info.m_vsName );
				pProgramKey->SetString	( "ps", info.m_psName );

				pProgramKey->SetInt		( "vs_static", info.m_vsStaticIndex );
				pProgramKey->SetInt		( "ps_static", info.m_psStaticIndex );

				pProgramKey->SetInt		( "vs_dynamic", info.m_vsDynamicIndex );
				pProgramKey->SetInt		( "ps_dynamic", info.m_psDynamicIndex );
			}
		}
		i++;
	} while( info.m_status >= 0 );
	
	pProgramCache->SaveToFile( g_pFullFileSystem, cacheName, "MOD" );
	pProgramCache->deleteThis();
	
	// done! whew
#endif
}

bool	CShaderManager::LoadShaderCache( char *cacheName )
{
#ifdef DX_TO_GL_ABSTRACTION
	KeyValues *pProgramCache = new KeyValues( "" );
	bool found = pProgramCache->LoadFromFile( g_pFullFileSystem, cacheName, "MOD" );

	if ( !found ) 
	{
		Warning( "Could not load program cache file %s\n", cacheName );
		return false;
	}

	    if ( Q_stricmp( pProgramCache->GetName(), k_pszShaderCacheRootKey ) ) 
	    {
			Warning( "Ignoring out-of-date shader cache (%s) with root key %s\n", cacheName, pProgramCache->GetName() );
	        return false;
	    }
    	
	int nTotalLinkedShaders = 0;
	int nTotalKeyValues = 0;

	// walk the table..
	FOR_EACH_SUBKEY( pProgramCache, pProgramKey )
	{
		nTotalKeyValues++;

		// extract values decribing the specific active pair
		// then see if either stage needs a compilation done
		// then proceed to link
		
		KeyValues *pValue = pProgramKey->GetFirstValue();
		if (!pValue)
			continue;
		const char *pVertexShaderName = pValue->GetString();

		pValue = pValue->GetNextValue();
		if (!pValue)
			continue;
		const char *pPixelShaderName = pValue->GetString();

		pValue = pValue->GetNextValue();
		if (!pValue)
			continue;
		int nVertexShaderStaticIndex = pValue->GetInt();

		pValue = pValue->GetNextValue();
		if (!pValue)
			continue;
		int nPixelShaderStaticIndex = pValue->GetInt();

		pValue = pValue->GetNextValue();
		if (!pValue)
			continue;
		int nVertexShaderDynamicIndex = pValue->GetInt();

		pValue = pValue->GetNextValue();
		if (!pValue)
			continue;
		int nPixelShaderDynamicIndex = pValue->GetInt();

		ShaderLookup_t vshLookup;
		vshLookup.m_Name = m_ShaderSymbolTable.AddString( pVertexShaderName ); // TODO: use String() here and catch this odd case
		vshLookup.m_nStaticIndex = nVertexShaderStaticIndex;
		VertexShader_t vertexShader = m_VertexShaderDict.Find( vshLookup );

		// if the VS was not found - now is the time to build it
		if( vertexShader == m_VertexShaderDict.InvalidIndex())
		{
			char	temp[1024];
				
			V_snprintf( temp, sizeof(temp), "vs-file %s vs-index %d", pVertexShaderName, nVertexShaderStaticIndex );
			CreateVertexShader( pVertexShaderName, nVertexShaderStaticIndex, temp );
			
			// this one should not fail
			vertexShader = m_VertexShaderDict.Find( vshLookup );
			Assert( vertexShader != m_VertexShaderDict.InvalidIndex());
		}
		
		ShaderLookup_t pshLookup;
		pshLookup.m_Name = m_ShaderSymbolTable.AddString( pPixelShaderName );
		pshLookup.m_nStaticIndex = nPixelShaderStaticIndex;
		PixelShader_t pixelShader = m_PixelShaderDict.Find( pshLookup );

		if( pixelShader == m_PixelShaderDict.InvalidIndex())
		{
			char	temp[1024];
			
			V_snprintf( temp, sizeof(temp), "ps-file %s ps-index %d", pPixelShaderName, nPixelShaderStaticIndex );
			CreatePixelShader( pPixelShaderName, nPixelShaderStaticIndex, temp );
			
			// this one should not fail
			pixelShader = m_PixelShaderDict.Find( pshLookup );
			Assert( pixelShader != m_PixelShaderDict.InvalidIndex());
		}
		
		// If we found both shaders, do the link!
		if ( ( vertexShader != m_VertexShaderDict.InvalidIndex() ) && ( pixelShader != m_PixelShaderDict.InvalidIndex() ) )
		{
			// double check that the hardware shader arrays are actually instantiated.. bail on the attempt if not (odd...)
			if (m_VertexShaderDict[vertexShader].m_ShaderStaticCombos.m_pHardwareShaders && m_PixelShaderDict[pixelShader].m_ShaderStaticCombos.m_pHardwareShaders)
			{
				// and sanity check the indices..
				if ( (nVertexShaderDynamicIndex>=0) && (nPixelShaderDynamicIndex>=0) )
				{
					HardwareShader_t hardwareVertexShader = m_VertexShaderDict[vertexShader].m_ShaderStaticCombos.m_pHardwareShaders[nVertexShaderDynamicIndex];
					HardwareShader_t hardwarePixelShader = m_PixelShaderDict[pixelShader].m_ShaderStaticCombos.m_pHardwareShaders[nPixelShaderDynamicIndex];

					if ( ( hardwareVertexShader != INVALID_HARDWARE_SHADER ) && ( hardwarePixelShader != INVALID_HARDWARE_SHADER ) )
					{
						if ( S_OK != Dx9Device()->LinkShaderPair( (IDirect3DVertexShader9 *)hardwareVertexShader, (IDirect3DPixelShader9 *)hardwarePixelShader ) )
						{
							Warning( "Could not link OpenGL shaders: %s (%d, %d) : %s (%d, %d)\n", pVertexShaderName, nVertexShaderStaticIndex, nVertexShaderDynamicIndex, pPixelShaderName, nPixelShaderStaticIndex, nPixelShaderDynamicIndex );
						}
						else
						{
							nTotalLinkedShaders++;
						}
					}
				}
				else
				{
					Warning( "nVertexShaderDynamicIndex or nPixelShaderDynamicIndex was negative\n" );
				}
			}
			else
			{
				Warning( "m_pHardwareShaders was null\n" );
			}
		}
		else
		{
			Warning( "Invalid shader linkage: %s (%d, %d) : %s (%d, %d)\n", pVertexShaderName, nVertexShaderStaticIndex, nVertexShaderDynamicIndex, pPixelShaderName, nPixelShaderStaticIndex, nPixelShaderDynamicIndex );
		}
	}

	Msg( "Loaded program cache file \"%s\", total keyvalues: %i, total successfully linked: %i\n", cacheName, nTotalKeyValues, nTotalLinkedShaders );

	return true;

#else
	return false;	// have to return a value on Windows build to appease compiler
#endif
}


	
//-----------------------------------------------------------------------------
// Creates and destroys vertex shaders
//-----------------------------------------------------------------------------
VertexShader_t CShaderManager::CreateVertexShader( const char *pFileName, int nStaticVshIndex, char *debugLabel )
{
	MEM_ALLOC_CREDIT();

	if ( !pFileName )
	{
		return INVALID_SHADER;
	}

	#if 0 //old
		if ( mat_cacheshaders.GetBool() )
		{
			WriteToShaderCache( pFileName, nStaticVshIndex );
		}
	#endif
	
	VertexShader_t shader;
	ShaderLookup_t lookup;
	lookup.m_Name = m_ShaderSymbolTable.AddString( pFileName );
	lookup.m_nStaticIndex = nStaticVshIndex;
	shader = m_VertexShaderDict.Find( lookup );
	if ( shader == m_VertexShaderDict.InvalidIndex() )
	{
		//printf("\nCShaderManager::CreateVertexShader( filename = %s, staticVshIndex = %d - not in cache", pFileName, nStaticVshIndex );
	
		shader = m_VertexShaderDict.AddToTail( lookup );
		if ( !LoadAndCreateShaders( m_VertexShaderDict[shader], true, debugLabel ) )
		{
			return INVALID_SHADER;
		}
	}
	m_VertexShaderDict[shader].IncRefCount();
	return shader;
}

//-----------------------------------------------------------------------------
// Create pixel shader
//-----------------------------------------------------------------------------
PixelShader_t CShaderManager::CreatePixelShader( const char *pFileName, int nStaticPshIndex, char *debugLabel )
{
	MEM_ALLOC_CREDIT();

	if ( !pFileName )
	{
		return INVALID_SHADER;
	}

	#if 0 //old
		if ( mat_cacheshaders.GetBool() )
		{
			WriteToShaderCache( pFileName, nStaticPshIndex );
		}
	#endif
	
	PixelShader_t shader;
	ShaderLookup_t lookup;
	lookup.m_Name = m_ShaderSymbolTable.AddString( pFileName );
	lookup.m_nStaticIndex = nStaticPshIndex;
	shader = m_PixelShaderDict.Find( lookup );
	if ( shader == m_PixelShaderDict.InvalidIndex() )
	{
		shader = m_PixelShaderDict.AddToTail( lookup );
		if ( !LoadAndCreateShaders( m_PixelShaderDict[shader], false, debugLabel ) )
		{
			return INVALID_SHADER;
		}
	}
	m_PixelShaderDict[shader].IncRefCount();
	return shader;
}

//-----------------------------------------------------------------------------
// Clear the refCounts to zero
//-----------------------------------------------------------------------------
void CShaderManager::ClearVertexAndPixelShaderRefCounts()
{
	for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); 
		 vshIndex != m_VertexShaderDict.InvalidIndex(); 
		 vshIndex = m_VertexShaderDict.Next( vshIndex ) )
	{
		m_VertexShaderDict[vshIndex].m_nRefCount = 0;
	}

	for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); 
		 pshIndex != m_PixelShaderDict.InvalidIndex(); 
		 pshIndex = m_PixelShaderDict.Next( pshIndex ) )
	{
		m_PixelShaderDict[pshIndex].m_nRefCount = 0;
	}
}

//-----------------------------------------------------------------------------
// Destroy all shaders that have no reference
//-----------------------------------------------------------------------------
void CShaderManager::PurgeUnusedVertexAndPixelShaders()
{
	#ifdef DX_TO_GL_ABSTRACTION
		if (mat_autosave_glshaders.GetInt())
		{
			SaveShaderCache("glshaders.cfg");
		}
		return;	// don't purge shaders, it's too costly to put them back
	#endif
	
	// iterate vertex shaders
	for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); vshIndex != m_VertexShaderDict.InvalidIndex(); )
	{
		Assert( m_VertexShaderDict[vshIndex].m_nRefCount >= 0 );

		// Get the next one before we potentially delete the current one.
		VertexShader_t next = m_VertexShaderDict.Next( vshIndex );
		if ( m_VertexShaderDict[vshIndex].m_nRefCount <= 0 )
		{
			DestroyVertexShader( vshIndex );
		}
		vshIndex = next;
	}

	// iterate pixel shaders
	for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); pshIndex != m_PixelShaderDict.InvalidIndex(); )
	{
		Assert( m_PixelShaderDict[pshIndex].m_nRefCount >= 0 );

		// Get the next one before we potentially delete the current one.
		PixelShader_t next = m_PixelShaderDict.Next( pshIndex );
		if ( m_PixelShaderDict[pshIndex].m_nRefCount <= 0 )
		{
			DestroyPixelShader( pshIndex );
		}
		pshIndex = next;
	}
}



void* CShaderManager::GetCurrentVertexShader()
{
	return (void*)m_HardwareVertexShader;
}

void* CShaderManager::GetCurrentPixelShader()
{
	return (void*)m_HardwarePixelShader;
}


//-----------------------------------------------------------------------------
// The low-level dx call to set the vertex shader state
//-----------------------------------------------------------------------------
void CShaderManager::SetVertexShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader )
{
	if ( m_HardwareVertexShader != shader )
	{
		RECORD_COMMAND( DX8_SET_VERTEX_SHADER, 1 );
		RECORD_INT( ( int )shader ); // hack hack hack

		Dx9Device()->SetVertexShader( (IDirect3DVertexShader9*)shader );
		m_HardwareVertexShader = shader;
	}
}

void CShaderManager::BindVertexShader( VertexShaderHandle_t hVertexShader )
{
	HardwareShader_t hHardwareShader = m_RawVertexShaderDict[ (VertexShaderIndex_t)(uintp)hVertexShader] ;
	SetVertexShaderState( hHardwareShader );
}


//-----------------------------------------------------------------------------
// Sets a particular vertex shader as the current shader
//-----------------------------------------------------------------------------
void CShaderManager::SetVertexShader( VertexShader_t shader )
{
	// Determine which vertex shader to use...
	if ( shader == INVALID_SHADER )
	{
		SetVertexShaderState( 0 );
		return;
	}

	int vshIndex = m_nVertexShaderIndex;
	Assert( vshIndex >= 0 );
	if( vshIndex < 0 )
	{
		vshIndex = 0;
	}

	ShaderLookup_t &vshLookup = m_VertexShaderDict[shader];
//	Warning( "vsh: %s static: %d dynamic: %d\n", m_ShaderSymbolTable.String( vshLookup.m_Name ),
//		vshLookup.m_nStaticIndex, m_nVertexShaderIndex );

#ifdef DYNAMIC_SHADER_COMPILE
	HardwareShader_t &dxshader = m_VertexShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[vshIndex];
	if ( dxshader == INVALID_HARDWARE_SHADER )
	{
		// compile it since we haven't already!
		dxshader = CompileShader( m_ShaderSymbolTable.String( vshLookup.m_Name ), vshLookup.m_nStaticIndex, vshIndex, true );
		Assert( dxshader != INVALID_HARDWARE_SHADER );

		if( IsX360() )
		{
			//360 does not respond well at all to bad shaders or Error() calls. So we're staying here until we get something that compiles
			while( dxshader == INVALID_HARDWARE_SHADER )
			{
				Warning( "A dynamically compiled vertex shader has failed to build. Pausing for 5 seconds and attempting rebuild.\n" );
#ifdef _WIN32
				Sleep( 5000 );
#elif POSIX
				usleep( 5000 );
#endif
				dxshader = CompileShader( m_ShaderSymbolTable.String( vshLookup.m_Name ), vshLookup.m_nStaticIndex, vshIndex, true );
			}
		}
	}
#else
	if ( vshLookup.m_Flags & SHADER_FAILED_LOAD )
	{
		Assert( 0 );
		return;
	}
#ifdef _DEBUG
	vshDebugIndex = (vshDebugIndex + 1) % MAX_SHADER_HISTORY;
	Q_strncpy( vshDebugName[vshDebugIndex], m_ShaderSymbolTable.String( vshLookup.m_Name ), sizeof( vshDebugName[0] ) );
#endif
	Assert( vshIndex < vshLookup.m_ShaderStaticCombos.m_nCount );
	HardwareShader_t dxshader = vshLookup.m_ShaderStaticCombos.m_pHardwareShaders[vshIndex];
#endif

	if ( IsPC() && ( dxshader == INVALID_HARDWARE_SHADER ) && m_bCreateShadersOnDemand )
	{
#ifdef DYNAMIC_SHADER_COMPILE
		ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &m_VertexShaderDict[shader].m_ShaderStaticCombos.m_pCreationData[vshIndex];
#else
		ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &vshLookup.m_ShaderStaticCombos.m_pCreationData[vshIndex];
#endif

		dxshader = CreateD3DVertexShader( ( DWORD * )pCreationData->ByteCode.Base(), pCreationData->ByteCode.Count(), m_ShaderSymbolTable.String( vshLookup.m_Name ) );

#ifdef DYNAMIC_SHADER_COMPILE 
		// copy the compiled shader handle back to wherever it's supposed to be stored
		m_VertexShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[vshIndex] = dxshader;
#else
		vshLookup.m_ShaderStaticCombos.m_pHardwareShaders[vshIndex] = dxshader;
#endif
	}

	Assert( dxshader );

#ifndef DYNAMIC_SHADER_COMPILE
	if( !dxshader )
	{
		Error( "!!!!!Using invalid shader combo!!!!!  Consult a programmer and tell them to build debug materialsystem.dll and stdshader*.dll.  Run with \"mat_bufferprimitives 0\" and look for CMaterial in the call stack and see what m_pDebugName is.  You are likely using a shader combo that has been skipped.\n" );
	}
#endif

	SetVertexShaderState( dxshader );
}

//-----------------------------------------------------------------------------
// The low-level dx call to set the pixel shader state
//-----------------------------------------------------------------------------
void CShaderManager::SetPixelShaderState( HardwareShader_t shader, DataCacheHandle_t hCachedShader )
{
	if ( m_HardwarePixelShader != shader )
	{		
		Dx9Device()->SetPixelShader( (IDirect3DPixelShader*)shader );		
		m_HardwarePixelShader = shader;
	}
}

void CShaderManager::BindPixelShader( PixelShaderHandle_t hPixelShader )
{
	HardwareShader_t hHardwareShader = m_RawPixelShaderDict[ (PixelShaderIndex_t)(uintp)hPixelShader ];
	SetPixelShaderState( hHardwareShader );
}


//-----------------------------------------------------------------------------
// Sets a particular pixel shader as the current shader
//-----------------------------------------------------------------------------
void CShaderManager::SetPixelShader( PixelShader_t shader )
{
	if ( shader == INVALID_SHADER )
	{
		SetPixelShaderState( 0 );
		return;
	}

	int pshIndex = m_nPixelShaderIndex;
	Assert( pshIndex >= 0 );
	ShaderLookup_t &pshLookup = m_PixelShaderDict[shader];
//	Warning( "psh: %s static: %d dynamic: %d\n", m_ShaderSymbolTable.String( pshLookup.m_Name ),
//		pshLookup.m_nStaticIndex, m_nPixelShaderIndex );

#ifdef DYNAMIC_SHADER_COMPILE
	HardwareShader_t &dxshader = m_PixelShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[pshIndex];
	if ( dxshader == INVALID_HARDWARE_SHADER )
	{
		// compile it since we haven't already!
		dxshader = CompileShader( m_ShaderSymbolTable.String( pshLookup.m_Name ), pshLookup.m_nStaticIndex, pshIndex, false );
//		Assert( dxshader != INVALID_HARDWARE_SHADER );

		if( IsX360() )
		{
			//360 does not respond well at all to bad shaders or Error() calls. So we're staying here until we get something that compiles
			while( dxshader == INVALID_HARDWARE_SHADER )
			{
				Warning( "A dynamically compiled pixel shader has failed to build. Pausing for 5 seconds and attempting rebuild.\n" );
#ifdef _WIN32
				Sleep( 5000 );
#elif POSIX
				usleep( 5000 );
#endif
				dxshader = CompileShader( m_ShaderSymbolTable.String( pshLookup.m_Name ), pshLookup.m_nStaticIndex, pshIndex, false );
			}
		}
	}
#else
	if ( pshLookup.m_Flags & SHADER_FAILED_LOAD )
	{
		Assert( 0 );
		return;
	}
#ifdef _DEBUG
	pshDebugIndex = (pshDebugIndex + 1) % MAX_SHADER_HISTORY;
	Q_strncpy( pshDebugName[pshDebugIndex], m_ShaderSymbolTable.String( pshLookup.m_Name ), sizeof( pshDebugName[0] ) );
#endif
	HardwareShader_t dxshader = pshLookup.m_ShaderStaticCombos.m_pHardwareShaders[pshIndex];
#endif

	if ( IsPC() && ( dxshader == INVALID_HARDWARE_SHADER ) && m_bCreateShadersOnDemand )
	{
#ifdef DYNAMIC_SHADER_COMPILE
		ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &m_PixelShaderDict[shader].m_ShaderStaticCombos.m_pCreationData[pshIndex];
#else
		ShaderStaticCombos_t::ShaderCreationData_t *pCreationData = &pshLookup.m_ShaderStaticCombos.m_pCreationData[pshIndex];
#endif

		const char *pShaderName = m_ShaderSymbolTable.String( pshLookup.m_Name );
		dxshader = CreateD3DPixelShader( ( DWORD * )pCreationData->ByteCode.Base(), pCreationData->iCentroidMask, pCreationData->ByteCode.Count(), pShaderName );

#ifdef DYNAMIC_SHADER_COMPILE 
		// copy the compiled shader handle back to wherever it's supposed to be stored
		m_PixelShaderDict[shader].m_ShaderStaticCombos.m_pHardwareShaders[pshIndex] = dxshader;
#else
		pshLookup.m_ShaderStaticCombos.m_pHardwareShaders[pshIndex] = dxshader;
#endif
	}

	AssertMsg( dxshader != INVALID_HARDWARE_SHADER, "Failed to set pixel shader." );
	SetPixelShaderState( dxshader );
}

//-----------------------------------------------------------------------------
// Resets the shader state
//-----------------------------------------------------------------------------
void CShaderManager::ResetShaderState()
{
	// This will force the calls to SetVertexShader + SetPixelShader to actually set the state
	m_HardwareVertexShader = (HardwareShader_t)-1;
	m_HardwarePixelShader = (HardwareShader_t)-1;

	SetVertexShader( INVALID_SHADER );
	SetPixelShader( INVALID_SHADER );
}

//-----------------------------------------------------------------------------
// Destroy a particular vertex shader
//-----------------------------------------------------------------------------
void CShaderManager::DestroyVertexShader( VertexShader_t shader )
{
	ShaderStaticCombos_t &combos = m_VertexShaderDict[shader].m_ShaderStaticCombos;
	int i;
	for ( i = 0; i < combos.m_nCount; i++ )
	{
		if ( combos.m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER )
		{
			IDirect3DVertexShader9* pShader = ( IDirect3DVertexShader9 * )combos.m_pHardwareShaders[i];
			UnregisterVS( pShader );
#ifdef DBGFLAG_ASSERT
			int nRetVal = 
#endif
				pShader->Release();
			Assert( nRetVal == 0 );
		}
	}
	delete [] combos.m_pHardwareShaders;
	combos.m_pHardwareShaders = NULL;

	if ( combos.m_pCreationData != NULL )
	{
		delete [] combos.m_pCreationData;
		combos.m_pCreationData = NULL;
	}

	m_VertexShaderDict.Remove( shader );
}

//-----------------------------------------------------------------------------
// Destroy a particular pixel shader
//-----------------------------------------------------------------------------
void CShaderManager::DestroyPixelShader( PixelShader_t pixelShader )
{
	ShaderStaticCombos_t &combos = m_PixelShaderDict[pixelShader].m_ShaderStaticCombos;
	int i;
	for ( i = 0; i < combos.m_nCount; i++ )
	{
		if ( combos.m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER )
		{
			IDirect3DPixelShader* pShader = ( IDirect3DPixelShader * )combos.m_pHardwareShaders[i];
			UnregisterPS( pShader );
#ifdef DBGFLAG_ASSERT
			int nRetVal = 
#endif
				pShader->Release();
			Assert( nRetVal == 0 );
		}
	}
	delete [] combos.m_pHardwareShaders;
	combos.m_pHardwareShaders = NULL;

	if ( combos.m_pCreationData != NULL )
	{
		delete [] combos.m_pCreationData;
		combos.m_pCreationData = NULL;
	}

	m_PixelShaderDict.Remove( pixelShader );
}


//-----------------------------------------------------------------------------
// Destroys all shaders
//-----------------------------------------------------------------------------
void CShaderManager::DestroyAllShaders( void )
{
	// Remarking this out because it's conflicting with dxabstract's shutdown resource leak detection code (we leak thousands of shaders at shutdown with this in place). 
	// I see no reason why we would want to do this in D3D9 but not GL?
//#ifdef DX_TO_GL_ABSTRACTION
//		return;
//#endif
	
	for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); 
		 vshIndex != m_VertexShaderDict.InvalidIndex(); )
	{
		Assert( m_VertexShaderDict[vshIndex].m_nRefCount >= 0 );
		VertexShader_t next = m_VertexShaderDict.Next( vshIndex );
		DestroyVertexShader( vshIndex );
		vshIndex = next;
	}

	for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); 
		 pshIndex != m_PixelShaderDict.InvalidIndex(); )
	{
		Assert( m_PixelShaderDict[pshIndex].m_nRefCount >= 0 );
		PixelShader_t next = m_PixelShaderDict.Next( pshIndex );
		DestroyPixelShader( pshIndex );
		pshIndex = next;
	}

	// invalidate the file cache
	m_ShaderFileCache.Purge();
}

//-----------------------------------------------------------------------------
// print all vertex and pixel shaders along with refcounts to the console
//-----------------------------------------------------------------------------
void CShaderManager::SpewVertexAndPixelShaders( void )
{
	// only spew a populated shader file cache
	Msg( "\nShader File Cache:\n" );
	for ( intp cacheIndex = m_ShaderFileCache.Head();
		 cacheIndex != m_ShaderFileCache.InvalidIndex();
		 cacheIndex = m_ShaderFileCache.Next( cacheIndex ) )
	{
		ShaderFileCache_t *pCache = &m_ShaderFileCache[cacheIndex];
		Msg( "Total Combos:%9d Static:%9d Dynamic:%7d SeekTable:%7d Ver:%d '%s'\n", 
			pCache->m_Header.m_nTotalCombos, 
			pCache->m_Header.m_nTotalCombos/pCache->m_Header.m_nDynamicCombos,
			pCache->m_Header.m_nDynamicCombos,
			pCache->IsOldVersion() ? 0 : pCache->m_Header.m_nNumStaticCombos,
			pCache->m_Header.m_nVersion,
			m_ShaderSymbolTable.String( pCache->m_Filename ) );
	}
	Msg( "\n" );

	// spew vertex shader dictionary
	int totalVertexShaders = 0;
	int totalVertexShaderSets = 0;
	for ( VertexShader_t vshIndex = m_VertexShaderDict.Head(); 
		 vshIndex != m_VertexShaderDict.InvalidIndex();
		 vshIndex = m_VertexShaderDict.Next( vshIndex ) )
	{
		const ShaderLookup_t &lookup = m_VertexShaderDict[vshIndex];
		const char *pName = m_ShaderSymbolTable.String( lookup.m_Name );
		Msg( "vsh 0x%8.8x: static combo:%9d dynamic combos:%6d refcount:%4d \"%s\"\n", vshIndex,
			( int )lookup.m_nStaticIndex, ( int )lookup.m_ShaderStaticCombos.m_nCount,
			lookup.m_nRefCount, pName );
		totalVertexShaders += lookup.m_ShaderStaticCombos.m_nCount;
		totalVertexShaderSets++;
	}

	// spew pixel shader dictionary
	int totalPixelShaders = 0;
	int totalPixelShaderSets = 0;
	for ( PixelShader_t pshIndex = m_PixelShaderDict.Head(); 
		 pshIndex != m_PixelShaderDict.InvalidIndex();
		 pshIndex = m_PixelShaderDict.Next( pshIndex ) )
	{
		const ShaderLookup_t &lookup = m_PixelShaderDict[pshIndex];
		const char *pName = m_ShaderSymbolTable.String( lookup.m_Name );
		Msg( "psh 0x%8.8x: static combo:%9d dynamic combos:%6d refcount:%4d \"%s\"\n", pshIndex,
			( int )lookup.m_nStaticIndex, ( int )lookup.m_ShaderStaticCombos.m_nCount,
			lookup.m_nRefCount, pName );
		totalPixelShaders += lookup.m_ShaderStaticCombos.m_nCount;
		totalPixelShaderSets++;
	}

	Msg( "Total unique vertex shaders: %d\n", totalVertexShaders );
	Msg( "Total vertex shader sets: %d\n", totalVertexShaderSets );
	Msg( "Total unique pixel shaders: %d\n", totalPixelShaders );
	Msg( "Total pixel shader sets: %d\n", totalPixelShaderSets );
}

CON_COMMAND( mat_spewvertexandpixelshaders, "Print all vertex and pixel shaders currently loaded to the console" )
{
	( ( CShaderManager * )ShaderManager() )->SpewVertexAndPixelShaders();
}

const char *CShaderManager::GetActiveVertexShaderName()
{
#if !defined( _DEBUG )
	return "";
#else
	if ( !m_HardwareVertexShader )
	{
		return "NULL";
	}
	return vshDebugName[vshDebugIndex];
#endif
}

const char *CShaderManager::GetActivePixelShaderName()
{
#if !defined( _DEBUG )
	return "";
#else
	if ( !m_HardwarePixelShader )
	{
		return "NULL";
	}
	return pshDebugName[pshDebugIndex];
#endif
}

#ifdef DYNAMIC_SHADER_COMPILE
void CShaderManager::FlushShaders( void )
{
	for( VertexShader_t shader = m_VertexShaderDict.Head(); 
	     shader != m_VertexShaderDict.InvalidIndex(); 
		 shader = m_VertexShaderDict.Next( shader ) )
	{
		int i;
		ShaderStaticCombos_t &combos = m_VertexShaderDict[shader].m_ShaderStaticCombos;
		for( i = 0; i < combos.m_nCount; i++ )
		{
			if( combos.m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER )
			{
#ifdef _DEBUG
				int nRetVal=
#endif
					( ( IDirect3DVertexShader9 * )combos.m_pHardwareShaders[i] )->Release();
				Assert( nRetVal == 0 );
			}
			combos.m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER;
		}
	}

	for( PixelShader_t shader = m_PixelShaderDict.Head(); 
	     shader != m_PixelShaderDict.InvalidIndex(); 
		 shader = m_PixelShaderDict.Next( shader ) )
	{
		int i;
		ShaderStaticCombos_t &combos = m_PixelShaderDict[shader].m_ShaderStaticCombos;
		for( i = 0; i < combos.m_nCount; i++ )
		{
			if( combos.m_pHardwareShaders[i] != INVALID_HARDWARE_SHADER )
			{
#ifdef _DEBUG
				int nRetVal =
#endif
					( ( IDirect3DPixelShader * )combos.m_pHardwareShaders[i] )->Release();
				Assert( nRetVal == 0 );
			}
			combos.m_pHardwareShaders[i] = INVALID_HARDWARE_SHADER;
		}
	}

	// invalidate the file cache
	m_ShaderFileCache.Purge();
}
#endif

#ifdef DYNAMIC_SHADER_COMPILE
static void MatFlushShaders( void )
{
#if defined( _X360 )
	XBX_rSyncShaderCache();
#endif
	( ( CShaderManager * )ShaderManager() )->FlushShaders();
}
#endif

#ifdef DYNAMIC_SHADER_COMPILE
CON_COMMAND( mat_flushshaders, "flush all hardware shaders when using DYNAMIC_SHADER_COMPILE" )
{
	MatFlushShaders();
}
#endif

CON_COMMAND( mat_shadercount, "display count of all shaders and reset that count" )
{
	Warning( "Num Pixel Shaders = %d Vertex Shaders=%d\n", s_NumPixelShadersCreated, s_NumVertexShadersCreated );
	s_NumVertexShadersCreated = 0;
	s_NumPixelShadersCreated = 0;
}

#if defined( DX_TO_GL_ABSTRACTION )
void	CShaderManager::DoStartupShaderPreloading()
{
#ifdef ANDROID // Too slow
	return;
#endif

	if (mat_autoload_glshaders.GetInt())
	{
		double flStartTime = Plat_FloatTime();

		s_NumVertexShadersCreated = s_NumPixelShadersCreated = 0;

		// try base file
#ifdef OSX		
		if ( !LoadShaderCache("glbaseshaders_osx.cfg") )		// factory cache
#else
		if ( !LoadShaderCache("glbaseshaders.cfg") )		// factory cache
#endif
		{
			Warning( "Could not find base GL shader cache file\n" );
		}

		if ( !LoadShaderCache("glshaders.cfg") )			// user mutable cache
		{
			Warning( "Could not find user GL shader cache file\n" );
		}

		double flEndTime = Plat_FloatTime();
		Msg( "Precache: Took %d ms, Vertex %d, Pixel %d\n", ( int )( ( flEndTime - flStartTime ) * 1000.0 ), s_NumVertexShadersCreated, s_NumPixelShadersCreated );
	}
}
#endif