//========= Copyright Valve Corporation, All rights reserved. ============//
//
//=============================================================================


// Standard includes
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <io.h>


// Valve includes
#include "itemtest/itemtest.h"
#include "bitmap/bitmap.h"
#include "bitmap/imageformat.h"
#include "bitmap/psd.h"
#include "bitmap/tgaloader.h"
#include "bitmap/tgawriter.h"
#include "vtf/vtf.h"
#include "datamodel/dmattribute.h"
#include "datamodel/dmelement.h"
#include "datamodel/idatamodel.h"
#include "fbxutils/dmfbxserializer.h"
#include "filesystem.h"
#include "movieobjects/dmefaceset.h"
#include "movieobjects/dmematerial.h"
#include "movieobjects/dmemesh.h"
#include "movieobjects/dmemodel.h"
#include "movieobjects/dmobjserializer.h"
#include "movieobjects/dmsmdserializer.h"
#include "movieobjects/dmeanimationlist.h"
#include "movieobjects/dmeclip.h"
#include "movieobjects/dmechannel.h"
#include "movieobjects/dmelog.h"
#include "steam/steam_api.h"
#include "tier1/fmtstr.h"
#include "tier1/utlsymbol.h"
#include "tier2/fileutils.h"
#include "tier2/p4helpers.h"
#include "../public/zip_utils.h"


// Last include
#include "tier0/memdbgon.h"

#ifdef BEGIN_DEFINE_LOGGING_CHANNEL
BEGIN_DEFINE_LOGGING_CHANNEL( LOG_ITEMTEST, "ItemTest", LCF_CONSOLE_ONLY, LS_MESSAGE );
ADD_LOGGING_CHANNEL_TAG( "ItemTest" );
END_DEFINE_LOGGING_CHANNEL();
#endif

// This isn't available in the TF runtime (yet?)
#ifndef FUNCTION_LINE_STRING
#define FUNCTION_LINE_STRINGIFY(x) #x
#define FUNCTION_LINE_TOSTRING(x) FUNCTION_LINE_STRINGIFY(x)
#define FUNCTION_LINE_STRING __FUNCTION__ "(" FUNCTION_LINE_TOSTRING(__LINE__) "): "
#endif

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
enum
{
	k64KB = 65536
};


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
static CSteamAPIContext g_SteamAPIContext;
bool CItemUpload::m_bDev = false;
bool CItemUpload::m_bIgnoreEnvVars = false;
bool CItemUpload::m_bP4 = false;
CUtlString CItemUpload::m_szForcedSteamID = "";
CItemTestManifest *CItemUpload::m_pItemTestManifest = NULL;
static bool g_bCompilePreview = false;
static const char* kVMT = "VMT%d";

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
inline bool UtlStringLessThan( const CUtlString &sLhs, const CUtlString &sRhs )
{
	return CaselessStringLessThanIgnoreSlashes( sLhs.String(), sRhs.String() );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CItemLog::Msg( const char *pszFormat, ... ) const
{
	va_list args;
	va_start( args, pszFormat );
	CFmtStrMax str;
	str.AppendFormatV( pszFormat, args );
	Log( kItemtest_Log_Info, str );
	va_end( args );
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CItemLog::Warning( const char *pszFormat, ... ) const
{
	va_list args;
	va_start( args, pszFormat );
	CFmtStrMax str;
	str.AppendFormatV( pszFormat, args );
	Log( kItemtest_Log_Warning, str );
	va_end( args );
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CItemLog::Error( const char *pszFormat, ... ) const
{
	va_list args;
	va_start( args, pszFormat );
	CFmtStrMax str;
	str.AppendFormatV( pszFormat, args );
	Log( kItemtest_Log_Error, str );
	va_end( args );
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CItemLog::Log( ItemtestLogLevel_t nLogLevel, const char *pszMessage ) const
{
	if ( m_pItemLog && m_pItemLog != this )
	{
		m_pItemLog->Log( nLogLevel, pszMessage );
		return;
	}

	switch ( nLogLevel )
	{
	case kItemtest_Log_Info:
		Log_Msg( LOG_ITEMTEST, "%s", pszMessage );
		break;
	case kItemtest_Log_Warning:
		Log_Warning( LOG_ITEMTEST, "%s", pszMessage );
		break;
	case kItemtest_Log_Error:
		Log_Error( LOG_ITEMTEST, "%s", pszMessage );
		break;
	}
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CItemUpload::InitManifest( void )
{
	if ( m_pItemTestManifest )
		return true;

	m_pItemTestManifest = new CItemTestManifest( "scripts/itemtest_manifest.txt", new CItemLog() );
	return m_pItemTestManifest->IsValid();
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CItemUpload::SanitizeName( const char *pszName, CUtlString &sCleanName )
{
	char pszTemp[MAX_PATH];

	V_strcpy_safe( pszTemp, pszName );

	// Convert to lowercase, strip punctuation and turn spaces into underscores
	char *pszSrc = pszTemp;
	char *pszDst = pszTemp;
	while ( *pszSrc )
	{
		char c = *pszSrc++;

		if ( c >= 'a' && c <= 'z' )
		{
			*pszDst++ = c;
		}
		else if ( c >= 'A' && c <= 'Z' )
		{
			*pszDst++ = c - 'A' + 'a';
		}
		else if ( c >= '0' && c <= '9' )
		{
			*pszDst++ = c;
		}
		else if ( V_isspace(c) || c == '_' )
		{
			*pszDst++ = '_';
		}
		else
		{
			// Punctuation or non-ASCII characters, skip 'em!
		}
	}
	*pszDst = '\0';

	sCleanName = pszTemp;

	return !sCleanName.IsEmpty();
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CItemTestManifest::CItemTestManifest( const char *pszManifestFile, CItemLog *pItemLog ) :
	m_pItemLog( pItemLog ),
	m_vecVMTTextureRemaps( UtlStringLessThan )
{
	m_pManifestKV = NULL;
	m_pItemDirectory = "";
	m_pAnimationDirectory = "";
	m_pIconDirectory = "";
	m_pZipSourceDirectory = "";
	m_pZipOutputDirectory = "";
	m_pQCTemplate = "";
	m_pQCITemplate = "";
	m_bTerseMessages = false;
	m_bItemPathUsesSteamId = true;

	m_pManifestKV = new KeyValues( pszManifestFile );
	if ( !m_pManifestKV->LoadFromFile(g_pFullFileSystem, pszManifestFile, "MOD") )
	{
		m_pManifestKV->deleteThis();
		m_pManifestKV = NULL;

		m_pItemLog->Warning( "ERROR: Failed to load manifest file: %s\n", pszManifestFile );
		return;
	}

	// Class list
	if ( !ParseStringsFromManifest( m_pManifestKV, "classes", m_vecClasses ) )
		return;

	// MDL Extensions
	if ( !ParseStringsFromManifest( m_pManifestKV, "mdl_files", m_vecMDLExtensions ) )
		return;

	// Animation MDL Extensions
	if ( !ParseStringsFromManifest( m_pManifestKV, "animation_mdl_files", m_vecAnimationMDLExtensions ) )
		return;

	// Material types
	KeyValues *pKVMaterialTypes = m_pManifestKV->FindKey("material_types");
	if ( !pKVMaterialTypes )
	{
		m_pItemLog->Warning( "ERROR: Failed to find a 'material_types' section in manifest file: %s\n", pszManifestFile );
		return;
	}
	FOR_EACH_SUBKEY( pKVMaterialTypes, pKVMaterialType )
	{
		const char *pszString = pKVMaterialType->GetName();
		int nIdx = m_vecMaterialTypes.AddToTail();
		m_vecMaterialTypes[nIdx].pszMaterialType = pszString;
	}

	const char *pszDefaultMatTypeString = m_pManifestKV->GetString("default_material_type");
	if ( !pszDefaultMatTypeString || !pszDefaultMatTypeString[0] )
	{
		m_pItemLog->Warning( "ERROR: Failed to find a 'default_material_type' string in manifest file: %s\n", pszManifestFile );
		return;
	}
	m_nDefaultMaterialType = GetMaterialType( pszDefaultMatTypeString );
	if ( m_nDefaultMaterialType == kInvalidMaterialType )
	{
		m_pItemLog->Warning( "ERROR: Default material type '%s' wasn't found in the material type list in manifest file: %s\n", m_nDefaultMaterialType, pszManifestFile );
		return;
	}

	// Material skins
	KeyValues *pKVMaterialSkins = m_pManifestKV->FindKey("material_skins");
	if ( pKVMaterialSkins )
	{
		FOR_EACH_SUBKEY( pKVMaterialSkins, pKVMaterialSkin )
		{
			const char *pszString = pKVMaterialSkin->GetName();
			int nIdx = m_vecMaterialSkins.AddToTail();
			m_vecMaterialSkins[nIdx].pszMaterialSkin = pszString;
			m_vecMaterialSkins[nIdx].pszFilenameAppend = pKVMaterialSkin->GetString("file_append");
		}

		const char *pszDefaultMatSkinString = m_pManifestKV->GetString("default_material_skin");
		if ( !pszDefaultMatSkinString || !pszDefaultMatSkinString[0] )
		{
			m_pItemLog->Warning( "ERROR: Failed to find a 'default_material_skin' string in manifest file: %s\n", pszManifestFile );
			return;
		}
		m_nDefaultMaterialSkin = GetMaterialSkin( pszDefaultMatSkinString );
		if ( m_nDefaultMaterialSkin == kInvalidMaterialSkin )
		{
			m_pItemLog->Warning( "ERROR: Default material skin '%s' wasn't found in the material skin list in manifest file: %s\n", pszDefaultMatSkinString, pszManifestFile );
			m_nDefaultMaterialSkin = 0;
			return;
		}
	}
	else
	{
		m_nDefaultMaterialSkin = 0;
	}

	// Texture types
	KeyValues *pKVTextureTypes = m_pManifestKV->FindKey("texture_types");
	if ( !pKVTextureTypes )
	{
		m_pItemLog->Warning( "ERROR: Failed to find a 'texture_types' section in manifest file: %s\n", pszManifestFile );
		return;
	}
	FOR_EACH_SUBKEY( pKVTextureTypes, pKVTexture )
	{
		const char *pszString = pKVTexture->GetName();
		int nIdx = m_vecTextureTypes.AddToTail();
		m_vecTextureTypes[nIdx].pszTextureType = pszString;
		m_vecTextureTypes[nIdx].bOptional = pKVTexture->GetBool( "optional" );
		m_vecTextureTypes[nIdx].pkvAddToVTEXConfig = pKVTexture->FindKey("add_to_vtex_config");
	}

	// VMT templates
	KeyValues *pKVTemplates = m_pManifestKV->FindKey("vmt_templates");
	if ( !pKVTemplates )
	{
		m_pItemLog->Warning( "ERROR: Failed to find a 'vmt_templates' section in manifest file: %s\n", pszManifestFile );
		return;
	}

	KeyValues *pKVClassTemplates = pKVTemplates->FindKey("classes");
	if ( pKVClassTemplates )
	{
		m_vecClassTemplates.SetCount( m_vecClasses.Count() );
		for ( int i = 0; i < m_vecClassTemplates.Count(); ++i )
		{
			m_vecClassTemplates[ i ] = NULL;
		}

		FOR_EACH_SUBKEY( pKVClassTemplates, pKVClassTemplate )
		{
			const char *pszHero = pKVClassTemplate->GetName();
			const char *pszTemplate = pKVClassTemplate->GetString();

			int iClass = m_vecClasses.Find( pszHero );
			if ( iClass == m_vecClasses.InvalidIndex() )
			{
				m_pItemLog->Warning( "ERROR: Found an invalid class '%s' in the vmt_templates entries in manifest file: %s\n", pszHero, pszManifestFile );
				return;
			}

			m_vecClassTemplates[iClass] = pszTemplate;
		}
	}

	KeyValues *pKVVMTRemaps = pKVTemplates->FindKey("vmt_texture_settings");
	if ( pKVVMTRemaps )
	{
		FOR_EACH_SUBKEY( pKVVMTRemaps, pKVRemap )
		{
			const char *pszVMTVar = pKVRemap->GetName();
			const char *pszTexture = pKVRemap->GetString();

			m_vecVMTTextureRemaps.Insert( pszTexture, pszVMTVar );
		}
	}

	// Icon types
	KeyValues *pKVIconTypes = m_pManifestKV->FindKey("icon_types");
	if ( pKVIconTypes )
	{
		FOR_EACH_SUBKEY( pKVIconTypes, pKVIcon )
		{
			const char *pszString = pKVIcon->GetName();
			int nIdx = m_vecIconTypes.AddToTail();
			m_vecIconTypes[nIdx].pszIconType = pszString;
			m_vecIconTypes[nIdx].nWidth = pKVIcon->GetInt( "width" );
			m_vecIconTypes[nIdx].nHeight = pKVIcon->GetInt( "height" );
			m_vecIconTypes[nIdx].pszFilenameAppend = pKVIcon->GetString("file_append");
			m_vecIconTypes[nIdx].pkvAddToVTEXConfig = pKVIcon->FindKey("add_to_vtex_config");

			KeyValues *pKVVMT = pKVIcon->FindKey("vmt_template");
			if ( pKVVMT )
			{
				m_vecIconTypes[nIdx].pkvVMTTemplate = pKVVMT->GetFirstSubKey();
			}
			else
			{
				m_vecIconTypes[nIdx].pkvVMTTemplate = NULL;
			}
		}
	}

	m_pItemDirectory = m_pManifestKV->GetString("item_directory");
	m_pAnimationDirectory = m_pManifestKV->GetString("animation_directory");
	m_pIconDirectory = m_pManifestKV->GetString("icon_directory");
	m_pZipSourceDirectory = m_pManifestKV->GetString("archive_source_path");
	m_pZipOutputDirectory = m_pManifestKV->GetString("archive_output_path");

	m_pQCTemplate = m_pManifestKV->GetString("qc_template");
	if ( V_strlen( m_pQCTemplate ) == 0 )
	{
		m_pItemLog->Warning( "ERROR: qc_template not defined in manifest file: %s\n", pszManifestFile );
		return;
	}

	m_pQCITemplate = m_pManifestKV->GetString( "qci_template" );
	if ( V_strlen( m_pQCITemplate ) == 0 )
	{
		m_pItemLog->Warning( "ERROR: qci_template not defined in manifest file: %s\n", pszManifestFile );
		return;
	}

	const char *pszQCLODDistances = m_pManifestKV->GetString("qc_lod_distances");
	CUtlVector<char*> vecQCLODDistances;
	V_SplitString( pszQCLODDistances, ",", vecQCLODDistances );
	m_vecQCLODDistances.SetCount( vecQCLODDistances.Count() );
	for ( int i = 0; i < vecQCLODDistances.Count(); ++i )
	{
		m_vecQCLODDistances[i] = V_atoi( vecQCLODDistances[i] );
	}

	m_bTerseMessages = m_pManifestKV->GetBool( "terse_messages", false );
	m_bItemPathUsesSteamId = m_pManifestKV->GetBool( "item_path_has_steamid", true );

}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CItemTestManifest::ParseStringsFromManifest( KeyValues *pKV, const char *pszKeyName, CUtlVector< CUtlString > &vecList )
{
	KeyValues *pKVSub = pKV->FindKey(pszKeyName);
	if ( !pKVSub )
	{
		m_pItemLog->Warning( "ERROR: Failed to find a '%s' section in manifest file.\n", pszKeyName );
		return false;
	}

	FOR_EACH_SUBKEY( pKVSub, pKVSubKey )
	{
		const char *pszString = pKVSubKey->GetName();
		vecList.AddToTail( pszString );
	}

	return true;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int	CItemTestManifest::GetMaterialType( const char *pszMaterialType )
{
	FOR_EACH_VEC( m_vecMaterialTypes, i )
	{
		if ( V_stricmp(m_vecMaterialTypes[i].pszMaterialType, pszMaterialType) == 0 )
			return i;
	}

	return kInvalidMaterialType;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int	CItemTestManifest::GetMaterialSkin( const char *pszMaterialSkin )
{
	FOR_EACH_VEC( m_vecMaterialSkins, i )
	{
		if ( V_stricmp(m_vecMaterialSkins[i].pszMaterialSkin, pszMaterialSkin) == 0 )
			return i;
	}

	return kInvalidMaterialSkin;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int	CItemTestManifest::GetTextureType( const char *pszTextureType )
{
	FOR_EACH_VEC( m_vecTextureTypes, i )
	{
		if ( V_stricmp(m_vecTextureTypes[i].pszTextureType, pszTextureType) == 0 )
			return i;
	}

	return -1;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
KeyValues *CItemTestManifest::GetTextureAddToVTEXConfig( const char *pszTextureType )
{
	int nIdx = GetTextureType(pszTextureType);
	if ( nIdx == -1 )
		return NULL;

	return m_vecTextureTypes[nIdx].pkvAddToVTEXConfig;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int	CItemTestManifest::GetIconType( const char *pszIconType )
{
	FOR_EACH_VEC( m_vecIconTypes, i )
	{
		if ( V_stricmp(m_vecIconTypes[i].pszIconType, pszIconType) == 0 )
			return i;
	}

	return -1;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CItemTestManifest::GetIconDimensions( int nIcon, int &nWidth, int &nHeight )
{
	if ( nIcon >= 0 && nIcon < m_vecIconTypes.Count() )
	{
		nWidth = m_vecIconTypes[nIcon].nWidth;
		nHeight = m_vecIconTypes[nIcon].nHeight;
		return true;
	}
	else
	{
		nWidth = 0;
		nHeight = 0;
		return false;
	}
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const char *CItemTestManifest::GetVMTVarForTextureType( const char *pszTexture ) 
{ 
	int nIndex = m_vecVMTTextureRemaps.Find(pszTexture);
	if ( nIndex == m_vecVMTTextureRemaps.InvalidIndex() )
		return NULL;

	return m_vecVMTTextureRemaps[nIndex];
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int GetClassCount()
{
	return CItemUpload::Manifest()->GetNumClasses();
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const char *GetClassString( int i )
{
	return ( i < 0 || i >= GetClassCount() ) ? NULL : CItemUpload::Manifest()->GetClass(i);
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const char *GetClassString( const char *pszClassString )
{
	if ( !pszClassString || V_strlen( pszClassString ) <= 0 )
		return NULL;

	// Make sure it exists in our manifest file
	for ( int i = 0; i < CItemUpload::Manifest()->GetNumClasses(); i++ )
	{
		const char *pszHero = CItemUpload::Manifest()->GetClass(i);
		if ( V_stricmp(pszHero, pszClassString) == 0 )
			return pszHero;
	}

	//Log_Warning( LOG_ITEMTEST, "Invalid class specified: %s\n", pszClassString );

	return NULL;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int GetClassIndex( const char *pszClassString )
{
	const char *pszCleanClassString = GetClassString( pszClassString );

	if ( !pszCleanClassString )
		return -1;

	for ( int i = 0; i < GetClassCount(); ++i )
	{
		if ( !V_stricmp( pszCleanClassString, GetClassString( i ) ) )
		{
			return i;
		}
	}

	return -1;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
template < class T >
const Vector &CItemUploadGame< T >::GetBipHead( int i )
{
	static const Vector vOrigin( 0, 0, 0 );

	return ( i < 0 || i >= GetClassCount() ) ? vOrigin : CItemUploadGame< T >::s_vBipHead[i];
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
template < class T >
const RadianEuler &CItemUploadGame< T >::GetBipHeadRotation( int i )
{
	static const RadianEuler eOrigin( 0, 0, 0 );

	return ( i < 0 || i >= GetClassCount() ) ? eOrigin : CItemUploadGame< T >::s_eBipHead[i];
}



//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const Vector CItemUploadGame< CItemUploadTF >::s_vBipHead[] =
{
	Vector(  0,				76.142968, -0.39608 ),	// demo
	Vector(  0,				69.030248, -1.264691 ),	// engineer
	Vector( -0.000138993,	79.541796, -3.352982 ),	// heavy
	Vector( -0.000111273,	76.504372, -0.565035 ),	// medic
	Vector( -0.000102534,	71.788881,  2.145585 ),	// pyro
	Vector( 0,				73.501752, -1.429994 ),	// scout
	Vector( 0,				75.982279, -3.858408 ),	// sniper
	Vector( 0,				75.194376, -1.120618 ),	// soldier
	Vector( 0,				75.679732, -2.87915  )	// spy
};

COMPILE_TIME_ASSERT( ARRAYSIZE( CItemUploadGame< CItemUploadTF >::s_vBipHead ) == CItemUploadTF::kClassCount );

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const RadianEuler CItemUploadGame< CItemUploadTF >::s_eBipHead[] =
{
	RadianEuler( DEG2RAD( -180.0 ),   0, 0 ),	// demo
	RadianEuler( DEG2RAD( -170.459 ), 0, 0 ),	// engineer
	RadianEuler( DEG2RAD( -180.0 ),   0, 0 ),	// heavy
	RadianEuler( DEG2RAD( -180.0 ),   0, 0 ),	// medic
	RadianEuler( DEG2RAD( -154.175 ), 0, 0 ),	// pyro
	RadianEuler( DEG2RAD( -173.451 ), 0, 0 ),	// scout
	RadianEuler( DEG2RAD( -172.722 ), 0, 0 ),	// sniper
	RadianEuler( DEG2RAD( -179.729 ), 0, 0 ),	// soldier
	RadianEuler( DEG2RAD( -180.0 ),   0, 0 )	// spy
};

COMPILE_TIME_ASSERT( ARRAYSIZE( CItemUploadGame< CItemUploadTF >::s_eBipHead ) == CItemUploadTF::kClassCount );


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
template class CItemUploadGame< CItemUploadTF >;


//-----------------------------------------------------------------------------
//
// Try and get the Steam Account ID and return it as a 10 character hex
// string prefixed with 0x.
//
//-----------------------------------------------------------------------------
bool CItemUpload::GetSteamId( CUtlString &sSteamId )
{
	if ( GetDevMode() )
	{
		sSteamId = "";
		return true;
	}

	const char *pszForcedSteamID = GetForcedSteamID();
	if ( pszForcedSteamID && pszForcedSteamID[0] )
	{
		sSteamId = pszForcedSteamID;
		return true;
	}

	bool bRetVal = false;

	char szBuf[ BUFSIZ ];
	szBuf[0] = '\0';

	// Try to query steam directly, this will fail if steam isn't running or the
	// process calling this function wasn't launched through steam (or through a
	// process launched through steam) or there isn't a steam_appid.txt in
	// the same directory as the executable running this (use 440 for TF).

	bool shutdownSteam = false;
	if ( !SteamClient() )
	{
		SteamAPI_InitSafe();
		SteamAPI_SetTryCatchCallbacks( false ); // We don't use exceptions, so tell steam not to use try/catch in callback handlers
		shutdownSteam = true;
	}

	if ( SteamAPI_IsSteamRunning() )
	{
		g_SteamAPIContext.Init();

		ISteamUser *pSteamUser = g_SteamAPIContext.SteamUser();

		if ( pSteamUser )
		{
			CSteamID cSteamID = pSteamUser->GetSteamID();
			const uint32 nAccountID = cSteamID.GetAccountID();
			V_snprintf( szBuf, ARRAYSIZE( szBuf ), "0x%08x", nAccountID );

			bRetVal = true;
		}
	}

	if ( shutdownSteam )
	{
		SteamAPI_Shutdown();
	}

	sSteamId = szBuf;

	return bRetVal;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CItemUpload::GetVProjectDir( CUtlString &sVProjectDir )
{
	char szVProject[ BUFSIZ ] = "";

	GetModSubdirectory( "", szVProject, ARRAYSIZE( szVProject ) );
	V_StripTrailingSlash( szVProject );

	sVProjectDir = szVProject;

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CItemUpload::GetVMod( CUtlString &sVMod )
{
	sVMod = "";

	CUtlString sVProjectDir;
	if ( !GetVProjectDir( sVProjectDir ) )
		return false;

	char szBuf[ k64KB ];
	V_FileBase( sVProjectDir.String(), szBuf, ARRAYSIZE( szBuf ) );

	sVMod = szBuf;

	return true;
}


//-----------------------------------------------------------------------------
// Guess where the SourceSDK root is based on executable directory...
// If there's a /bin/orangebox/bin/ in the executable path we know 
// the SourceSDK is above it, otherwise we don't know anything and false is
// returned
//
// If DevMode (-dev) then always returns false
//
// TODO: This is for TF & SourceSDK only and likely this is the only
//       weird configuration this hack needs to be done with.  If it's
//       a normal game/content tree then none of this hacky stuff
//       should be needed
//-----------------------------------------------------------------------------
bool CItemUpload::GetSourceSDKFromExe( CUtlString &sSourceSDK, CUtlString &sSourceSDKBin )
{
	if ( GetDevMode() )
		return false;

	CUtlString sCurrentExecutableFileName;
	GetCurrentExecutableFileName( sCurrentExecutableFileName );

	// Special hack for executables running out of the orange box SDK
	sCurrentExecutableFileName.FixSlashes( '/' );
	const char *pszBinOrangeBoxBin = V_strstr( sCurrentExecutableFileName.String(), "/bin/orangebox/bin/" );
	if ( pszBinOrangeBoxBin )
	{
		sSourceSDK.SetDirect( sCurrentExecutableFileName.String(), pszBinOrangeBoxBin - sCurrentExecutableFileName.String() );

		char szBinDir[ MAX_PATH ];
		V_ExtractFilePath( sCurrentExecutableFileName.String(), szBinDir, ARRAYSIZE( szBinDir ) );
		sSourceSDKBin = szBinDir;
		return true;
	}

	// Get Source SDK path
	HKEY hKey;
	char szSDKPath[ k64KB ];
	char szEngineVersion[ k64KB ];
	szSDKPath[0] = szEngineVersion[0] = '\0';

	GetEnvironmentVariable( "SOURCESDK", szSDKPath, sizeof( szSDKPath ) );

	if ( ERROR_SUCCESS == RegOpenKey( HKEY_CURRENT_USER, "Software\\Valve\\Source SDK", &hKey ) ) 
	{
		DWORD dwSize = sizeof( szEngineVersion );
		RegQueryValueEx( hKey, "EngineVer", NULL, NULL, (LPBYTE)szEngineVersion, &dwSize );
		RegCloseKey( hKey );
	}
	else
	{
		// Let's assume orange box for now
		V_strcpy_safe( szEngineVersion, "orangebox" );
	}

	if ( *szSDKPath )
	{
		// Normalize slashes to be consistent with the orange box SDK code above
		V_FixSlashes( szSDKPath, '/' );

		sSourceSDK = szSDKPath;
		sSourceSDKBin.Format( "%s/bin/%s/bin", szSDKPath, szEngineVersion );
		return true;
	}

	return false;
}


//-----------------------------------------------------------------------------
// Returns $SOURCESDK_content/FileName( $VPROJECT ) if it SOURCESDK is set
// otherwise returns VCONTENT if it is set, otherwise returns
// $VPROJECT/../
//-----------------------------------------------------------------------------
static bool CheckContentPath( CUtlString &sContentDir )
{
	char szContentDir[ MAX_PATH ];
	V_FixupPathName( szContentDir, ARRAYSIZE( szContentDir ), sContentDir );
	V_StripTrailingSlash( szContentDir );
	sContentDir = szContentDir;

	return g_pFullFileSystem->IsDirectory( sContentDir );
}
bool CItemUpload::GetContentDir( CUtlString &sContentDir )
{
	// Without VPROJECT set, can't figure anything out
	CUtlString sVMod;
	if ( !GetVMod( sVMod ) )
		return false;

	char szBuf[ k64KB ];

	// The game includes its own content directory?
	if ( IgnoreEnvironmentVariables() )
	{
		CUtlString sVProject;
		if ( !GetVProjectDir( sVProject ) )
			return false;
		sVProject.FixSlashes( '/' );

		// When we run in Steam, we get something like this back:
		//		"u:/steambeta/steamapps/common/[staging] dota 2/dota"
		// We need to trim off the game name, and append content.
		V_strcpy_safe( szBuf, sVProject );
		if ( !V_StripLastDir( szBuf, ARRAYSIZE( szBuf ) ) )
			return false;

		sContentDir = szBuf;
		sContentDir += "content/";
		sContentDir += sVMod;
		return true;
	}

	CUtlString sSourceSDK, sSourceSDKBin;

	// Check for the game/content layout in dev builds
	CUtlString sVProject;
	if ( GetVProjectDir( sVProject ) )
	{
		sVProject.FixSlashes( '/' );
		const char *pszGame = V_stristr( sVProject.String(), "/game/" );
		if ( pszGame )
		{
			sContentDir = sVProject;
			sContentDir.SetLength( pszGame - sVProject.String() );
			sContentDir += "/content/";
			sContentDir += sVMod;

			V_FixupPathName( szBuf, ARRAYSIZE( szBuf ), sContentDir.String() );
			sContentDir = szBuf;

			if ( CheckContentPath( sContentDir ) )
			{
				return true;
			}
		}
		else
		{
			// try to look for workshop/content in the mod dir
			sContentDir = sVProject;
			sContentDir += "/workshop/content";

			V_FixupPathName( szBuf, ARRAYSIZE( szBuf ), sContentDir.String() );
			sContentDir = szBuf;

			if ( CheckContentPath( sContentDir ) )
			{
				return true;
			}
		}
	}

	// Check for the VCONTENT environment variable
	if ( GetEnvironmentVariable( "VCONTENT", szBuf, ARRAYSIZE( szBuf ) ) != 0 )
	{
		sContentDir = szBuf;
		sContentDir += "/";
		sContentDir += sVMod;
		V_FixupPathName( szBuf, ARRAYSIZE( szBuf ), sContentDir.String() );
		sContentDir = szBuf;

		if ( CheckContentPath( sContentDir ) )
		{
			return true;
		}
	}

	// Check for the Source SDK in steam builds
	if ( GetSourceSDKFromExe( sSourceSDK, sSourceSDKBin ) )
	{
		sContentDir = sSourceSDK;
		sContentDir += "_content\\";
		sContentDir += sVMod;

		V_FixupPathName( szBuf, ARRAYSIZE( szBuf ), sContentDir.String() );
		sContentDir = szBuf;

		if ( CheckContentPath( sContentDir ) )
		{
			return true;
		}
	}

	return false;
}



//-----------------------------------------------------------------------------
//
// Find the directory for the binaries
//
//-----------------------------------------------------------------------------
static bool CheckToolPath( CUtlString &sBinDir )
{
	char szBinDir[ MAX_PATH ];
	V_FixupPathName( szBinDir, ARRAYSIZE( szBinDir ), sBinDir );
	V_StripTrailingSlash( szBinDir );
	sBinDir = szBinDir;

	char szVtexFileName[ MAX_PATH ];
	V_ComposeFileName( sBinDir, "vtex.exe", szVtexFileName, ARRAYSIZE( szVtexFileName ) );

	char szStudiomdlFileName[ MAX_PATH ];
	V_ComposeFileName( sBinDir, "studiomdl.exe", szStudiomdlFileName, ARRAYSIZE( szStudiomdlFileName ) );

	return g_pFullFileSystem->FileExists( szVtexFileName ) && g_pFullFileSystem->FileExists( szStudiomdlFileName );
}
bool CItemUpload::GetBinDirectory( CUtlString &sBinDir )
{
	// Get the full path to the executable this code is running in
	// this should be the 'bin' directory we want... just to be sure
	// make sure vtex.exe and studiomdl.exe exist in that directory

	CUtlString sCurrentExecutableFileName;
	GetCurrentExecutableFileName( sCurrentExecutableFileName );

	char szBinDir[ MAX_PATH ];
	V_ExtractFilePath( sCurrentExecutableFileName.String(), szBinDir, ARRAYSIZE( szBinDir ) );
	sBinDir = szBinDir;

	if ( CheckToolPath( sBinDir ) )
	{
		return true;
	}

	// Check for the game/bin directory
	CUtlString sVProject;
	if ( GetVProjectDir( sVProject ) && !sVProject.IsEmpty() )
	{
		sVProject.FixSlashes( '/' );
		const char *pszGame = V_stristr( sVProject.String(), "/game/" );
		if ( pszGame )
		{
			sBinDir = sVProject;
			sBinDir.SetLength( pszGame - sVProject.String() );
			sBinDir += "/game/bin";

			if ( CheckToolPath( sBinDir ) )
			{
				return true;
			}
		}

		// When we run in Steam, we get something like this back:
		//		"u:/steambeta/steamapps/common/[staging] dota 2/dota"
		// We need to trim off the game name, and append bin.
		V_strcpy_safe( szBinDir, sVProject );
		if ( !V_StripLastDir( szBinDir, ARRAYSIZE( szBinDir ) ) )
			return false;

		sBinDir = szBinDir;
		sBinDir += "/bin";

		if ( CheckToolPath( sBinDir ) )
		{
			return true;
		}
	}

	// Check for the Source SDK in steam builds
	CUtlString sSourceSDK;
	if ( !GetDevMode() && GetSourceSDKFromExe( sSourceSDK, sBinDir ) )
	{
		if ( CheckToolPath( sBinDir ) )
		{
			return true;
		}
	}

	return false;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CItemUpload::FileExists( const char *pszFilename )
{
	DWORD attribs = ::GetFileAttributesA( pszFilename );
	if ( attribs == INVALID_FILE_ATTRIBUTES )
		return false;

	return ( ( attribs & FILE_ATTRIBUTE_DIRECTORY ) == 0 );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CItemUpload::CopyFiles( const char *pszSourceDir, const char *pszPattern, const char *pszDestDir )
{
	char szFindPattern[ k64KB ];
	bool bAllSucceeded = true;

	V_snprintf( szFindPattern, sizeof( szFindPattern ), "%s%s", pszSourceDir, pszPattern );

	WIN32_FIND_DATA findData;
	HANDLE hFind = FindFirstFile( szFindPattern, &findData );
	if ( hFind == INVALID_HANDLE_VALUE )
	{
		return false;
	}
	else
	{
		do
		{
			char szSrcPath[ k64KB ];
			char szDestPath[ k64KB ];

			V_snprintf( szSrcPath, sizeof( szSrcPath ), "%s%s", pszSourceDir, findData.cFileName );
			V_snprintf( szDestPath, sizeof( szDestPath ), "%s\\%s", pszDestDir, findData.cFileName );

			DeleteFile( szDestPath );
			::CopyFile( szSrcPath, szDestPath, false );
			bAllSucceeded &= FileExists( szDestPath );

		} while ( FindNextFile( hFind, &findData ) );
		FindClose( hFind );

		return bAllSucceeded;
	}
}


static bool DoFileCopy( const char *pszSourceFile, const char *pszDestFile )
{
	int             remaining, count;
	char			buf[4096];
	FileHandle_t	in, out;

	in = g_pFullFileSystem->Open( pszSourceFile, "rb" );

	AssertMsg( in, "DoFileCopy: Input file failed to open" );

	if ( in == FILESYSTEM_INVALID_HANDLE )
		return false;
		
	// create directories up to the cache file
	char szDestPath[MAX_PATH];
	V_ExtractFilePath( pszDestFile, szDestPath, sizeof( szDestPath ) );
	g_pFullFileSystem->CreateDirHierarchy( szDestPath );

	out = g_pFullFileSystem->Open( pszDestFile, "wb" );

	AssertMsg( out, "DoFileCopy: Output file failed to open" );
	
	if ( out == FILESYSTEM_INVALID_HANDLE )
	{
		g_pFullFileSystem->Close( in );
		return false;
	}
		
	remaining = g_pFullFileSystem->Size( in );
	while ( remaining > 0 )
	{
		if (remaining < sizeof(buf))
		{
			count = remaining;
		}
		else
		{
			count = sizeof(buf);
		}
		g_pFullFileSystem->Read( buf, count, in );
		g_pFullFileSystem->Write( buf, count, out );
		remaining -= count;
	}

	g_pFullFileSystem->Close( in );
	g_pFullFileSystem->Close( out );   
	
	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CItemUpload::CopyFile( const char *pszSourceFile, const char *pszDestFile )
{
	if ( ::CopyFile( pszSourceFile, pszDestFile, false ) == 0 )
		return false;

	DWORD nFileAttr = GetFileAttributes( pszDestFile );
	if ( nFileAttr == INVALID_FILE_ATTRIBUTES )
		return false;

	nFileAttr &= ~FILE_ATTRIBUTE_READONLY;
	SetFileAttributes( pszDestFile, nFileAttr );

	return true;
}


//=============================================================================
//
//=============================================================================
static bool RemoveTextBlock( const char *str, char const *search, char *pszOutBuf, int nSizeofOutBuf )
{
	if ( str != pszOutBuf )
	{
		V_strncpy( pszOutBuf, str, nSizeofOutBuf );
	}

	bool changed = false;
	if ( !V_strstr( str, search ) )
	{
		return false;
	}

	int offset = 0;
	while ( true )
	{
		char* pos = V_strstr( str + offset, search );
		if ( !pos )
		{
			break;
		}

		CUtlString temp = str;

		// Found an instance
		int left = pos - str;
		CUtlString strLeft = temp.Slice( 0, left );

		pos = V_strstr( str + left, "}" );
		if ( !pos )
		{
			AssertMsg( pos, "cannot find end of text block\n" );
			return false;
		}

		int right = pos - str + 1;
		CUtlString strRight = temp.Slice( right );

		temp = strLeft;
		temp += strRight;

		// Replace entire string
		V_strncpy( pszOutBuf, temp.String(), nSizeofOutBuf );

		offset = right;

		changed = true;
	}

	return changed;
}


//=============================================================================
//
//=============================================================================
CTargetBase::CTargetBase( CAsset *pAsset, const CTargetBase *pTargetParent )
: CItemLog( pAsset )
, m_pAsset( pAsset )
, m_nRefCount( 0 )
, m_pTargetParent( pTargetParent )
, m_bIgnoreP4( false )
, m_kvCustomKeys( new KeyValues( "custom keys" ) )
{
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetBase::Compile()
{
	CUtlString sName;
	if ( !GetOutputPath( sName, 0, PATH_FLAG_FILE | PATH_FLAG_EXTENSION ) )
	{
		Warning( "CTarget%s::Compile - GetOutputPath failed\n", GetTypeString() );
		return false;
	}

	CUtlString sTmp;
	if ( !IsOk( sTmp ) )
	{
		Warning( "CTarget%s::Compile( %s ) - Not Valid: %s\n", GetTypeString(), sName.String(), sTmp.String() );
		return false;
	}

	// Compile all inputs first

	if ( !CreateOutputDirectory() )
	{
		Warning( "CTarget%s::Compile - CreateOutputDirectory failed\n", GetTypeString() );
		return false;
	}

	CUtlVector< CTargetBase * > inputs;
	bool bRet = GetInputs( inputs );

	if ( !bRet )
	{
		Warning( "CTarget%s::Compile - GetInputs failed\n", GetTypeString() );
		return bRet;
	}

	for ( int i = 0; i < inputs.Count(); ++i )
	{
		CTargetBase *pTargetBase = inputs.Element( i );

		if ( !pTargetBase )
		{
			Warning( "WARNING: CTarget%s::Compile - Target %d NULL\n", GetTypeString(), i );
			continue;
		}

		if ( !pTargetBase->Compile() )
		{
			Warning( "WARNING: CTarget%s::Compile - Target %d Compile Failed\n", GetTypeString(), i );
			bRet = false;
			break;
		}
	}

	return bRet;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const char *CTargetBase::GetItemDirectory() const
{
	if ( m_pTargetParent )
	{
		return m_pTargetParent->GetItemDirectory();
	}
	else
	{
		return CItemUpload::Manifest()->GetItemDirectory();
	}
}


//-----------------------------------------------------------------------------
// Return the number of files that are output from this CTarget
//-----------------------------------------------------------------------------
int CTargetBase::GetOutputCount() const
{
	const ExtensionList *pList = GetExtensionsAndCount();
	return (pList ? pList->Count() : 0);
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetBase::GetOutputPath(
	CUtlString &sOutputPath,
	int nIndex /* = 0 */,
	uint nPathFlags /* = PATH_FLAG_ALL */ ) const
{
	sOutputPath.Clear();

	if ( nIndex < 0 )
		return false;

	CUtlVector< CUtlString > sOutputPaths;
	if ( !GetOutputPaths( sOutputPaths, nPathFlags ) )
		return false;

	if ( nIndex >= sOutputPaths.Count() )
		return false;

	sOutputPath = sOutputPaths.Element( nIndex );

	return ( sOutputPath.Length() > 0 );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetBase::GetOutputPaths(
	CUtlVector< CUtlString > &sOutputPaths,
	uint nPathFlags /* = PATH_FLAG_ALL */,
	bool bRecurse /* = false */ ) const
{
	CUtlString sTmp;
	if ( !IsOk( sTmp ) )
	{
		Warning( FUNCTION_LINE_STRING "Error! CTarget%s - Not Valid: %s\n", GetTypeString(), sTmp.String() );
		return false;
	}

	CUtlString sDirName;

	if ( nPathFlags & PATH_FLAG_PATH )
	{
		if ( !GetDirName( sDirName, nPathFlags ) )
			return false;
	}

	const ExtensionList *pvecExtensions = GetExtensionsAndCount();
	int nExtCount = pvecExtensions ? pvecExtensions->Count() : 0;

	for ( int i = 0; i < nExtCount; ++i )
	{
		CUtlString &sOutputPath = sOutputPaths.Element( sOutputPaths.AddToTail() );

		if ( nPathFlags & PATH_FLAG_PATH )
		{
			sOutputPath = sDirName;
		}

		if ( nPathFlags & PATH_FLAG_FILE )
		{
			if ( sOutputPath.Length() > 0 )
			{
				sOutputPath += "/";
			}

			CUtlString sName;
			GetName( sName );

			sOutputPath += sName;

			if ( nPathFlags & PATH_FLAG_EXTENSION )
			{
				sOutputPath += pvecExtensions->Element(i);
			}
		}

		sOutputPath.FixSlashes();
	}

	bool bRet = sOutputPaths.Count() > 0;

	if ( bRecurse )
	{
		CUtlVector< CTargetBase * > inputs;
		if ( GetInputs( inputs ) )
		{
			for ( int i = 0; i < inputs.Count(); ++i )
			{
				CTargetBase *pTargetBase = inputs.Element( i );
				if ( !pTargetBase )
					continue;

				bRet = pTargetBase->GetOutputPaths( sOutputPaths, nPathFlags, bRecurse );
			}
		}
		else
		{
			bRet = false;
		}
	}

	return bRet;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetBase::GetOutputPaths(
	CUtlVector< CUtlString > &sOutputPaths,
	bool bRelative /* = true */,
	bool bRecurse /* = true */,
	bool bExtension /* = true */,
	bool bPrefix /* = true */ ) const
{
	CUtlString sTmp;

	if ( !IsOk( sTmp ) )
	{
		Warning( FUNCTION_LINE_STRING "Error! CTarget%s - Not Valid: %s\n", GetTypeString(), sTmp.String() );
		return false;
	}

	CUtlString sDirA;

	if ( bRelative )
	{
		if ( !Asset()->GetRelativeDir( sDirA, bPrefix ? GetPrefix() : NULL, this ) )
			return false;
	}
	else
	{
		if ( !Asset()->GetAbsoluteDir( sDirA, bPrefix ? GetPrefix() : NULL, this ) )
			return false;
	}

	const ExtensionList *pvecExtensions = GetExtensionsAndCount();
	int nExtCount = pvecExtensions ? pvecExtensions->Count() : 0;

	for ( int i = 0; i < nExtCount; ++i )
	{
		CUtlString &sRelativePath = sOutputPaths.Element( sOutputPaths.AddToTail() );

		CUtlString sName;
		GetName( sName );

		sRelativePath = sDirA;
		sRelativePath += "/";
		sRelativePath += sName;

		if ( bExtension )
		{
			sRelativePath += pvecExtensions->Element(i);
		}

		sRelativePath.FixSlashes();
	}

	bool bRet = sOutputPaths.Count() > 0;

	if ( bRecurse )
	{
		CUtlVector< CTargetBase * > inputs;
		if ( GetInputs( inputs ) )
		{
			for ( int i = 0; i < inputs.Count(); ++i )
			{
				CTargetBase *pTargetBase = inputs.Element( i );
				if ( !pTargetBase )
					continue;

				bRet = pTargetBase->GetOutputPaths( sOutputPaths, bRelative, bRecurse, bExtension ) && bRet;
			}
		}
		else
		{
			bRet = false;
		}
	}

	return bRet;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetBase::GetInputPaths(
	CUtlVector< CUtlString > &sInputPaths,
	bool bRelative /* = true */,
	bool bRecurse /* = true */,
	bool bExtension /* = true */ )
{
	CUtlString sTmp;
	if ( !IsOk( sTmp ) )
	{
		Warning( FUNCTION_LINE_STRING "Error! CTarget%s - Not Valid: %s\n", GetTypeString(), sTmp.String() );
		return false;
	}

	CUtlVector< CTargetBase * > inputs;
	if ( !GetInputs( inputs ) )
		return false;

	bool bRet = true;

	for ( int i = 0; bRet && i < inputs.Count(); ++i )
	{
		CTargetBase *pTargetBase = inputs.Element( i );
		if ( !pTargetBase )
			continue;

		bRet = pTargetBase->GetOutputPaths( sInputPaths, bRelative, false, bExtension ) && bRet;

		if ( bRecurse )
		{
			bRet = pTargetBase->GetInputPaths( sInputPaths, bRelative, bRecurse, bExtension ) && bRet;
		}
	}

	return bRet;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetBase::GetName( CUtlString &sName ) const
{
	if ( !GetCustomOutputName().IsEmpty() )
	{
		sName = GetCustomOutputName();
		sName += GetNameSuffix();
		return;
	}

	// don't take parent's custom output name
	if ( m_pTargetParent && m_pTargetParent->GetCustomOutputName().IsEmpty() )
	{
		m_pTargetParent->GetName( sName );
	}
	else
	{
		sName = GetAssetName();
	}

	sName += GetNameSuffix();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const CUtlString &CTargetBase::GetAssetName() const
{
	return Asset()->GetAssetName();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CAsset *CTargetBase::Asset() const
{
	return m_pAsset;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetBase::CheckFile( const char *pszFilename ) const
{
	if ( _access( pszFilename, 06 ) == 0 )
		return true;

	Warning( "CTarget%s::Compile NO FILE! - %s\n", GetTypeString(), pszFilename );

	return false;
}



//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetBase::GetDirName(
	CUtlString &sDirName,
	uint nPathFlags /* = PATH_FLAG_ALL */ ) const
{
	sDirName.Clear();

	CAsset *pAsset = Asset();
	if ( !pAsset )
	{
		Warning( FUNCTION_LINE_STRING "Error! CTarget%s - NULL Asset\n", GetTypeString() );
		return false;
	}

	CUtlString sTmp;
	if ( !pAsset->IsOk( sTmp ) )
	{
		Warning( FUNCTION_LINE_STRING "Error! CTarget%s - Not Valid: %s\n", GetTypeString(), sTmp.String() );
		return false;
	}

	if ( nPathFlags & PATH_FLAG_ABSOLUTE )
	{
		if ( IsContent() )
		{
			if ( !CItemUpload::GetContentDir( sDirName ) )
				return false;
		}
		else
		{
			if ( !CItemUpload::GetVProjectDir( sDirName ) )
				return false;
		}

		sDirName += "/";

		// ABSOLUTE implies PREFIX & MODELS
		nPathFlags |= PATH_FLAG_PREFIX;
		nPathFlags |= PATH_FLAG_MODELS;
	}

	if ( nPathFlags & PATH_FLAG_ZIP )
	{
		const char *pszZipPrefix;

		if ( IsContent() )
		{
			pszZipPrefix = CItemUpload::Manifest()->GetZipSourceDirectory();
		}
		else
		{
			pszZipPrefix = CItemUpload::Manifest()->GetZipOutputDirectory();
		}

		if ( *pszZipPrefix )
		{
			sDirName += pszZipPrefix;
			sDirName += "/";
		}
	}

	if ( ( nPathFlags & PATH_FLAG_PREFIX ) && ( nPathFlags & PATH_FLAG_MODELS ) )
	{
		const char *pszPrefix = GetPrefix();
		if ( pszPrefix )
		{
			sDirName += pszPrefix;
			sDirName += "/";
		}

		// PREFIX implies MODELS
		nPathFlags |= PATH_FLAG_MODELS;
	}

	if ( nPathFlags & PATH_FLAG_MODELS && IsModelPath() )
	{
		// If not starting with prefix, then optionally start with models
		sDirName += "models/";
	}

	if ( GetCustomRelativeDir() )
	{
		sDirName += GetCustomRelativeDir();
	}
	else
	{
		sDirName += GetItemDirectory();
		sDirName += pAsset->GetClass();
		sDirName += "/";

		if ( CItemUpload::Manifest()->GetItemPathUsesSteamId() )
		{
			const char *pszSteamId = pAsset->GetSteamId();
			if ( pszSteamId )
			{
				sDirName += pszSteamId;
				sDirName += "/";
			}
		}
		
		sDirName += pAsset->GetAssetName();
	}

	char szBuf[ k64KB ];

	V_FixupPathName( szBuf, ARRAYSIZE( szBuf ), sDirName.String() );
	sDirName = szBuf;

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetBase::CreateOutputDirectory() const
{
	CUtlString sDir;
	if ( !GetOutputPath( sDir, 0, PATH_FLAG_PATH | PATH_FLAG_ABSOLUTE ) )
		return false;

	if ( !CItemUpload::CreateDirectory( sDir.String() ) )
	{
		Warning( "CTarget%s::CreateDirectory( %s ) - Failed\n", GetTypeString(), sDir.String() );
		return false;
	}

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetBase::AddOrEditP4File( const char *pszFilePath )
{
	if ( m_bIgnoreP4 )
		return;

	if ( CItemUpload::GetP4() )
	{
		char szCorrectCaseFilePath[MAX_PATH];
		g_pFullFileSystem->GetCaseCorrectFullPath( pszFilePath, szCorrectCaseFilePath );
		CP4AutoEditAddFile a( szCorrectCaseFilePath );
	}
}


//=============================================================================
//
//=============================================================================
bool VTFGetInfo( const char *fileName, int *width, int *height, ImageFormat *imageFormat, float *sourceGamma )
{
	// Just load the whole file into a memory buffer
	CUtlBuffer bufFileContents;
	if ( !g_pFullFileSystem->ReadFile( fileName, NULL, bufFileContents ) )
	{
		return false;
	}

	IVTFTexture *pVTFTexture = CreateVTFTexture();
	if ( !pVTFTexture->Unserialize( bufFileContents, true ) )
	{
		return false;
	}

	*width = pVTFTexture->Width();
	*height = pVTFTexture->Height();
	// It's not actually RGBA, but it will be when we decompress and load it...
	*imageFormat = IMAGE_FORMAT_RGBA8888;
	*sourceGamma = 0.0f;

	DestroyVTFTexture( pVTFTexture );

	return true;
}

//=============================================================================
//
//=============================================================================
CTargetTGA::CTargetTGA( CAsset *pAsset, const CTargetVMT *pTargetVMT )
: CTargetBase( pAsset, pTargetVMT )
, m_pTargetVMT( pTargetVMT )
, m_nSrcImageType( IMAGE_FILE_UNKNOWN )
, m_nWidth( 0 )
, m_nHeight( 0 )
, m_nChannelCount( 0 )
, m_bNoNiceFiltering( false )
, m_bAlpha( false )
, m_bPowerOfTwo( false )
{
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CTargetTGA::~CTargetTGA()
{
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetTGA::IsOk( CUtlString &sMsg ) const
{
	// No input file specified
	if ( m_sInputFile.Length() <= 0 )
	{
		sMsg = "No input file specified";
		return false;
	}

	// Input file invalid size
	if ( m_nWidth <= 0 )
	{
		sMsg = "Invalid image width (";
		sMsg += m_nWidth;
		sMsg += ")";
		return false;
	}

	if ( m_nHeight <= 0 )
	{
		sMsg = "Invalid image height (";
		sMsg += m_nHeight;
		sMsg += ")";
		return false;
	}

	// TODO: Maximum size?

	// Only 3 or 4 channel images ok
	if ( m_nChannelCount != 3 && m_nChannelCount != 4 )
	{
		sMsg = "Invalid number of channels (";
		sMsg += m_nChannelCount;
		sMsg = ") only 3 (RGB) and 4 (RGBA) channel images allowed";
		return false;
	}

	// Has to be a power of two
	if ( !m_bPowerOfTwo )
	{
		sMsg = "Image dimensions are not a power of two";
		return false;
	}

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetTGA::IsModelPath() const
{
	return m_pTargetVMT->IsModelPath();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetTGA::GetInputPaths(
	CUtlVector< CUtlString > &sInputPaths,
	bool bRelative /* = true */,
	bool bRecurse /* = true */,
	bool bExtension /* = true */ )
{
	sInputPaths.AddToTail( m_sInputFile );

	return CTargetBase::GetInputPaths( sInputPaths, bRelative, bRecurse, bExtension );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const ExtensionList *CTargetTGA::GetExtensionsAndCount( void ) const
{
	static ExtensionList vecExtensions;
	if ( !vecExtensions.Count() )
	{
		vecExtensions.AddToTail( ".tga" );
		vecExtensions.AddToTail( ".txt" );
	}

	return &vecExtensions;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const char *CTargetTGA::GetPrefix() const
{
	if ( m_sPrefix.IsEmpty() ) {
		CUtlString strParentPrefix = m_pTargetVMT->GetPrefix();
		m_sPrefix = strParentPrefix.Replace( "materials", "materialsrc" );
	}
	return m_sPrefix.String();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetTGA::GetName( CUtlString &sName ) const
{
	if ( GetCustomOutputName().IsEmpty() )
	{
		CTargetBase::GetName( sName );
	}
	else
	{
		sName = GetCustomOutputName();
	}
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetTGA::Compile()
{
	// 'reset' the input file from the current value to force a reload on compile, make a temp copy as it's going to be overwritten
	if ( !SetInputFile( CUtlString( GetInputFile() ).String() ) )
		return false;

	if ( !CTargetBase::Compile() )
		return false;

	CUtlString sName;
	if ( !GetOutputPath( sName, 0, PATH_FLAG_FILE | PATH_FLAG_EXTENSION ) )
	{
		Warning( "CTarget%s::Compile - GetOutputPath Failed\n", GetTypeString() );
		return false;
	}

	CUtlString sTmp;
	if ( !IsOk( sTmp ) )
	{
		Warning( "CTarget%s::Compile( %s ) - Not Valid: %s\n", GetTypeString(), sName.String(), sTmp.String() );
		return false;
	}

	CUtlString sAbsPath;
	GetOutputPath( sAbsPath, 0 );

	CUtlString sRelPath;
	GetOutputPath( sRelPath, 0, PATH_FLAG_ALL & ~PATH_FLAG_ABSOLUTE );

	if ( sAbsPath.IsEmpty() || sRelPath.IsEmpty() )
	{
		Warning( "CTarget%s::Compile( %s ) - GetOutputPath failed\n", GetTypeString(), sName.String() );
		return false;
	}

	Msg( "Compiling %s: %s\n", GetTypeString(), sRelPath.String() );

	if ( CItemUpload::IsSameFile( m_sInputFile.String(), sAbsPath.String() ) )
	{
		Warning( "CTarget%s::Compile( %s ) - Same File, No Work To Do!\n", GetTypeString(), sName.String() );
		return true;
	}

	Bitmap_t bitmap;
	CUtlMemory< unsigned char > tgaBits;

	switch ( m_nSrcImageType )
	{
		case IMAGE_FILE_TGA:
		{
			int nWidth = 0, nHeight = 0;
			if ( !TGALoader::LoadRGBA8888( m_sInputFile.String(), tgaBits, nWidth, nHeight ) )
			{
				Warning( "CTarget%s::Compile( %s ) - Couldn't Load TGA\n", GetTypeString(), sName.String() );
				return false;
			}

			bitmap.SetBuffer( nWidth, nHeight, IMAGE_FORMAT_RGBA8888, tgaBits.Base(), false, nWidth*4 );
		}
		break;

		case IMAGE_FILE_PSD:
		{
			if ( !PSDReadFileRGBA8888( m_sInputFile.String(), NULL, bitmap ) )
			{
				Warning( "CTarget%s::Compile( %s ) - Couldn't Load PSD\n", GetTypeString(), sName.String() );
				return false;
			}
		}
		break;

		case IMAGE_FILE_VTF:
		{
			// Just load the whole file into a memory buffer
			CUtlBuffer bufFileContents;
			if ( !g_pFullFileSystem->ReadFile( m_sInputFile.String(), NULL, bufFileContents ) )
			{
				Warning( "CTarget%s::Compile( %s ) - Couldn't Load VTF\n", GetTypeString(), sName.String() );
				return false;
			}

			IVTFTexture *pVTFTexture = CreateVTFTexture();
			if ( !pVTFTexture->Unserialize( bufFileContents ) )
			{
				Warning( "CTarget%s::Compile( %s ) - Couldn't Load VTF\n", GetTypeString(), sName.String() );
				return false;
			}

			int nWidth = pVTFTexture->Width();
			int nHeight = pVTFTexture->Height();
			pVTFTexture->ConvertImageFormat( IMAGE_FORMAT_RGBA8888, false );

			int nMemSize = ImageLoader::GetMemRequired( nWidth, nHeight, 1, IMAGE_FORMAT_RGBA8888, false );
			tgaBits.EnsureCapacity( nMemSize );
			Q_memcpy( tgaBits.Base(), pVTFTexture->ImageData(), nMemSize );

			DestroyVTFTexture( pVTFTexture );

			bitmap.SetBuffer( nWidth, nHeight, IMAGE_FORMAT_RGBA8888, tgaBits.Base(), false, nWidth*4 );
		}
		break;
	}

	if ( bitmap.Format() != IMAGE_FORMAT_RGBA8888 || bitmap.Width() != m_nWidth || bitmap.Height() != m_nHeight )
	{
		Warning( "CTarget%s::Compile( %s ) - Invalid Bitmap Size, Expected %d x %d x %d (%d)\n",
			GetTypeString(), sName.String(), m_nWidth, m_nHeight, 4, m_nWidth * m_nHeight * 4 );
		return false;
	}

	CUtlBuffer bufVTEXConfig( 0, 0, CUtlBuffer::TEXT_BUFFER );

	// If we're supposed to resize these to another resolution, setup the VTEX config file to do so.
	int nTargetWidth = m_pTargetVMT->GetTargetWidth();
	int nTargetHeight = m_pTargetVMT->GetTargetHeight();
	if ( nTargetWidth && nTargetHeight )
	{
		// We want to use "reduce", not "maxwidth" & "maxheight", so we throw away the higher resolution mips.
		// Determine the right amount of reduction based on the texture's width & height (and fail if it's the wrong aspect ratio)
		int nFactor = (m_nWidth / nTargetWidth);
		if ( nFactor <= 0 )
		{
			Warning( "CTarget%s::Compile( %s ) - Failed to determine reduction factor (target width is %d, texture is %d)\n", GetTypeString(), sName.String(), nTargetWidth, m_nWidth );
			return false;
		}
		int nHeightFactor = (m_nHeight / nTargetHeight);
		if ( nFactor != nHeightFactor )
		{
			Warning( "CTarget%s::Compile( %s ) - Failed to determine reduction factor (target size aspect ratio (%dx%d) doesn't match texture's aspect ratio (%dx%d))\n", GetTypeString(), sName.String(), nTargetWidth, nTargetHeight, m_nWidth, m_nHeight );
			return false;
		}

		if ( nFactor > 1 )
		{
			bufVTEXConfig.PutString( CFmtStr("reduce %d\n", nFactor) );
		}
	}

	KeyValues *pKV = m_pTargetVMT->GetTextureAddToVTEXConfigForTGA( this );
	if ( pKV )
	{
		FOR_EACH_SUBKEY( pKV, pKVEntry )
		{
			bufVTEXConfig.Printf( "%s %s\n", pKVEntry->GetName(), pKVEntry->GetString() );
		}
	}

	// check if we should modify abspath
	CUtlString sTGAOutput = Asset()->CheckRedundantOutputFilePath( GetInputFile().String(), bufVTEXConfig.String(), sAbsPath.String() );
	AddOrEditP4File( sTGAOutput.String() );

	char szVTEXConfigFilename[MAX_PATH];
	V_strcpy_safe( szVTEXConfigFilename, sTGAOutput.String() );
	V_SetExtension( szVTEXConfigFilename, ".txt", sizeof(szVTEXConfigFilename) );
	AddOrEditP4File( szVTEXConfigFilename );
	g_pFullFileSystem->WriteFile( szVTEXConfigFilename, NULL, bufVTEXConfig );

	// TODO: Don't write alpha if VMT using this doesn't specify the alpha is used for anything
	CUtlBuffer outBuf;
	const bool bWriteTGA = TGAWriter::WriteToBuffer( bitmap.GetBits(), outBuf, bitmap.Width(), bitmap.Height(), bitmap.Format(), m_bAlpha ? IMAGE_FORMAT_BGRA8888 : IMAGE_FORMAT_BGR888 );

	if ( !bWriteTGA )
	{
		Warning( "CTarget%s::Compile( %s ) - Couldn't write TGA to buffer\n", GetTypeString(), sName.String() );
		return false;
	}

	if ( !g_pFullFileSystem->WriteFile( sTGAOutput.String(), NULL, outBuf ) )
	{
		Warning( "CTarget%s::Compile( %s ) - Couldn't write TGA to file \"%s\"\n", GetTypeString(), sName.String(), sTGAOutput.String() );
		return false;
	}

	if ( !CheckFile( sTGAOutput.String() ) )
	{
		Warning( "CTarget%s::Compile( %s ) - File Check Failed - \"%s\"\n", GetTypeString(), sName.String(), sTGAOutput.String() );
		return false;
	}

	if ( CItemUpload::Manifest()->UseTerseMessages() )
	{
		Msg( " - Compilation successful.\n" );
	}
	else
	{
		Msg( "CTarget%s::Compile OK! - %s\n", GetTypeString(), sRelPath.String() );
	}

	// store the output name to use it to output VTF file
	char szFileName[FILENAME_MAX];
	V_StripExtension( V_GetFileName( szVTEXConfigFilename ), szFileName, ARRAYSIZE( szFileName ) );
	SetCustomOutputName( szFileName );

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetTGA::Clear()
{
	m_sInputFile = "";
	m_sFileBase = "";
	m_sExtension = "";

	m_nWidth = 0;
	m_nHeight = 0;
	m_nChannelCount = 0;
	m_bNoNiceFiltering = false;
	m_bAlpha = false;
	m_bPowerOfTwo = false;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetTGA::SetInputFile( const char *pszFilename )
{
	Clear();

	if ( !pszFilename || V_strlen( pszFilename ) <= 0 )
	{
		Warning( "ERROR: Empty filename specified for TGA file\n" );
		return false;
	}

	char szBuf[ k64KB ];

	m_sInputFile = pszFilename;

	V_FileBase( pszFilename, szBuf, ARRAYSIZE( szBuf ) );
	m_sFileBase = szBuf;

	// Try to automatically handle the suffixes for properly named assets
	static const char *szSuffixes[] =
	{
		//"_color",
		"_normal",
		"_height",
		"_specmask"
		"_specexp",
		"_trans",
		"_illum",
		"_color_red",
		"_color_blue"
	};

	CUtlString sTmp;

	for ( int i = 0; i < ARRAYSIZE( szSuffixes ); ++i )
	{
		const char *pszSuffix = szSuffixes[i];

		sTmp = GetAssetName();
		sTmp += pszSuffix;
		if ( m_sFileBase == sTmp )
		{
			SetNameSuffix( pszSuffix );
		}
	}
	
	V_ExtractFileExtension( pszFilename, szBuf, ARRAYSIZE( szBuf ) );
	m_sExtension = szBuf;

	ImageFormat imageFormat;
	float flSourceGamma = 0;

	if ( TGALoader::GetInfo( pszFilename, &m_nWidth, &m_nHeight, &imageFormat, &flSourceGamma ) )
	{
		m_nSrcImageType = IMAGE_FILE_TGA;
	}
	else if ( PSDGetInfo( pszFilename, NULL, &m_nWidth, &m_nHeight, &imageFormat, &flSourceGamma ) )
	{
		m_nSrcImageType = IMAGE_FILE_PSD;
	}
	else if ( VTFGetInfo( pszFilename, &m_nWidth, &m_nHeight, &imageFormat, &flSourceGamma ) )
	{
		m_nSrcImageType = IMAGE_FILE_VTF;
	}
	else
	{
		Warning( "ERROR: Specified file is not a TGA (Targa) or PSD File: \"%s\"\n", szBuf );

		Clear();

		return false;
	}

	// ImageFormat can only be one of:
	switch ( imageFormat )
	{
	case IMAGE_FORMAT_I8:
		m_nChannelCount = 1;
		m_bAlpha = false;
		break;
	case IMAGE_FORMAT_ABGR8888:
	case IMAGE_FORMAT_BGRA8888:
		m_nChannelCount = 4;
		m_bAlpha = true;
		break;
	case IMAGE_FORMAT_BGR888:
		m_nChannelCount = 3;
		m_bAlpha = false;
		break;
	case IMAGE_FORMAT_RGBA8888:
		m_nChannelCount = 4;
		m_bAlpha = true;
		break;
	case IMAGE_FORMAT_RGB888:
		m_nChannelCount = 3;
		m_bAlpha = false;
		break;
	default:
		break;
	}

	const int nWidthPow = NearestPowerOfTwo( m_nWidth );
	const int nHeightPow = NearestPowerOfTwo( m_nHeight );

	if ( nWidthPow == m_nWidth && nHeightPow == m_nHeight )
	{
		m_bPowerOfTwo = true;
	}
	else
	{
		Warning( "ERROR: Specified texture file (%s) Size %dx%d dimensions not powers of two, perhaps resize to %dx%d\n",
			m_sInputFile.String(),
			m_nWidth, m_nHeight,
			nWidthPow, nHeightPow );
		return false;
	}

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const CUtlString &CTargetTGA::GetInputFile() const
{
	return m_sInputFile;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
template < class T >
T CTargetTGA::NearestPowerOfTwo( T v )
{
	if (v == 0)
		return static_cast< T >( 1 );

	T k;
	for ( k = sizeof( T ) * 8 - 1; ( ( static_cast< T >( 1U ) << k) & v ) == 0; --k);

	if ( ( ( static_cast< T >( 1U ) << ( k - 1 ) ) & v ) == 0 )
		return static_cast< T >( 1U ) << k;

	return static_cast< T >( 1U ) << ( k + 1 );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetTGA::UpdateManifest( KeyValues *pKv )
{
	pKv->SetString( "filename", m_sInputFile.String() );
	CUtlString sOutName;
	if ( GetOutputPath( sOutName, 0, PATH_FLAG_PATH | PATH_FLAG_FILE | PATH_FLAG_PREFIX | PATH_FLAG_MODELS | PATH_FLAG_EXTENSION ) )
	{
		pKv->SetString( "out_filename", sOutName );
	}
	pKv->SetInt( "width", m_nWidth );
	pKv->SetInt( "height", m_nHeight );
	pKv->SetInt( "channels", m_nChannelCount );
	pKv->SetInt( "nonice", m_bNoNiceFiltering );
	pKv->SetBool( "alpha", m_bAlpha );

	const char *pszTextureType = m_pTargetVMT->GetTextureTypeForTGA( this );
	if ( pszTextureType )
	{
		KeyValues *pVTEXKV = CItemUpload::Manifest()->GetTextureAddToVTEXConfig( pszTextureType );
		if ( pVTEXKV )
		{
			KeyValues *pTmpKey = new KeyValues( pVTEXKV->GetName() );
			pKv->AddSubKey( pTmpKey );
			pVTEXKV->CopySubkeys( pTmpKey );
		}
	}
}


//=============================================================================
//
//=============================================================================
CTargetVTF::CTargetVTF( CAsset *pAsset, const CTargetVMT *pTargetVMT )
: CTargetBase( pAsset, pTargetVMT )
, m_pTargetVMT( pTargetVMT )
{
	m_pTargetTGA = Asset()->NewTarget< CTargetTGA >( m_pTargetVMT );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CTargetVTF::~CTargetVTF()
{
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetVTF::IsModelPath() const
{
	return m_pTargetVMT->IsModelPath();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetVTF::Compile()
{
	if ( !CTargetBase::Compile() )
		return false;

	CUtlString sName;
	if ( !GetOutputPath( sName, 0, PATH_FLAG_FILE | PATH_FLAG_EXTENSION ) )
		return false;

	CUtlString sTmp;
	if ( !IsOk( sTmp ) )
	{
		Warning( "CTarget%s::Compile( %s ) - Not Valid: %s\n", GetTypeString(), sName.String(), sTmp.String() );
		return false;
	}

	CUtlString sBinDir;
	if ( !CItemUpload::GetBinDirectory( sBinDir ) )
	{
		Warning( "CTarget%s::Compile( %s ) - GetBinDirectory Failed\n", GetTypeString(), sName.String() );
		return false;
	}

	CUtlString sAbsPath;
	GetOutputPath( sAbsPath, 0 );

	CUtlString sRelPath;
	GetOutputPath( sRelPath, 0, PATH_FLAG_ALL & ~PATH_FLAG_ABSOLUTE );

	if ( sAbsPath.IsEmpty() || sRelPath.IsEmpty() )
	{
		Warning( "CTarget%s::Compile( %s ) - GetOutputPath failed\n", GetTypeString(), sName.String() );
		return false;
	}

	Msg( "Compiling %s: %s\n", GetTypeString(), sRelPath.String() );

	AddOrEditP4File( sAbsPath.String() );

	CUtlVector< CUtlString > sAbsInputPaths;

	if ( !GetInputPaths( sAbsInputPaths, false, false ) )
	{
		Warning( "CTarget%s::Compile( %s ) - GetInputPaths failed\n", GetTypeString(), sName.String() );
		return false;
	}

	if ( sAbsInputPaths.Count() != 2 )
	{
		Warning( "CTarget%s::Compile( %s ) - GetPaths returned %d paths, expected 2\n", GetTypeString(), sName.String(), sAbsInputPaths.Count() );
		return false;
	}

	CFmtStrN< k64KB > sCmd;

	if ( CItemUpload::IgnoreEnvironmentVariables() )
	{
		// We can't rely on environment variables in VTEX. So tell it to just built it on the spot, and we'll move it afterwards.
		sCmd.sprintf( "\"%s\\vtex.exe\" -nop4 -nopause -dontusegamedir \"%s\"", sBinDir.String(), sAbsInputPaths.Element( 0 ).String() );
	}
	else
	{
		CUtlString sVProject;
		CItemUpload::GetVProjectDir( sVProject );
		sCmd.sprintf( "\"%s\\vtex.exe\" -nop4 -nopause -vproject \"%s\" \"%s\"", sBinDir.String(), sVProject.String(), sAbsInputPaths.Element( 0 ).String() );
	}

	if ( !CItemUpload::RunCommandLine( sCmd.Access(), sBinDir.String(), this ) )
	{
		Warning( "CTarget%s::Compile( %s ) - RunCommandLine Failed - \"%s\"\n", GetTypeString(), sName.String(), sCmd.Access() );
		return false;
	}

	if ( CItemUpload::IgnoreEnvironmentVariables() )
	{
		char sVTFName[MAX_PATH];
		V_strcpy_safe( sVTFName, sAbsInputPaths.Element( 0 ).String() );
		V_SetExtension( sVTFName, ".vtf", sizeof(sVTFName) );

		// We built the VTF in the directory with the content. Now move it to the out dir.
		if ( ::MoveFileEx( sVTFName, sAbsPath.String(), MOVEFILE_REPLACE_EXISTING ) == 0 )
		{
			Warning( "CTarget%s::Compile( %s ) - Failed to move \"%s\" to \"%s\"\n", GetTypeString(), sName.String(), sVTFName, sAbsPath.String() );
			return false;
		}
	}

	if ( !CheckFile( sAbsPath.String() ) )
	{
		Warning( "CTarget%s::Compile( %s ) - File Check Failed - \"%s\"\n", GetTypeString(), sName.String(), sAbsPath.String() );
		return false;
	}

	if ( CItemUpload::Manifest()->UseTerseMessages() )
	{
		Msg( " - Compilation successful.\n" );
	}
	else
	{
		Msg( "CTarget%s::Compile OK! - %s\n", GetTypeString(), sRelPath.String() );
	}

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetVTF::GetInputs( CUtlVector< CTargetBase * > &inputs ) const
{
	Assert( m_pTargetTGA.IsValid() );

	inputs.AddToTail( m_pTargetTGA.GetObject() );

	return inputs.Count() > 0;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const ExtensionList *CTargetVTF::GetExtensionsAndCount( void ) const
{
	static ExtensionList vecExtensions;
	if ( !vecExtensions.Count() )
	{
		vecExtensions.AddToTail( ".vtf" );
	}

	return &vecExtensions;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const char *CTargetVTF::GetPrefix() const
{
	return m_pTargetVMT->GetPrefix();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetVTF::GetName( CUtlString &sName ) const
{
	Assert( m_pTargetTGA.IsValid() );

	m_pTargetTGA->GetName( sName );
}


//=============================================================================
//
//=============================================================================
CTargetVMT::CTargetVMT( CAsset *pAsset, const CTargetBase *pTargetParent )
: CTargetBase( pAsset, pTargetParent )
, m_pVMTKV( NULL )
, m_nColorAlphaType( kNoColorAlpha )
, m_nNormalAlphaType( kNoNormalAlpha )
, m_nMaterialType( kInvalidMaterialType )
, m_bDuplicate( false )
, m_nTargetWidth( 0 )
, m_nTargetHeight( 0 )
{
	m_vecTargetVTFs.SetCount( CItemUpload::Manifest()->GetNumMaterialSkins() );
	FOR_EACH_VEC( m_vecTargetVTFs, i )
	{
		m_vecTargetVTFs[i].SetCount( CItemUpload::Manifest()->GetNumTextureTypes() );
	}
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CTargetVMT::~CTargetVMT()
{
	if ( m_pVMTKV )
	{
		m_pVMTKV->deleteThis();
	}

	CUtlString sMaterialId;
	GetMaterialId( sMaterialId );

	Asset()->RemoveMaterial( sMaterialId );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetVMT::Compile()
{
	if ( GetDuplicate() )
		return true;

	if ( !CTargetBase::Compile() )
		return g_bCompilePreview ? true : false;

	CAsset *pAsset = Asset();
	if ( !pAsset )
		return false;

	CUtlString sName;
	if ( !GetOutputPath( sName, 0, PATH_FLAG_FILE | PATH_FLAG_EXTENSION ) )
		return false;

	CUtlString sTmp;
	if ( !IsOk( sTmp ) )
	{
		Warning( "CTarget%s::Compile( %s ) - Not Valid: %s\n", GetTypeString(), sName.String(), sTmp.String() );
		return false;
	}

	int nVmtCount = GetOutputCount();

	CUtlVector< CUtlString > sAbsOutputPaths;
	CUtlVector< CUtlString > sRelOutputPaths;

	if ( !GetOutputPaths( sAbsOutputPaths, false, false, true ) || !GetOutputPaths( sRelOutputPaths, true, false, true ) )
	{
		Warning( "CTarget%s::Compile( %s ) - GetOutputPaths failed\n", GetTypeString(), sName.String() );
		return false;
	}

	if ( sAbsOutputPaths.Count() != nVmtCount || sAbsOutputPaths.Count() != sRelOutputPaths.Count() )
	{
		Warning( "CTarget%s::Compile( %s ) - GetOutputPaths returned %d paths, expected %d\n", GetTypeString(), sName.String(), sAbsOutputPaths.Count(), 1 );
		return false;
	}

	Assert( nVmtCount == sAbsOutputPaths.Count() );
	Assert( nVmtCount == sRelOutputPaths.Count() );

	char szBuf[ k64KB ];

	for ( int i = 0; i < nVmtCount; ++i )
	{
		CUtlVector< CUtlString > sChildRelOutputPaths;

		if ( !GetOutputPath( sName, i, PATH_FLAG_FILE | PATH_FLAG_EXTENSION ) )
		{
			Warning( "CTarget%s::Compile( %s ) - Can't GetOutputPath %d\n", GetTypeString(), sName.String(), i );
			return false;
		}

		Msg( "Compiling %s: %s\n", GetTypeString(), sRelOutputPaths.Element( i ).String() );

		AddOrEditP4File( sAbsOutputPaths.Element( i ).String() );

		// Load the template in.
		KeyValues *pVMTKV = GetVMTKV( i );
		if ( !pVMTKV )
		{
			// We've already printed a warning, so we're done
			return false;
		}

		// Set any remapped VMT vars.
		FOR_EACH_VEC( m_vecTargetVTFs[i], iVTF )
		{
			if ( m_vecTargetVTFs[i][iVTF].IsValid() )
			{
				const char *pszVMTVar = CItemUpload::Manifest()->GetVMTVarForTextureType( CItemUpload::Manifest()->GetTextureType(iVTF) );
				if ( pszVMTVar && pszVMTVar[0] )
				{
					sChildRelOutputPaths.RemoveAll();
					m_vecTargetVTFs[i][iVTF]->GetOutputPaths( sChildRelOutputPaths, true, false, false, false );

					if ( sChildRelOutputPaths.Count() > 0 )
					{
						V_strncpy( szBuf, sChildRelOutputPaths.Element( 0 ).String(), ARRAYSIZE( szBuf ) );
						V_FixSlashes( szBuf, '/' );

						if ( pVMTKV->FindKey( pszVMTVar ) )
						{
							pVMTKV->SetString( pszVMTVar, szBuf );
						}
					}
				}
			}
		}

		CUtlBuffer fileBuf( 0, 0, CUtlBuffer::TEXT_BUFFER );
		pVMTKV->RecursiveSaveToFile( fileBuf, 0 );
		g_pFullFileSystem->WriteFile( sAbsOutputPaths.Element( i ).String(), "MOD", fileBuf );

		Msg( "CTarget%s::Compile OK! - %s\n", GetTypeString(), sRelOutputPaths.Element( i ).String() );
	}

	return true;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const char *CTargetVMT::MaterialTypeToString( int nMaterialType )
{
	if ( nMaterialType == kInvalidMaterialType )
		return "InvalidMaterialType";

	const char *pszMaterialName = CItemUpload::Manifest()->GetMaterialType( nMaterialType );
	if ( pszMaterialName )
		return pszMaterialName;

	return "Unknown";
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CTargetVMT::StringToMaterialType( const char *pszUserData )
{
	return CItemUpload::Manifest()->GetMaterialType( pszUserData );
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetVMT::IsOk( CUtlString &sMsg ) const
{
	if ( GetMaterialType() == kInvalidMaterialType )
	{
		sMsg += "\"";
		sMsg += m_sMaterialId;
		sMsg += "\": ";
		sMsg += "Invalid material type, must be one of Primary or Secondary";
		return false;
	}

	if ( GetDuplicate() )
		return true;

	if ( !g_bCompilePreview )
	{
		// Make sure we have all the required textures.
		int nSkinIndex = CItemUpload::Manifest()->GetDefaultMaterialSkin();
		FOR_EACH_VEC( m_vecTargetVTFs[ nSkinIndex ], i )
		{
			if ( !m_vecTargetVTFs[nSkinIndex][i].IsValid() && CItemUpload::Manifest()->IsTextureTypeRequired( i ) )
			{
				sMsg += "\"";
				sMsg += m_sMaterialId;
				sMsg += "\": ";
				sMsg += "Missing required texture type";
				Warning( "Missing texture of type '%s'\n", CItemUpload::Manifest()->GetTextureType( i ) );
				return false;
			}
		}
	}
	

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const ExtensionList *CTargetVMT::GetExtensionsAndCount( void ) const
{
	if ( GetDuplicate() )
		return NULL;

	m_vecExtensions.SetCount( 0 );
	FOR_EACH_VEC( m_vecTargetVTFs, i )
	{
		FOR_EACH_VEC( m_vecTargetVTFs[i], j )
		{
			if ( m_vecTargetVTFs[i][j].IsValid() )
			{
				CUtlString sExtension = CItemUpload::Manifest()->GetMaterialSkinFilenameAppend( i );
				sExtension += ".vmt";
				m_vecExtensions.AddToTail( sExtension );
				break;
			}
		}
	}

	return &m_vecExtensions;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetVMT::GetInputs( CUtlVector< CTargetBase * > &inputs ) const
{
	CUtlString sTmp;

	if ( !IsOk( sTmp ) )
	{
		Warning( FUNCTION_LINE_STRING "Error! CTarget%s - Not Valid: %s\n", GetTypeString(), sTmp.String() );
		return false;
	}

	FOR_EACH_VEC( m_vecTargetVTFs, i )
	{
		FOR_EACH_VEC( m_vecTargetVTFs[i], j )
		{
			if ( m_vecTargetVTFs[i][j].IsValid() )
			{
				inputs.AddToTail( m_vecTargetVTFs[i][j].GetObject() );
			}
		}
	}

	return inputs.Count() > 0;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetVMT::GetName( CUtlString &sName ) const
{
	sName.Clear();

	if ( GetDuplicate() )
	{
	}
	else
	{
		CTargetBase::GetName( sName );

		if ( m_nMaterialType == kInvalidMaterialType )
		{
			sName += "_INVALID";
		}
		else
		{
			if ( m_nMaterialType > 0 )
			{
				sName += "_";
				sName += m_nMaterialType;
			}
		}
	}
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetVMT::SetMaterialId( const char *pszMaterialId )
{
	m_sMaterialId = pszMaterialId;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetVMT::SetMaterialType( int nMaterialType )
{
	CAsset *pAsset = Asset();
	if ( !pAsset )
		return false;

	if ( nMaterialType < 0 || nMaterialType >= CItemUpload::Manifest()->GetNumMaterialTypes() )
		return false;

	m_bDuplicate = false;
	m_nMaterialType = kInvalidMaterialType;

	if ( nMaterialType == kInvalidMaterialType )
		return true;

	// Recursively increment other non-duplicate materials which match this material
	// and so on...
	for ( int i = 0; i < pAsset->GetTargetVMTCount(); ++i )
	{
		CTargetVMT *pTargetVMT = pAsset->GetTargetVMT( i );
		if ( !pTargetVMT )
			continue;

		if ( !pTargetVMT->GetDuplicate() && pTargetVMT->GetMaterialType() == nMaterialType )
		{
			pTargetVMT->SetMaterialType( ( ( nMaterialType + 1 ) % CItemUpload::Manifest()->GetNumMaterialTypes() ) );
		}
	}

	m_nMaterialType = nMaterialType;

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CTargetVMT::GetMaterialType() const
{
	return m_nMaterialType;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetVMT::SetDuplicate( int nMaterialType )
{
	if ( nMaterialType < 0 || nMaterialType >= CItemUpload::Manifest()->GetNumMaterialTypes() )
		return;

	m_bDuplicate = true;
	m_nMaterialType = nMaterialType;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetVMT::SetTargetVTF( const char *pszTextureType, const char *pszFilename, int nSkinIndex )
{
	// Make sure it's a valid texture type
	int nTextureType = CItemUpload::Manifest()->GetTextureType( pszTextureType );
	if ( nTextureType == -1 )
	{
		Warning( "Invalid texture type specified: '%s'\n", pszTextureType );
		return false;
	}
	if ( nSkinIndex == kInvalidMaterialSkin )
	{
		Warning( "Invalid skin type specified: '%d'\n", nSkinIndex );
		return false;
	}

	CUtlString sSuffix;
	sSuffix += CItemUpload::Manifest()->GetMaterialSkinFilenameAppend( nSkinIndex );
	sSuffix += pszTextureType;

	return SetTargetVTF( m_vecTargetVTFs[nSkinIndex][nTextureType], pszFilename, sSuffix.String() );
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetVMT::SetTargetVTF(
	CSmartPtr< CTargetVTF > &pTargetVTF,
	const char *pszFilename,
	const char *pszSuffix ) const
{
	if ( !pTargetVTF )
	{
		pTargetVTF = Asset()->NewTarget< CTargetVTF >( this );
	}

	if ( !pTargetVTF )
		return false;

	pTargetVTF->SetNameSuffix( pszSuffix );

	bool bResult = pTargetVTF->SetInputFile( pszFilename );

	return bResult;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetVMT::SetVMTKV( const KeyValues *pKV, int nSkinIndex /*= 0*/ )
{
	if ( !m_pVMTKV )
	{
		m_pVMTKV = new KeyValues( "data" );	
	}

	const char* pszKeyName = CFmtStr( kVMT, nSkinIndex );
	if ( KeyValues *pPreviousKey = m_pVMTKV->FindKey( pszKeyName ) )
	{
		m_pVMTKV->RemoveSubKey( pPreviousKey );
	}

	KeyValues *pNewKey = new KeyValues( pszKeyName );
	pNewKey->AddSubKey( pKV->MakeCopy() );
	m_pVMTKV->AddSubKey( pNewKey );
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
KeyValues *CTargetVMT::GetVMTKV( int nSkinIndex )
{
	if ( !m_pVMTKV )
	{
		m_pVMTKV = new KeyValues( "data" );	
	}

	const char* pszKeyName = CFmtStr( kVMT, nSkinIndex );
	KeyValues* pKey = m_pVMTKV->FindKey( pszKeyName );
	if ( pKey )
	{
		return pKey->GetFirstSubKey();
	}

	KeyValues *pVMTKV = new KeyValues("VMTTemplate");

	if ( CItemUpload::Manifest()->HasClassVMTTemplates() )
	{
		const int nClassIndex = GetClassIndex( Asset()->GetClass() );
		const char *pszVMTTemplate = CItemUpload::Manifest()->GetClassVMTTemplate( nClassIndex );
		if ( !pszVMTTemplate )
		{
			Warning( "Could not find a VMT template for class '%s'\n", Asset()->GetClass() );
			pVMTKV->deleteThis();
			return NULL;
		}

		if ( !pVMTKV->LoadFromFile( g_pFullFileSystem, pszVMTTemplate, "MOD" ) )
		{
			Warning( "Failed to load specified VMT template '%s' for class '%s'.\n", pszVMTTemplate, Asset()->GetClass() );
			pVMTKV->deleteThis();
			return NULL;
		}
	}
	else
	{
		CreateLegacyTemplate( pVMTKV );
	}

	KeyValues *pNewKey = new KeyValues( pszKeyName );
	pNewKey->AddSubKey( pVMTKV );
	m_pVMTKV->AddSubKey( pNewKey );

	return pVMTKV;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetVMT::CreateLegacyTemplate( KeyValues *pVMTKV )
{
	pVMTKV->SetName( "VertexlitGeneric" );

	for ( int iVTF = 0; iVTF < CItemUpload::Manifest()->GetNumTextureTypes(); ++iVTF )
	{
		const char *pszVMTVar = CItemUpload::Manifest()->GetVMTVarForTextureType( CItemUpload::Manifest()->GetTextureType( iVTF ) );
		if ( pszVMTVar && *pszVMTVar )
		{
			pVMTKV->SetString( pszVMTVar, "" );
		}
	}

	// Wearables usually use this lightwarp: "models/player/pyro/pyro_lightwarp"
	// Weapons usually use this lightwarp: "models/lightwarps/weapon_lightwarp"
	// Weapons are the more custom case, so we'll default to a good wearable lightwarp
	pVMTKV->SetString( "$lightwarptexture", "models/player/pyro/pyro_lightwarp" );
	pVMTKV->SetString( "$phong", "1" );
	pVMTKV->SetString( "$phongexponent", "25" );
	pVMTKV->SetString( "$phongboost", "0.1" );
	pVMTKV->SetString( "$phongfresnelranges", "[.25 .5 1]" );

	pVMTKV->SetString( "$rimlight", "1" );			// To enable rim lighting (requires phong)
	pVMTKV->SetString( "$rimlightexponent", "4" );	// Exponent for phong component of rim 
	pVMTKV->SetString( "$rimlightboost", "2" );		// Boost for ambient cube component of rim lighting

	switch ( m_nColorAlphaType )
	{
	case kNoColorAlpha:
		break;
	case kTransparency:
		pVMTKV->SetString( "$translucent", "1" );
		break;
	case kPaintable:
		pVMTKV->SetString( "$blendtintbybasealpha", "1" );
		pVMTKV->SetString( "$blendtintcoloroverbase", "0" );   // between 0-1 determines how much blended by tinting vs. replacing the color

		pVMTKV->SetString( "$colortint_base", "{ 255 255 255 }" );	// put the RGB value of whats being colored when no paint is present, if $blendtintcoloroverbase is 0 then put [255 255 255] here.
		pVMTKV->SetString( "$color2", "{ 255 255 255 }" );
		pVMTKV->SetString( "$colortint_tmp", "[0 0 0]" );
		break;
	case kColorSpecPhong:
		pVMTKV->SetString( "$basemapalphaphongmask", "1" );
		break;
	}

	switch ( m_nNormalAlphaType )
	{
	case kNoNormalAlpha:
		break;
	case kNormalSpecPhong:
		pVMTKV->SetString( "$bumpmapalphaphongmask", "1" );
		break;
	}

	// Variables for the cloak effect
	pVMTKV->SetString( "$cloakPassEnabled", "1" );

	// Variables for the burning effect
	pVMTKV->SetString( "$detail", "effects/tiledfire/fireLayeredSlowTiled512" );
	pVMTKV->SetString( "$detailscale", "5" );
	pVMTKV->SetString( "$detailblendfactor", "0" );
	pVMTKV->SetString( "$detailblendmode", "6" );

	// Variables for the jarate effect
	pVMTKV->SetString( "$yellow", "0" );

	// The order of the proxies is important!
	KeyValues *pProxies = pVMTKV->FindKey( "Proxies", true );

	// Proxies for the cloak effect
	KeyValues *pProxiesWeaponInvis = pProxies->FindKey( "invis", true );
	pProxiesWeaponInvis->FindKey( "temp_key_wont_print_dont_worry", true );

	// Proxies for the burning effect
	KeyValues *pProxiesAnimatedTexture = pProxies->FindKey( "AnimatedTexture", true );
	pProxiesAnimatedTexture->SetString( "animatedtexturevar", "$detail" );
	pProxiesAnimatedTexture->SetString( "animatedtextureframenumvar", "$detailframe" );
	pProxiesAnimatedTexture->SetString( "animatedtextureframerate", "30" );

	KeyValues *pProxiesBurnLevel = pProxies->FindKey( "BurnLevel", true );
	pProxiesBurnLevel->SetString( "resultVar", "$detailblendfactor" );

	// Proxies for paintable items
	if ( m_nColorAlphaType == kPaintable )
	{
		KeyValues *pProxiesItemTintColor = pProxies->FindKey( "ItemTintColor", true );
		pProxiesItemTintColor->SetString( "resultVar", "$colortint_tmp" );

		KeyValues *pProxiesSelectFirstIfNonZero = pProxies->FindKey( "SelectFirstIfNonZero", true );
		pProxiesSelectFirstIfNonZero->SetString( "srcVar1", "$colortint_tmp" );
		pProxiesSelectFirstIfNonZero->SetString( "srcVar2", "$colortint_base" );
		pProxiesSelectFirstIfNonZero->SetString( "resultVar", "$color2" );

		// Proxies for the jarate effect
		KeyValues *pProxiesYellowLevel = pProxies->FindKey( "YellowLevel", true );
		pProxiesYellowLevel->SetString( "resultVar", "$yellow" );

		KeyValues *pProxiesMultiply = pProxies->FindKey( "Multiply", true );
		pProxiesMultiply->SetString( "srcVar1", "$color2" );
		pProxiesMultiply->SetString( "srcVar2", "$yellow" );
		pProxiesMultiply->SetString( "resultVar", "$color2" );
	}
	else
	{
		// Proxies for the jarate effect
		KeyValues *pProxiesYellowLevel = pProxies->FindKey( "YellowLevel", true );
		pProxiesYellowLevel->SetString( "resultVar", "$yellow" );

		KeyValues *pProxiesEquals = pProxies->FindKey( "Equals", true );
		pProxiesEquals->SetString( "srcVar1", "$yellow" );
		pProxiesEquals->SetString( "resultVar", "$color2" );
	}
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetVMT::UpdateManifest( KeyValues *pKV )
{
	pKV->SetString( "materialId", m_sMaterialId.String() );
	pKV->SetString( "materialType", MaterialTypeToString( GetMaterialType() ) );
	pKV->SetInt( "duplicate", GetDuplicate() );
	pKV->SetInt( "target_width", m_nTargetWidth );
	pKV->SetInt( "target_height", m_nTargetHeight );

	if ( !GetDuplicate() )
	{
		KeyValues *pColorSubKey = new KeyValues( "color" );
		pKV->AddSubKey( pColorSubKey );

		pColorSubKey->SetString( "alphaType", ColorAlphaTypeToString( GetColorAlphaType() ) );

		FOR_EACH_VEC( m_vecTargetVTFs, i )
		{
			KeyValues *pSkinSubKey;
			const char *pszSkinType = CItemUpload::Manifest()->GetMaterialSkin(i);
			if ( pszSkinType )
				pSkinSubKey = new KeyValues( pszSkinType );
			else
				pSkinSubKey = pColorSubKey;

			FOR_EACH_VEC( m_vecTargetVTFs[i], j )
			{
				if ( m_vecTargetVTFs[i][j].IsValid() )
				{
					const char *pszTextureType = CItemUpload::Manifest()->GetTextureType(j);
					KeyValues *pSubKey = new KeyValues( pszTextureType );
					pSkinSubKey->AddSubKey( pSubKey );
					m_vecTargetVTFs[i][j]->UpdateManifest( pSubKey );
				}
			}
		}
	}
}


//=============================================================================
//
//=============================================================================
CTargetIcon::CTargetIcon( CAsset *pAsset, int nIconType )
: CTargetVMT( pAsset, pAsset )
, m_nIconType( nIconType )
{
	int nWidth, nHeight;
	CItemUpload::Manifest()->GetIconDimensions( m_nIconType, nWidth, nHeight );
	SetTargetResolution( nWidth, nHeight );

	const char *pszSuffix = CItemUpload::Manifest()->GetIconFilenameAppend( m_nIconType );
	SetNameSuffix( pszSuffix );

	KeyValues *pVMTKV = CItemUpload::Manifest()->GetIconVMTTemplate( m_nIconType );
	if ( pVMTKV )
	{
		SetVMTKV( pVMTKV );
	}

	SetMaterialType( CItemUpload::Manifest()->GetDefaultMaterialType() );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetIcon::SetTargetVTF( const char *pszFilename )
{
	return CTargetVMT::SetTargetVTF( m_vecTargetVTFs[0][0], pszFilename, "" );
}


//=============================================================================
//
//=============================================================================
CTargetDMX::CTargetDMX( CAsset *pAsset, const CTargetQC *pTargetQC )
	: CTargetBase( pAsset, pTargetQC )
	, m_pDmRoot( NULL )
	, m_nLod( -1 )
	, m_flAnimationLoopStartTime( -1.f )
{
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CTargetDMX::~CTargetDMX()
{
	Clear();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetDMX::IsOk( CUtlString &sMsg ) const
{
	if ( m_nLod < 0 )
	{
		sMsg = "Invalid LOD (";
		sMsg += m_nLod;
		sMsg += ")";
		return false;
	}

	if ( m_sInputFile.IsEmpty() || m_sExtension.IsEmpty() )
	{
		sMsg = "No input file specified";
		return false;
	}

	if ( !CItemUpload::FileExists( m_sInputFile.String() ) )
	{
		sMsg = "Input file: \"";
		sMsg += m_sInputFile;
		sMsg += "\" doesn't exist";
		return false;
	}

	if ( !( IsInputSmd() || IsInputObj() || IsInputDmx() || IsInputFbx() ) )
	{
		sMsg = "Input file: \"";
		sMsg += m_sInputFile;
		sMsg += "\" is not a DMX, OBJ or SMD file";
		return false;
	}

	if ( !m_pDmRoot )
	{
		sMsg = "Input file: \"";
		sMsg += m_sInputFile;
		sMsg += "\" couldn't be read";
		return false;
	}

	const int nPolyCount = GetPolyCount();
	if ( nPolyCount <= 0 && m_strQCITemplate.IsEmpty() )
	{
		sMsg = "Invalid polygon count (";
		sMsg += nPolyCount;
		sMsg += ")";
		return false;
	}

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetDMX::Compile()
{
	if ( !ReloadFile() )
		return false;

	if ( !CTargetBase::Compile() )
		return false;

	CUtlString sName;
	if ( !GetOutputPath( sName, 0, PATH_FLAG_FILE | PATH_FLAG_EXTENSION ) )
		return false;

	CUtlString sTmp;
	if ( !IsOk( sTmp ) )
	{
		Warning( "CTarget%s::Compile( %s ) - Not Valid: %s\n", GetTypeString(), sName.String(), sTmp.String() );
		return false;
	}

	CUtlString sAbsPath;
	GetOutputPath( sAbsPath, 0 );

	CUtlString sRelPath;
	GetOutputPath( sRelPath, 0, PATH_FLAG_ALL & ~PATH_FLAG_ABSOLUTE );

	if ( sAbsPath.IsEmpty() || sRelPath.IsEmpty() )
	{
		Warning( "CTarget%s::Compile( %s ) - GetOutputPath failed\n", GetTypeString(), sName.String() );
		return false;
	}

	Msg( "Compiling %s: %s\n", GetTypeString(), sRelPath.String() );

	AddOrEditP4File( sAbsPath.String() );

	CUndoScopeGuard undo( "replaceMaterial" );

	ReplaceMaterials();

	SkinToBipHead();

#ifdef _DEBUG
	const bool bRet = g_pDataModel->SaveToFile( sAbsPath.String(), NULL, "keyvalues2", "model", m_pDmRoot );
#else // ifdef _DEBUG
	const bool bRet = g_pDataModel->SaveToFile( sAbsPath.String(), NULL, "binary", "model", m_pDmRoot );
#endif // _DEBUG

	undo.Abort();

	if ( !bRet )
	{
		Msg( "CTarget%s::Compile( %s ) - SaveToFile failed - \"%s\"\n", GetTypeString(), sName.String(), sAbsPath.String() );
		return false;
	}

	if ( !CheckFile( sAbsPath.String() ) )
	{
		Warning( "CTarget%s::Compile( %s ) - File Check Failed - \"%s\"\n", GetTypeString(), sName.String(), sAbsPath.String() );
		return false;
	}

	if ( !OutputQCIFile() )
	{
		return false;
	}

	if ( CItemUpload::Manifest()->UseTerseMessages() )
	{
		Msg( " - Compilation successful.\n" );
	}
	else
	{
		Msg( "CTarget%s::Compile OK! - %s\n", GetTypeString(), sRelPath.String() );
	}

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetDMX::GetInputPaths(
	CUtlVector< CUtlString > &sInputPaths,
	bool bRelative /* = true */,
	bool bRecurse /* = true */,
	bool bExtension /* = true */ )
{
	sInputPaths.AddToTail( m_sInputFile );

	return CTargetBase::GetInputPaths( sInputPaths, bRelative, bRecurse, bExtension );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const ExtensionList *CTargetDMX::GetExtensionsAndCount( void ) const
{
	static ExtensionList vecExtensions;
	vecExtensions.RemoveAll();
	vecExtensions.AddToTail( ".dmx" );
	if ( !m_strQCITemplate.IsEmpty() )
	{
		vecExtensions.AddToTail( ".qci" );
	}

	return &vecExtensions;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetDMX::GetName( CUtlString &sName ) const
{
	CTargetBase::GetName( sName );

	if ( GetLod() > 0 )
	{
		sName += "_lod";
		sName += GetLod();
	}
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CTargetDMX::GetPolyCount() const
{
	if ( !m_pDmRoot )
		return 0;

	int nPolyCount = 0;

	const DmFileId_t hRootFileId = m_pDmRoot->GetFileId();

	for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement(); hElement != DMELEMENT_HANDLE_INVALID; hElement = g_pDataModel->NextAllocatedElement( hElement ) )
	{
		if ( hElement == DMELEMENT_HANDLE_INVALID )
			continue;

		CDmeMesh *pDmeMesh = CastElement< CDmeMesh >( g_pDataModel->GetElement( hElement ) );
		if ( !pDmeMesh || pDmeMesh->GetFileId() != hRootFileId )
			continue;

		for ( int i = 0; i < pDmeMesh->FaceSetCount(); ++i )
		{
			CDmeFaceSet *pDmeFaceSet = pDmeMesh->GetFaceSet( i );
			if ( !pDmeFaceSet )
				continue;

			nPolyCount += pDmeFaceSet->GetFaceCount();
		}
	}

	return nPolyCount;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CTargetDMX::GetTriangleCount() const
{
	if ( !m_pDmRoot )
		return 0;

	int nTriangleCount = 0;

	const DmFileId_t hRootFileId = m_pDmRoot->GetFileId();

	for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement(); hElement != DMELEMENT_HANDLE_INVALID; hElement = g_pDataModel->NextAllocatedElement( hElement ) )
	{
		if ( hElement == DMELEMENT_HANDLE_INVALID )
			continue;

		CDmeMesh *pDmeMesh = CastElement< CDmeMesh >( g_pDataModel->GetElement( hElement ) );
		if ( !pDmeMesh || pDmeMesh->GetFileId() != hRootFileId )
			continue;

		for ( int i = 0; i < pDmeMesh->FaceSetCount(); ++i )
		{
			CDmeFaceSet *pDmeFaceSet = pDmeMesh->GetFaceSet( i );
			if ( !pDmeFaceSet )
				continue;

			nTriangleCount += pDmeFaceSet->GetTriangulatedIndexCount() / 3;
		}
	}

	return nTriangleCount;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CTargetDMX::GetVertexCount() const
{
	if ( !m_pDmRoot )
		return 0;

	int nVertexCount = 0;

	const DmFileId_t hRootFileId = m_pDmRoot->GetFileId();

	for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement(); hElement != DMELEMENT_HANDLE_INVALID; hElement = g_pDataModel->NextAllocatedElement( hElement ) )
	{
		if ( hElement == DMELEMENT_HANDLE_INVALID )
			continue;

		CDmeMesh *pDmeMesh = CastElement< CDmeMesh >( g_pDataModel->GetElement( hElement ) );
		if ( !pDmeMesh || pDmeMesh->GetFileId() != hRootFileId )
			continue;

		CDmeVertexData *pVertexData = pDmeMesh->GetBindBaseState();
		if ( !pVertexData )
			continue;

		nVertexCount += pVertexData->VertexCount();
	}

	return nVertexCount;
}


bool CTargetDMX::GetAnimationFrameInfo( float& flFrameRate, int& nFrameCount ) const
{
	if ( !m_pDmRoot )
		return false;

	CDmeAnimationList *pDmeAnimationList = m_pDmRoot->GetValueElement< CDmeAnimationList >( "animationList" );
	CDmeChannelsClip *pDmeChannelsClip = NULL;
 
	if ( pDmeAnimationList )
	{
		if ( pDmeAnimationList->GetAnimationCount() < 0 )
		{
			Warning( "No DmeChannelsClips found in root.animationList of DMX file: \"%s\"\n", m_sInputFile.String() );
			return false;
		}
 
		pDmeChannelsClip = pDmeAnimationList->GetAnimation( 0 );
		if ( !pDmeChannelsClip )
		{
			Warning( "No DmeChannelsClip found in root.animationList[0] of DMX file: \"%s\"\n", m_sInputFile.String() );
			return false;
		}
 
		flFrameRate = pDmeChannelsClip->GetValue< int >( "frameRate", 30 );
		nFrameCount = (int)( ( pDmeChannelsClip->GetDuration().GetSeconds() * flFrameRate ) + 0.5f ) + 1;

		return true;
	}

	return false;
}


//-----------------------------------------------------------------------------
// Returns true if all meshes in the DMX data have skinning information
//         false if any mesh doesn't have skinning data
//-----------------------------------------------------------------------------
bool CTargetDMX::AreAllMeshesSkinned() const
{
	if ( !m_pDmRoot )
		return false;			// No DMX data

	const DmFileId_t hRootFileId = m_pDmRoot->GetFileId();

	int nMeshCount = 0;

	for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement(); hElement != DMELEMENT_HANDLE_INVALID; hElement = g_pDataModel->NextAllocatedElement( hElement ) )
	{
		if ( hElement == DMELEMENT_HANDLE_INVALID )
			continue;

		CDmeMesh *pDmeMesh = CastElement< CDmeMesh >( g_pDataModel->GetElement( hElement ) );
		if ( !pDmeMesh || pDmeMesh->GetFileId() != hRootFileId )
			continue;

		CDmeVertexData *pDmeVertexData = pDmeMesh->GetBindBaseState();
		if ( !pDmeVertexData )
			continue;

		// If even one mesh doesn't have skinning data, abort
		if ( !pDmeVertexData->HasSkinningData() )
			return false;
	}

	// Make sure we saw at least one mesh
	return ( nMeshCount > 0 );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const CUtlString &CTargetDMX::GetInputFile() const
{
	return m_sInputFile;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetDMX::Clear()
{
	if ( m_pDmRoot )
	{
		CDisableUndoScopeGuard noUndo;

		m_targetVmtList.RemoveAll();

		g_pDataModel->UnloadFile( m_pDmRoot->GetFileId() );
		m_pDmRoot = NULL;
	}
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetDMX::SetInputFile( const char *pszFilename )
{
	// Because of the way datamodel works, bad things will happen if the same file is loaded twice
	Clear();

	m_sInputFile = pszFilename;

	char szBuf[ k64KB ];

	V_ExtractFileExtension( pszFilename, szBuf, ARRAYSIZE( szBuf ) );
	m_sExtension = szBuf;

	DmFileId_t hRootFileId = g_pDataModel->GetFileId( pszFilename );
	DmElementHandle_t hRootElement = g_pDataModel->GetFileRoot( hRootFileId );
	CDmElement *pDmRoot = g_pDataModel->GetElement( hRootElement );

	if ( !pDmRoot )
	{
		pDmRoot = LoadDmx();
	}

	if ( !pDmRoot )
	{
		pDmRoot = LoadFbx();
	}

	if ( !pDmRoot )
	{
		pDmRoot = LoadSmd();
	}

	if ( !pDmRoot )
	{
		pDmRoot = LoadObj();
	}

	if ( !pDmRoot )
	{
		Warning( "ERROR: Don't Know What Type Of Input File Is\n" );
		return false;
	}

	m_pDmRoot = pDmRoot;

	// TODO: Change to walking from root?

	hRootFileId = m_pDmRoot->GetFileId();

	// we must store the root handle so we don't reload the same file multiple time
	g_pDataModel->SetFileRoot( hRootFileId, m_pDmRoot->GetHandle() );

	int nMaterialType = 0;
	for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement(); hElement != DMELEMENT_HANDLE_INVALID; hElement = g_pDataModel->NextAllocatedElement( hElement ) )
	{
		if ( hElement == DMELEMENT_HANDLE_INVALID )
			continue;

		CDmeMaterial *pDmeMaterial = CastElement< CDmeMaterial >( g_pDataModel->GetElement( hElement ) );
		if ( !pDmeMaterial || pDmeMaterial->GetFileId() != hRootFileId )
			continue;

		CSmartPtr< CTargetVMT > pTargetVMT = Asset()->FindOrAddMaterial( pDmeMaterial->GetMaterialName(), nMaterialType );
		if ( pTargetVMT.IsValid() )
		{
			m_targetVmtList.AddToTail( pTargetVMT );
			++nMaterialType;
		}
	}

	CUtlString sTmp;
	if ( IsOk( sTmp ) )
		return true;

	Warning( FUNCTION_LINE_STRING "Error! CTarget%s - Not Valid: %s\n", GetTypeString(), sTmp.String() );
	return false;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetDMX::ReloadFile()
{
	// Keep references to all target VMT's to prevent them from being destroyed by a reload of the same file
	CUtlVector< CSmartPtr< CTargetVMT > > tmpVmtCopy;
	tmpVmtCopy = m_targetVmtList;

	// 'reset' the input file from the current value to force a reload on compile, make a temp copy as it's going to be overwritten
	if ( !SetInputFile( CUtlString( GetInputFile() ).String() ) )
		return false;

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetDMX::IsInputObj() const
{
	// TODO: Perhaps look at magic in the start of the file

	return !V_stricmp( "obj", m_sExtension.String() );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetDMX::IsInputSmd() const
{
	// TODO: Perhaps look at magic in the start of the file

	return !V_stricmp( "smd", m_sExtension.String() );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetDMX::IsInputDmx() const
{
	// TODO: Perhaps look at magic in the start of the file

	return !V_stricmp( "dmx", m_sExtension.String() );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetDMX::IsInputFbx() const
{
	// TODO: Perhaps look at magic in the start of the file

	return !V_stricmp( "fbx", m_sExtension.String() );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CDmElement *CTargetDMX::LoadObj()
{
	if ( !IsInputObj() )
		return NULL;

	CDisableUndoScopeGuard noUndo;

	CDmObjSerializer dmObjSerializer;

	return dmObjSerializer.ReadOBJ( m_sInputFile.String() );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CDmElement *CTargetDMX::LoadSmd()
{
	if ( !IsInputSmd() )
		return NULL;

	CDisableUndoScopeGuard noUndo;

	CDmSmdSerializer dmSmdSerializer;

	dmSmdSerializer.SetIsAnimation( !m_strQCITemplate.IsEmpty() );

	return dmSmdSerializer.ReadSMD( m_sInputFile.String() );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CDmElement *CTargetDMX::LoadDmx()
{
	if ( !IsInputDmx() )
		return NULL;

	CDisableUndoScopeGuard noUndo;

	CDmElement *pDmRoot = NULL;

	g_pDataModel->RestoreFromFile( m_sInputFile.String(), NULL, NULL, &pDmRoot );

	return pDmRoot;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CDmElement *CTargetDMX::LoadFbx()
{
	if ( !IsInputFbx() )
		return NULL;

	CDisableUndoScopeGuard noUndo;

	CDmFbxSerializer dmFbxSerializer;

	return dmFbxSerializer.ReadFBX( m_sInputFile.String() );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetDMX::ReplaceMaterials() const
{
	CUtlString sTmp;
	if ( !IsOk( sTmp ) )
	{
		Warning( FUNCTION_LINE_STRING "Error! CTarget%s - Not Valid: %s\n", GetTypeString(), sTmp.String() );
		return;
	}

	DmFileId_t hRootFileId = m_pDmRoot->GetFileId();
	if ( hRootFileId == DMFILEID_INVALID )
		return;

	CAsset *pAsset = Asset();
	if ( !pAsset )
		return;

	CUtlMap< CUtlString, CUtlString > materialMap( UtlStringLessThan );

	CUtlString sRelativeDir;
	pAsset->GetRelativeDir( sRelativeDir, NULL, this );

	char szBuf[ k64KB ];

	for ( int i = 0; i < pAsset->GetTargetVMTCount(); ++i )
	{
		CTargetVMT *pTargetVMT = pAsset->GetTargetVMT( i );
		if ( !pTargetVMT )
			continue;

		CUtlString sMaterialId;
		pTargetVMT->GetMaterialId( sMaterialId );

		if ( sMaterialId.IsEmpty() || materialMap.IsValidIndex( materialMap.Find( sMaterialId ) ) )
			continue;

		CTargetVMT *pSrcTargetVMT = pTargetVMT;

		if ( pTargetVMT->GetDuplicate() )
		{
			const int nMaterialType = pTargetVMT->GetMaterialType();
			if ( nMaterialType < 0 || nMaterialType >= CItemUpload::Manifest()->GetNumMaterialTypes() )
			{
				Warning( "CTarget%s::ReplaceMaterials - Duplicate VMT (%s) with bad material type (%d)\n", GetTypeString(), sMaterialId.String(), nMaterialType );
				continue;
			}

			for ( int j = 0; j < pAsset->GetTargetVMTCount(); ++j )
			{
				CTargetVMT *pTmpTargetVMT = pAsset->GetTargetVMT( j );
				if ( !pTmpTargetVMT || pTmpTargetVMT->GetDuplicate() )
					continue;

				if ( pTmpTargetVMT->GetMaterialType() == nMaterialType )
				{
					pSrcTargetVMT = pTmpTargetVMT;
					break;
				}
			}

			if ( pTargetVMT == pSrcTargetVMT )
			{
				Warning( "CTarget%s::ReplaceMaterials - Duplicate VMT (%s) Cannot Find Source Material Type (%d)\n", GetTypeString(), sMaterialId.String(), nMaterialType );
				continue;
			}
		}

		sMaterialId.FixSlashes( '/' );

		// The full extension can be _red.vmt or _blue.vmt so we need to keep the _red but not the .vmt
		CUtlString sMaterialPath;
		pSrcTargetVMT->GetOutputPath( sMaterialPath, 0, PATH_FLAG_FILE | PATH_FLAG_EXTENSION | PATH_FLAG_PATH | PATH_FLAG_MODELS );

		V_StripExtension( sMaterialPath, szBuf, ARRAYSIZE( szBuf ) );
		sMaterialPath.FixSlashes( '/' );

		sMaterialPath = szBuf;

		materialMap.InsertOrReplace( sMaterialId, sMaterialPath.String() );
	}


	for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement(); hElement != DMELEMENT_HANDLE_INVALID; hElement = g_pDataModel->NextAllocatedElement( hElement ) )
	{
		if ( hElement == DMELEMENT_HANDLE_INVALID )
			continue;

		CDmeMesh *pDmeMesh = CastElement< CDmeMesh >( g_pDataModel->GetElement( hElement ) );
		if ( !pDmeMesh || pDmeMesh->GetFileId() != hRootFileId )
			continue;

		FOR_EACH_MAP_FAST( materialMap, mmi )
		{
			pDmeMesh->ReplaceMaterial( materialMap.Key( mmi ), materialMap.Element( mmi ) );
		}
	}
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetDMX::SkinToBipHead()
{
	CAssetTF *pAssetTF = dynamic_cast< CAssetTF * >( Asset() );
	if ( !pAssetTF )
		return;

	if ( !pAssetTF->SkinToBipHead() )
		return;

	CDisableUndoScopeGuard undoGuard;

	CDmeModel *pSrcModel = m_pDmRoot->GetValueElement< CDmeModel >( "model" );
	if ( !pSrcModel )
		return;

	const DmFileId_t nFileId = m_pDmRoot->GetFileId();

	CDmElement *pDstRoot = CreateElement< CDmElement >( m_pDmRoot->GetName(), nFileId );

	CDmeModel *pDstModel = CreateElement< CDmeModel >( pSrcModel->GetName(), nFileId );
	pDstModel->GetTransform()->SetName( pDstModel->GetName() );

	pDstRoot->SetValue( "model", pDstModel );
	pDstRoot->SetValue( "skeleton", pDstModel );

	CDmeJoint *pBipHead = CreateElement< CDmeJoint >( "bip_head", nFileId );
	pBipHead->GetTransform()->SetName( "bip_head" );

	pDstModel->AddChild( pBipHead );
	pDstModel->AddJoint( pBipHead );

	const Vector &vBipHead = pAssetTF->GetBipHead();
	const RadianEuler &eBipHead = pAssetTF->GetBipHeadRotation();
	const Quaternion qBipHead = eBipHead;

	pBipHead->GetTransform()->SetOrientation( qBipHead );

	CUtlStack< CDmeDag * > depthFirstStack;
	depthFirstStack.Push( pSrcModel );

	while ( depthFirstStack.Count() )
	{
		CDmeDag *pDmeDag = NULL;

		depthFirstStack.Pop( pDmeDag );

		if ( !pDmeDag )
			continue;

		SkinToBipHead_R( pDstModel, pDmeDag, vBipHead );

		for ( int i = pDmeDag->GetChildCount() - 1; i >= 0; --i )
		{
			depthFirstStack.Push( pDmeDag->GetChild( i ) );
		}
	}

	pDstModel->CaptureJointsToBaseState( "bind" );

	DestroyElement( m_pDmRoot, TD_ALL );
	m_pDmRoot = pDstRoot;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetDMX::SkinToBipHead_R( CDmeModel *pDstModel, CDmeDag *pSrcDag, const Vector &vBipHead )
{
	if ( !pDstModel || !pSrcDag )
		return;

	CDmeMesh *pSrcMesh = CastElement< CDmeMesh >( pSrcDag->GetShape() );
	if ( !pSrcMesh )
		return;

	CDmeVertexData *pSrcBind = pSrcMesh->GetBindBaseState();
	if ( !pSrcBind )
		return;

	CDmeDag *pDstDag = CreateElement< CDmeDag >( pSrcDag->GetName(), pDstModel->GetFileId() );
	if ( !pDstDag )
		return;

	pDstDag->GetTransform()->SetName( pDstDag->GetName() );

	CDmeMesh *pDstMesh = pSrcMesh->Copy();
	if ( !pDstMesh )
		return;

	// Current state is left empty, not sure why
	pDstMesh->SetBindBaseState( pDstMesh->GetCurrentBaseState() );

	pDstMesh->Resolve();

	pDstDag->SetShape( pDstMesh );
	pDstModel->AddChild( pDstDag );

	matrix3x4_t mIdentity;	// TODO: Is there a static identity matrix like quat_identity?
	SetIdentityMatrix( mIdentity );
	matrix3x4_t mSrcAbs;
	//	pSrcDag->GetAbsTransform( mSrcAbs );

	{
		matrix3x4_t parentToWorld;
		pSrcDag->GetParentWorldMatrix( parentToWorld );

		matrix3x4_t localMatrix;
		pSrcDag->GetLocalMatrix( localMatrix );

		ConcatTransforms( parentToWorld, localMatrix, mSrcAbs );
	}

	matrix3x4_t mNormal;
	MatrixInverseTranspose( mSrcAbs, mNormal );

	bool bConvertToWorldSpace = !MatricesAreEqual( mIdentity, mSrcAbs );

	for ( int i = 0; i < pDstMesh->BaseStateCount(); ++i )
	{
		CDmeVertexData *pDstVertexData = pDstMesh->GetBaseState( i );
		if ( !pDstVertexData )
			continue;
		pDstVertexData->Resolve();

		// Remove all skinning information
		// This seems a little odd, creating data to remove it but if it already exists this is
		// the only way of setting the joint count to 0 without cheating (i.e. referring to the field by name)
		FieldIndex_t nJointWeightField;
		FieldIndex_t nJointIndexField;
		pDstVertexData->CreateJointWeightsAndIndices( 0, &nJointWeightField, &nJointIndexField );

		pDstVertexData->RemoveAttribute( pDstVertexData->FieldName( nJointWeightField ) );
		pDstVertexData->RemoveAttribute( pDstVertexData->FieldName( nJointIndexField ) );

		// Convert data to world space if required
		{
			Vector vTmp;
			static const int nZero = 0;
			static const float flOne = 1;

			FieldIndex_t nPositionField = pDstVertexData->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
			if ( nPositionField >= 0 )
			{
				CDmAttribute *pPosAttr = pDstVertexData->GetVertexData( nPositionField );
				if ( pPosAttr )
				{
					CDmrArray< Vector > posData = pPosAttr;

					// Set everything up to be skinned to joint 0
					pDstVertexData->CreateJointWeightsAndIndices( 1, &nJointWeightField, &nJointIndexField );
					pDstVertexData->AddVertexData( nJointIndexField, posData.Count() );
					pDstVertexData->AddVertexData( nJointWeightField, posData.Count() );

					if ( bConvertToWorldSpace )
					{
						for ( int iPos = 0; iPos < posData.Count(); ++iPos )
						{
							pDstVertexData->SetVertexData( nJointIndexField, iPos, 1, AT_INT, &nZero );
							pDstVertexData->SetVertexData( nJointWeightField, iPos, 1, AT_FLOAT, &flOne );
						}
					}
					else
					{
						for ( int iPos = 0; iPos < posData.Count(); ++iPos )
						{
							VectorTransform( posData[iPos], mSrcAbs, vTmp );
							posData.Set( iPos, vTmp );
							pDstVertexData->SetVertexData( nJointIndexField, i, 1, AT_INT, &nZero );
							pDstVertexData->SetVertexData( nJointWeightField, i, 1, AT_FLOAT, &flOne );
						}
					}
				}
			}

			FieldIndex_t nNormalField = pDstVertexData->FindFieldIndex( CDmeVertexData::FIELD_NORMAL );
			if ( nNormalField >= 0 )
			{
				CDmAttribute *pNormalAttr = pDstVertexData->GetVertexData( nNormalField );
				if ( pNormalAttr )
				{
					CDmrArray< Vector > normalData = pNormalAttr;
					for ( int iData = 0; iData < normalData.Count(); ++iData )
					{
						VectorRotate( normalData[iData], mNormal, vTmp );
						vTmp.NormalizeInPlace();
						normalData.Set( iData, vTmp );
					}
				}
			}
		}

		pDstVertexData->Resolve();
	}

	Vector vMin;
	Vector vMax;
	pDstMesh->GetBoundingBox( vMin, vMax );

	const Vector vCenter = ( vMax - vMin ) / 2.0 + vMin;
	const float flSqDistOrigin = vCenter.DistToSqr( Vector( 0, 0, 0 ) );
	const float flSqDistBipHead = vCenter.DistToSqr( vBipHead );

	// See where the model was modelled... around the bip_head bone or at the origin?
	// There are probably other problems which aren't accounted for this is a really naive
	// algorithm

	if ( flSqDistBipHead < flSqDistOrigin  )
	{
		// Model centered around bip head, subtract vBipHead from position data

		Vector vTmp;

		for ( int i = 0; i < pDstMesh->BaseStateCount(); ++i )
		{
			CDmeVertexData *pDstVertexData = pDstMesh->GetBaseState( i );
			if ( !pDstVertexData )
				continue;

			pDstVertexData->Resolve();

			FieldIndex_t nPositionField = pDstVertexData->FindFieldIndex( CDmeVertexData::FIELD_POSITION );
			if ( nPositionField >= 0 )
			{
				CDmAttribute *pPosAttr = pDstVertexData->GetVertexData( nPositionField );
				if ( pPosAttr )
				{
					CDmrArray< Vector > posData = pPosAttr;

					for ( int iPos = 0; iPos < posData.Count(); ++iPos )
					{
						VectorSubtract( posData[iPos], vBipHead, vTmp );
						posData.Set( iPos, vTmp );
					}
				}
			}

			pDstVertexData->Resolve();
		}
	}
}


//=============================================================================
//
//=============================================================================
bool CTargetDMX::OutputQCIFile()
{
	// Should we output QCI?
	if ( !m_strQCITemplate.IsEmpty() )
	{
		CUtlString sDMXName;
		if ( !GetOutputPath( sDMXName, 0, PATH_FLAG_FILE | PATH_FLAG_EXTENSION ) )
			return false;
			
		CUtlString sName;
		if ( !GetOutputPath( sName, 1, PATH_FLAG_FILE ) )
			return false;

		CUtlString sAbsPath;
		if ( !GetOutputPath( sAbsPath, 1 ) )
			return false;

		float flFrameRate;
		int nFrameCount;
		if ( !GetAnimationFrameInfo( flFrameRate, nFrameCount ) )
		{
			Warning( "Failed to get animation's frame info for %s\n", sAbsPath.String() );
			return false;
		}

		const int nBlendFrameOffset = 5;
		const int nLastFrameIndex = nFrameCount - 1;

		CUtlString strLayerName = "layer_";
		strLayerName += sName;

		CUtlString strBuf = m_strQCITemplate.Replace( "<ITEMTEST_SEQUENCE_LAYER_NAME>", strLayerName.String() );
		strBuf = strBuf.Replace( "<ITEMTEST_SEQUENCE_NAME>", sName.String() );
		strBuf = strBuf.Replace( "<ITEMTEST_DMX_FILE>", sName.String() );
		strBuf = strBuf.Replace( "<FPS>", CFmtStr( "fps %d", (int)flFrameRate ) );
		strBuf = strBuf.Replace( "<BLEND_LAYER_RANGE>", CFmtStr( "0 %d %d <LAST_FRAME_INDEX>", nBlendFrameOffset, nLastFrameIndex - nBlendFrameOffset ) );
		strBuf = strBuf.Replace( "<FRAME_COUNT>", CFmtStr( "%d", nFrameCount ) );
		strBuf = strBuf.Replace( "<LAST_FRAME_INDEX>", CFmtStr( "%d", nLastFrameIndex ) );
		

		CUtlBuffer bufQCFile( 0, 0, CUtlBuffer::TEXT_BUFFER );
		bufQCFile.PutString( strBuf.String() );

		AddOrEditP4File( sAbsPath.String() );

		g_pFullFileSystem->WriteFile( sAbsPath.String(), "MOD", bufQCFile );
	}

	return true;
}


void LeftRightToValueBalance( float flLeft, float flRight, float &flOutValue, float &flOutBalance )
{
	if ( flLeft > flRight )
	{
		flOutValue = flLeft;
		flOutBalance = 0.5f * flRight / flLeft;
	}
	else
	{
		flOutValue = flRight;
		flOutBalance = 1.0f - 0.5f * flRight / flLeft;
	}
}

void BufPrintf( CUtlBuffer& buf, int nLevel, const char *fmt, ... )
{
	va_list argptr;
	va_start( argptr, fmt );

	while ( nLevel-- > 0 )
	{
		buf.Printf( "  " );
	}

	buf.VaPrintf( fmt, argptr );
	va_end( argptr );
}

void BufBeginBlock( CUtlBuffer& buf, int &nLevel )
{
	BufPrintf( buf, nLevel, "{\n" );
	++nLevel;
}

void BufEndBlock( CUtlBuffer& buf, int &nLevel )
{
	--nLevel;
	BufPrintf( buf, nLevel, "}\n" );
}

template< class T >
CDmeTypedLogLayer< T > *GetTopmostLogLayer( CDmeChannel *pChannel )
{
	if ( !pChannel )
		return NULL;

	CDmeTypedLog< T > *pLog = CastElement< CDmeTypedLog< T > >( pChannel->GetLog() );
	if ( !pLog )
		return NULL;

	return pLog->GetLayer( pLog->GetTopmostLayer() );
}

void CTargetDMX::OutputSounds( CUtlBuffer &buf, int nIndentLevel, CDmElement *pExportedSounds )
{
	if ( !pExportedSounds )
		return;

	CDmrElementArray< CDmElement > soundsArray( pExportedSounds, "sounds" );
	if ( !soundsArray.IsValid() )
		return;

	int nSounds = soundsArray.Count();
	if ( nSounds == 0 )
		return;

	CUtlBuffer soundScriptBuffer( 0, 0, CUtlBuffer::TEXT_BUFFER );
	if ( !m_strSoundScriptFile.IsEmpty() )
	{
		if ( !g_pFullFileSystem->ReadFile( m_strSoundScriptFile.String(), "MOD", soundScriptBuffer ) )
		{
			Warning( "Failed to load sound script file\n" );
			return;
		}
	}
	else
	{
		Warning( "Sound script file not specified\n" );
		return;
	}

	BufPrintf( buf, nIndentLevel, "channel \"sounds\"\n" );
	BufBeginBlock( buf, nIndentLevel );
	{
		for ( int i = 0; i < nSounds; ++i )
		{
			CDmElement *pSound = soundsArray[ i ];
			if ( !pSound )
				continue;

			float flStartTime = pSound->GetValue< float >( "startTime" );
			CUtlString strWaveName = pSound->GetValueString( "fileName" );
			V_FixSlashes( strWaveName.GetForModify(), '/' );
			if ( !g_pFullFileSystem->FileExists( CFmtStr( "sound/%s", strWaveName.String() ), "GAME" ) )
			{
				CUtlString strSoundName = strWaveName.StripExtension();
				if ( !g_pFullFileSystem->FileExists( CFmtStr( "sound/%s.mp3", strSoundName.String() ), "GAME" ) )
				{
					Warning( "OutputSounds to VCD missing sound file: %s.wav/mp3\n", strSoundName.String() );
					continue;
				}
			}

			bool bIsVO = V_strstr( strWaveName.String(), "vo/" ) != NULL;
			const char *pszSoundScriptName = CFmtStr( "<CLASS_NAME>_<ITEMTEST_SEQUENCE_NAME>_%s", pSound->GetName() );

			// write sound script entry to sound script file
			int nSoundIndentLevel = 0;
			BufPrintf( soundScriptBuffer, nSoundIndentLevel, "\"%s\"\n", pszSoundScriptName );
			BufBeginBlock( soundScriptBuffer, nSoundIndentLevel );
			{
				BufPrintf( soundScriptBuffer, nSoundIndentLevel, "\"channel\"			\"%s\"\n", bIsVO ? "CHAN_VOICE" : "CHAN_STATIC" );
				BufPrintf( soundScriptBuffer, nSoundIndentLevel, "\"volume\"			\"VOL_NORM\"\n" );
				BufPrintf( soundScriptBuffer, nSoundIndentLevel, "\"pitch\"				\"PITCH_NORM\"\n" );
				BufPrintf( soundScriptBuffer, nSoundIndentLevel, "\"soundlevel\"		\"SNDLVL_95dB\"\n" );
				BufPrintf( soundScriptBuffer, nSoundIndentLevel, "\"wave\"				\"%s\"\n", strWaveName.String() );
			}
			BufEndBlock( soundScriptBuffer, nSoundIndentLevel );

			// write vcd speak event
			BufPrintf( buf, nIndentLevel, "event speak \"%s\"\n", pszSoundScriptName );
			BufBeginBlock( buf, nIndentLevel );
			{
				BufPrintf( buf, nIndentLevel, "time %f <MAX_SEQUENCE_LENGTH>\n", flStartTime ); 
				BufPrintf( buf, nIndentLevel, "param \"%s\"", pszSoundScriptName );
				BufPrintf( buf, nIndentLevel, "fixedlength\n" );
				BufPrintf( buf, nIndentLevel, "cctpe \"cc_master\"\n" );
				BufPrintf( buf, nIndentLevel, "cctoken \"\"\n" );
			}
			BufEndBlock( buf, nIndentLevel );
		}
	}
	BufEndBlock( buf, nIndentLevel );

	CUtlString strBuf = soundScriptBuffer.String();
	FOR_EACH_SUBKEY( GetCustomKeyValues(), pSubKey )
	{
		strBuf = strBuf.Replace( pSubKey->GetName(), pSubKey->GetString() );
	}
	soundScriptBuffer.Clear();
	soundScriptBuffer.PutString( strBuf.String() );

	// write to sound script file

	CUtlString sWorkingDir;
	CItemUpload::GetVProjectDir( sWorkingDir );
	
	char szSoundScriptFullPath[MAX_PATH];
	V_MakeAbsolutePath( szSoundScriptFullPath, sizeof( szSoundScriptFullPath ), m_strSoundScriptFile.String(), sWorkingDir.String() );
	if ( !g_pFullFileSystem->WriteFile( szSoundScriptFullPath, NULL, soundScriptBuffer ) )
	{
		Warning( "Failed to output soundscript: %s\n", szSoundScriptFullPath );
	}

	if ( GetCustomModPath() )
	{
		char szCustomPath[MAX_PATH];
		V_MakeAbsolutePath( szCustomPath, sizeof( szCustomPath ), CFmtStr( "%s/%s", GetCustomModPath(), m_strSoundScriptFile.String() ), sWorkingDir.String() );
		V_FixSlashes( szCustomPath );
		if ( DoFileCopy( szSoundScriptFullPath, szCustomPath ) )
		{
			Asset()->AddModOutput( szCustomPath );
		}
	}
}


// pChannelsClip is assumed to be an exported channelsclip (vs a live channelsclip)
bool CTargetDMX::WriteVCD( CUtlBuffer &vcdBuf )
{
	CDmeAnimationList *pDmeAnimationList = m_pDmRoot->GetValueElement< CDmeAnimationList >( "animationList" );
	CDmeChannelsClip *pDmeChannelsClip = NULL;
 
	if ( pDmeAnimationList )
	{
		if ( pDmeAnimationList->GetAnimationCount() < 0 )
		{
			Warning( "No DmeChannelsClips found in root.animationList of DMX file: \"%s\"\n", m_sInputFile.String() );
			return false;
		}
 
		pDmeChannelsClip = pDmeAnimationList->GetAnimation( 0 );
		if ( !pDmeChannelsClip )
		{
			Warning( "No DmeChannelsClip found in root.animationList[0] of DMX file: \"%s\"\n", m_sInputFile.String() );
			return false;
		}
	}

	DmeFramerate_t framerate( pDmeChannelsClip->GetValue< int >( "frameRate", 30 ) );
	
	int nLevel = 0;
	BufPrintf( vcdBuf, nLevel, "// Choreo version 1\n" );

	// loop animation?
	if ( m_flAnimationLoopStartTime >= 0.f )
	{
		BufPrintf( vcdBuf, nLevel, "event loop \"<ITEMTEST_SEQUENCE_NAME>_loop\"\n" );
		BufBeginBlock( vcdBuf, nLevel );
		{
			BufPrintf( vcdBuf, nLevel, "time <MAX_SEQUENCE_LENGTH> -1.000000\n" );
			BufPrintf( vcdBuf, nLevel, "param \"%f\"\n", m_flAnimationLoopStartTime );
			BufPrintf( vcdBuf, nLevel, "loopcount \"-1\"\n" );
		}
		BufEndBlock( vcdBuf, nLevel );
	}

	BufPrintf( vcdBuf, nLevel, "actor \"<CLASS_NAME>\"\n" );
	BufBeginBlock( vcdBuf, nLevel );
	{

		// sequence channel
		BufPrintf( vcdBuf, nLevel, "channel \"<CLASS_NAME>\"\n" );
		BufBeginBlock( vcdBuf, nLevel );
		{
			BufPrintf( vcdBuf, nLevel, "event sequence <ITEMTEST_SEQUENCE_NAME>\n" );
			BufBeginBlock( vcdBuf, nLevel );
			{
				BufPrintf( vcdBuf, nLevel, "time 0.000000 <MAX_SEQUENCE_LENGTH>\n" );
				BufPrintf( vcdBuf, nLevel, "param <ITEMTEST_SEQUENCE_NAME>\n" );
			}
			BufEndBlock( vcdBuf, nLevel );
		}
		BufEndBlock( vcdBuf, nLevel );

		// Flex Anim channel
		BufPrintf( vcdBuf, nLevel, "channel \"Face\"\n" );
		BufBeginBlock( vcdBuf, nLevel );
		{
			int nChannelCount = pDmeChannelsClip->m_Channels.Count();
			for ( int ci = 0; ci < nChannelCount; ++ci )
			{
				CDmeChannel *pChannel = pDmeChannelsClip->m_Channels[ ci ];
				// skip all transform
				if ( !pChannel || !pChannel->GetToAttribute() || pChannel->GetToAttribute()->GetType() != AT_FLOAT )
					continue;

				CDmElement *pToElement = pChannel->GetToElement();
				const char *pszToElementName = pToElement->GetName();

				// figure out IsStereoControl by checking if prefix is left_ and right_
				// in pDmeChannelsClip->m_Channels
				CDmeChannel *pLeftChannel  = NULL; //pChannel->GetValueElement< CDmeChannel >( "leftvaluechannel" );
				CDmeChannel *pRightChannel = NULL; //pChannel->GetValueElement< CDmeChannel >( "rightvaluechannel" );
				for ( int iSearch = 0; iSearch < nChannelCount; ++iSearch )
				{
					CDmeChannel *pSearchChannel = pDmeChannelsClip->m_Channels[ iSearch ];
					if ( !pSearchChannel )
						continue;

					// search for left and right channels
					const char *pszSearchChannelName = pSearchChannel->GetName();
					if ( V_strstr( pszSearchChannelName, pszToElementName ) )
					{
						if ( V_strstr( pszSearchChannelName, "left_" ) )
						{
							Assert( pLeftChannel == NULL );
							pLeftChannel = pSearchChannel;
						}
						else if ( V_strstr( pszSearchChannelName, "right_" ) )
						{
							Assert( pRightChannel == NULL );
							pRightChannel = pSearchChannel;
						}
					}
				}

				bool bIsStereo = pLeftChannel && pRightChannel;
				if ( bIsStereo )
				{
					// should we do anything for stereo (eye animation)?
					AssertMsg( 0, "Stereo channel is not supported." );
					continue;

					CDmeFloatLogLayer *pLeftLogLayer  = GetTopmostLogLayer< float >( pLeftChannel );
					CDmeFloatLogLayer *pRightLogLayer = GetTopmostLogLayer< float >( pRightChannel );
					if ( !pLeftLogLayer || !pRightLogLayer )
						continue;

					CUtlVectorFixedGrowable< DmeTime_t, 1024 > times;
					CUtlVectorFixedGrowable< float, 1024 > values;
					CUtlVectorFixedGrowable< float, 1024 > balances;

					// we should have some key count on both left and right key
					if ( pLeftLogLayer->GetKeyCount() == 0 || pRightLogLayer->GetKeyCount() == 0 )
					{
						// something is wrong here.
						Assert( 0 );
						continue;
					}

					DmeTime_t tStartOnFrame = MIN( pLeftLogLayer ->GetKeyTime( 0 ), pRightLogLayer->GetKeyTime( 0 ) );
					DmeTime_t tEndOnFrame = MAX( pLeftLogLayer ->GetKeyTime( pLeftLogLayer->GetKeyCount() - 1 ), pRightLogLayer->GetKeyTime( pRightLogLayer->GetKeyCount() - 1 ) );
#if 1 // resample time
					for ( DmeTime_t tCurr = tStartOnFrame; tCurr <= tEndOnFrame; tCurr = tCurr.TimeAtNextFrame( framerate ) )
					{
						float flLeftValue  = pLeftLogLayer->GetValue( pDmeChannelsClip->FromChildMediaTime( tCurr ) );
						float flRightValue = pLeftLogLayer->GetValue( pDmeChannelsClip->FromChildMediaTime( tCurr ) );

						float flValue, flBalance;
						LeftRightToValueBalance( flLeftValue, flRightValue, flValue, flBalance );

						times.AddToTail( tCurr );
						values.AddToTail( flValue );
						balances.AddToTail( flBalance );
					}
#else // merge time list from left and right channels
					int nLeft = pLeftLogLayer->GetKeyCount();
					int nRight = pRightLogLayer->GetKeyCount();
					int iLeft = pLeftLogLayer->FindKey( pDmeChannelsClip->ToChildMediaTime( tStart ) );
					int iRight = pLeftLogLayer->FindKey( pDmeChannelsClip->ToChildMediaTime( tStart ) );
					while ( iLeft < nLeft || iRight < nRight )
					{
						DmeTime_t tLeft  = iLeft  < nLeft  ? pDmeChannelsClip->FromChildMediaTime( pLeftLogLayer ->GetKeyTime( iLeft  ) ) : DMETIME_MAXTIME;
						DmeTime_t tRight = iRight < nRight ? pDmeChannelsClip->FromChildMediaTime( pRightLogLayer->GetKeyTime( iRight ) ) : DMETIME_MAXTIME;

						DmeTime_t t;
						float flLeftVal, flRightVal;
						if ( tLeft == tRight )
						{
							t = tLeft;
							flLeftVal = pLeftLogLayer->GetKeyValue( iLeft );
							flRightVal = pRightLogLayer->GetKeyValue( iRight );
							iLeft++;
							iRight++;
						}
						else if ( tLeft < tRight )
						{
							t = tLeft;
							flLeftVal = pLeftLogLayer->GetKeyValue( iLeft );
							flRightVal = pRightLogLayer->GetValue( t );
							iLeft++;
						}
						else
						{
							t = tRight;
							flLeftVal = pLeftLogLayer->GetValue( t );
							flRightVal = pRightLogLayer->GetKeyValue( iRight );
							iRight++;
						}

						float flValue, flBalance;
						LeftRightToValueBalance( flLeftVal, flRightVal, flValue, flBalance );

						times.AddToTail( t );
						values.AddToTail( flValue );
						balances.AddToTail( flBalance );
					}
#endif
					BufPrintf( vcdBuf, nLevel, "\"%s\" combo\n", pChannel->GetName() );
					BufBeginBlock( vcdBuf, nLevel );

					int nSamples = times.Count();
					for ( int si = 0; si < nSamples; ++si )
					{
						BufPrintf( vcdBuf, nLevel, "%.4f %.4f\n", times[ si ].GetSeconds(), values[ si ] );
					}

					BufEndBlock( vcdBuf, nLevel );

					BufBeginBlock( vcdBuf, nLevel );

					for ( int si = 0; si < nSamples; ++si )
					{
						BufPrintf( vcdBuf, nLevel, "%.4f %.4f\n", times[ si ].GetSeconds(), balances[ si ] );
					}

					BufEndBlock( vcdBuf, nLevel );
				}
				else
				{
					CDmeFloatLogLayer *pLogLayer = GetTopmostLogLayer< float >( pChannel );
					if ( !pLogLayer || pLogLayer->GetKeyCount() == 0 )
						continue;

					BufPrintf( vcdBuf, nLevel, "event expression \"%s\"\n", pToElement->GetName() );
					BufBeginBlock( vcdBuf, nLevel );
					{
						BufPrintf( vcdBuf, nLevel, "time %.4f %.4f\n", pDmeChannelsClip->GetStartTime().GetSeconds(), pDmeChannelsClip->GetEndTime().GetSeconds() );
						BufPrintf( vcdBuf, nLevel, "param \"player\\<CLASS_NAME>\\emotion\\emotion\"\n" );
						BufPrintf( vcdBuf, nLevel, "param2 \"%s\"\n", pToElement->GetName() );
						BufPrintf( vcdBuf, nLevel, "event_ramp\n" );
						BufBeginBlock( vcdBuf, nLevel );
						{
#if 0 // resampling
							if ( tStart < tStartOnFrame )
							{
								float flValue = pLogLayer->GetValue( pDmeChannelsClip->FromChildMediaTime( tStart ) );
								BufPrintf( vcdBuf, nLevel, "%.4f %.4f\n", tStart.GetSeconds(), flValue );
							}

							for ( DmeTime_t tCurr = tStartOnFrame; tCurr <= tEndOnFrame; tCurr = tCurr.TimeAtNextFrame( framerate ) )
							{
								float flValue = pLogLayer->GetValue( pDmeChannelsClip->FromChildMediaTime( tCurr ) );
								BufPrintf( vcdBuf, nLevel, "%.4f %.4f\n", tCurr.GetSeconds(), flValue );
							}

							if ( tEnd > tEndOnFrame )
							{
								float flValue = pLogLayer->GetValue( pDmeChannelsClip->FromChildMediaTime( tEnd ) );
								BufPrintf( vcdBuf, nLevel, "%.4f %.4f\n", tEnd.GetSeconds(), flValue );
							}
#else // use sample time that's already in the animation

							int nKeys = pLogLayer->GetKeyCount();
							for ( int k = 0; k < nKeys; ++k )
							{
								DmeTime_t t = pDmeChannelsClip->FromChildMediaTime( pLogLayer->GetKeyTime( k ) );
								float flValue = pLogLayer->GetKeyValue( k );
								BufPrintf( vcdBuf, nLevel, "%.4f %.4f\n", t.GetSeconds(), flValue );
							}
#endif
						}
						BufEndBlock( vcdBuf, nLevel );
					}
					BufEndBlock( vcdBuf, nLevel );
				}
			}

			// channel sounds
			OutputSounds( vcdBuf, nLevel, m_pDmRoot->GetValueElement< CDmElement >( "exportedSounds" ) );
		}
		BufEndBlock( vcdBuf, nLevel );
	}
	BufEndBlock( vcdBuf, nLevel );
	BufPrintf( vcdBuf, nLevel, "fps %d\n", RoundFloatToInt( framerate.GetFramesPerSecond() ) );
	BufPrintf( vcdBuf, nLevel, "snap off\n" );

	CUtlString strBuf = vcdBuf.String();
	FOR_EACH_SUBKEY( GetCustomKeyValues(), pSubKey )
	{
		strBuf = strBuf.Replace( pSubKey->GetName(), pSubKey->GetString() );
	}

	vcdBuf.Clear();
	vcdBuf.PutString( strBuf );

	return true;
}


//=============================================================================
//
//=============================================================================
CTargetVCD::CTargetVCD( CAsset *pAsset, const CTargetQC *pTargetQC )
	: CTargetBase( pAsset, pTargetQC ), m_pTargetQC( pTargetQC )
{
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetVCD::Compile()
{
	if ( !CTargetBase::Compile() )
		return false;

	CUtlString sName;
	if ( !GetOutputPath( sName, 0, PATH_FLAG_FILE | PATH_FLAG_EXTENSION ) )
		return false;

	CUtlString sTmp;
	if ( !IsOk( sTmp ) )
	{
		Warning( "CTarget%s::Compile( %s ) - Not Valid: %s\n", GetTypeString(), sName.String(), sTmp.String() );
		return false;
	}

	CUtlString sBinDir;
	if ( !CItemUpload::GetBinDirectory( sBinDir ) )
	{
		Warning( "CTarget%s::Compile( %s ) - GetBinDirectory Failed\n", GetTypeString(), sName.String() );
		return false;
	}

	CUtlString sAbsPath;
	GetOutputPath( sAbsPath, 0 );

	CUtlString sRelPath;
	GetOutputPath( sRelPath, 0, PATH_FLAG_ALL & ~PATH_FLAG_ABSOLUTE );

	if ( sAbsPath.IsEmpty() || sRelPath.IsEmpty() )
	{
		Warning( "CTarget%s::Compile( %s ) - GetOutputPath failed\n", GetTypeString(), sName.String() );
		return false;
	}

	Msg( "Compiling %s: %s\n", GetTypeString(), sRelPath.String() );

	CUtlBuffer bufVCDFile( 0, 0, CUtlBuffer::TEXT_BUFFER );
	if ( m_strVCDPath.IsEmpty() )
	{
		if ( !m_pTargetQC->GetTargetDMX( 0 )->WriteVCD( bufVCDFile ) )
		{
			Warning( "CTarget%s::Compile( %s ) - failed to create VCD buffer\n", GetTypeString(), m_strVCDPath.String() );
			return false;
		}
	}
	else if ( !g_pFullFileSystem->ReadFile( m_strVCDPath, NULL, bufVCDFile ) )
	{
		Warning( "CTarget%s::Compile( %s ) - failed to read input file\n", GetTypeString(), m_strVCDPath.String() );
		return false;
	}

	// output to the correct file path
	AddOrEditP4File( sAbsPath.String() );
	g_pFullFileSystem->WriteFile( sAbsPath.String(), "MOD", bufVCDFile );

	Msg( "CTarget%s::Compile OK! - %s\n", GetTypeString(), sRelPath );

	if ( GetCustomModPath() )
	{
		CUtlString sWorkingDir;
		if ( CItemUpload::IgnoreEnvironmentVariables() )
		{
			sWorkingDir = sAbsPath;
			sWorkingDir.SetLength( sWorkingDir.Length() - sRelPath.Length() );
			V_StripTrailingSlash( sWorkingDir.GetForModify() );
		}
		else
		{
			CItemUpload::GetVProjectDir( sWorkingDir );
		}

		char szCustomPath[MAX_PATH];
		V_MakeAbsolutePath( szCustomPath, sizeof( szCustomPath ), CFmtStr( "%s/%s", GetCustomModPath(), sRelPath.String() ), sWorkingDir.String() );
		V_FixSlashes( szCustomPath );
		if ( DoFileCopy( sAbsPath.String(), szCustomPath ) )
		{
			Asset()->AddModOutput( szCustomPath );
		}
	}

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const ExtensionList *CTargetVCD::GetExtensionsAndCount( void ) const
{
	static ExtensionList vecExtensions;
	if ( !vecExtensions.Count() )
	{
		vecExtensions.AddToTail( ".vcd" );
	}

	return &vecExtensions;
}


//=============================================================================
//
//=============================================================================
CTargetQC::CTargetQC( CAsset *pAsset, const CTargetMDL *pTargetMDL )
	: CTargetBase( pAsset, pTargetMDL )
	, m_QCTemplate( 0, 0, CUtlBuffer::TEXT_BUFFER )
	, m_TargetVCD( NULL )
{
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetQC::IsOk( CUtlString &sMsg ) const
{
	if ( m_TargetDMXs.Count() <= 0 )
	{
		sMsg = "No DMX input files specified";
		return false;
	}

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetQC::Compile()
{
	if ( !CTargetBase::Compile() )
		return false;

	CUtlString sName;
	if ( !GetOutputPath( sName, 0, PATH_FLAG_FILE | PATH_FLAG_EXTENSION ) )
		return false;

	CUtlString sAbsPath;
	GetOutputPath( sAbsPath, 0 );

	CUtlString sRelPath;
	GetOutputPath( sRelPath, 0, PATH_FLAG_ALL & ~PATH_FLAG_ABSOLUTE );

	if ( sAbsPath.IsEmpty() || sRelPath.IsEmpty() )
	{
		Warning( "CTarget%s::Compile( %s ) - GetOutputPath failed\n", GetTypeString(), sName.String() );
		return false;
	}

	CUtlString sLOD0;
	GetOutputPath( sLOD0, 0, PATH_FLAG_PATH | PATH_FLAG_FILE | PATH_FLAG_PREFIX | PATH_FLAG_MODELS );

	sLOD0.FixSlashes( '/' );
	const char *pszPostModel = V_strstr( sLOD0.String(), "models/" );
	CUtlString sModelName = pszPostModel ? pszPostModel + 7 : sLOD0;
	sModelName += ".mdl";

	Msg( "Compiling %s: %s\n", GetTypeString(), sRelPath.String() );

	AddOrEditP4File( sAbsPath.String() );

	CUtlString strBuf = GetQCTemplate();
	if ( strBuf.IsEmpty() )
	{
		return false;
	}

	strBuf = strBuf.Replace( "<ITEMTEST_REPLACE_MDLABSPATH>", sModelName.String() );

	for ( int nLOD = 1; nLOD < m_TargetDMXs.Count(); ++nLOD )
	{
		strBuf = strBuf.Replace( CFmtStr( "<ITEMTEST_REPLACE_LOD%d_HEADER>", nLOD ), CFmtStr( "$lod %d", CItemUpload::Manifest()->GetQCLODDistance( nLOD ) ) );
	}

	// Remove REPLACE_LOD block if it's not necessary
	RemoveTextBlock( strBuf.String(), "<ITEMTEST_REPLACE_LOD1_HEADER>", strBuf.GetForModify(), strBuf.Length() );
	RemoveTextBlock( strBuf.String(), "<ITEMTEST_REPLACE_LOD2_HEADER>", strBuf.GetForModify(), strBuf.Length() );

	CUtlString sSearch = "<ITEMTEST_REPLACE_SKIN_OPTIONALBLOCK>";
	CUtlString sReplace = "$cdmaterials \"<ITEMTEST_REPLACE_MATERIALS_PATH>\"\n$texturegroup skinfamilies\n{\n";
	CUtlString sMaterialPath;
	for ( int nSkin = 0; nSkin < CItemUpload::Manifest()->GetNumMaterialSkins(); ++nSkin )
	{
		int nVMTCount = 0;
		CUtlString sSkinMaterials = "    { ";
		for ( int nVMT = 0; nVMT < Asset()->GetTargetVMTCount(); ++nVMT )
		{
			CTargetVMT *pTargetVMT = Asset()->GetTargetVMT( nVMT );

			if ( nSkin >= pTargetVMT->GetOutputCount() )
				continue;

			// We grab the extension which includes the skin variant, and then trim the actual file extension
			CUtlString sVMTName;
			if ( !pTargetVMT->GetOutputPath( sVMTName, nSkin, PATH_FLAG_FILE | PATH_FLAG_EXTENSION ) )
			{
				Warning( "CTarget%s::Compile( %s ) - VMT %d skin %d GetOutputPath failed\n", GetTypeString(), sName.String(), nVMT, nSkin );
				continue;
			}
			V_StripExtension( sVMTName.String(), sVMTName.GetForModify(), sVMTName.Length() );

			if ( sMaterialPath.IsEmpty() )
			{
				pTargetVMT->GetOutputPath( sMaterialPath, 0, PATH_FLAG_PATH | PATH_FLAG_MODELS );
			}

			sSkinMaterials += CFmtStr( "\"%s\" ", sVMTName.String() );
			++nVMTCount;
		}
		sSkinMaterials += "}\n";

		if ( nVMTCount > 0 	)
		{
			sReplace += sSkinMaterials;
		}
	}
	sReplace += "}\n";
	strBuf = strBuf.Replace( sSearch.String(), sReplace.String() );
	strBuf = strBuf.Replace( "<ITEMTEST_REPLACE_MATERIALS_PATH>", sMaterialPath.String() );

	for ( int nLOD = 0; nLOD < m_TargetDMXs.Count(); ++nLOD )
	{
		CUtlString sLOD;
		m_TargetDMXs[ nLOD ]->GetOutputPath( sLOD, 0, PATH_FLAG_FILE | PATH_FLAG_EXTENSION );

		strBuf = strBuf.Replace( CFmtStr( "<ITEMTEST_REPLACE_LOD%d>", nLOD ), sLOD.String() );
	}

	// Should we include QCI?
	if ( !m_strQCITemplate.IsEmpty() )
	{
		CUtlString strQCIDir;
		m_TargetDMXs[ 0 ]->GetOutputPath( strQCIDir, 1, PATH_FLAG_PATH & ~PATH_FLAG_ABSOLUTE );
		strQCIDir = strQCIDir.Replace( '\\', '/' );
		strBuf = strBuf.Replace( "<QCI_RELATIVE_DIR>", strQCIDir.String() );

		CUtlString strQCIRel;
		m_TargetDMXs[ 0 ]->GetOutputPath( strQCIRel, 1, PATH_FLAG_ALL & ~PATH_FLAG_ABSOLUTE & ~PATH_FLAG_MODELS );
		strQCIRel = strQCIRel.Replace( '\\', '/' );
		strBuf = strBuf.Replace( "<QCI_RELATIVE_PATH>", strQCIRel.String() );
	}

	FOR_EACH_SUBKEY( GetCustomKeyValues(), pSubKey )
	{
		strBuf = strBuf.Replace( pSubKey->GetName(), pSubKey->GetString() );
	}

	CUtlBuffer bufQCFile( 0, 0, CUtlBuffer::TEXT_BUFFER );
	bufQCFile.PutString( strBuf.String() );

	g_pFullFileSystem->WriteFile( sAbsPath.String(), "MOD", bufQCFile );

	if ( !CheckFile( sAbsPath.String() ) )
	{
		Warning( "CTarget%s::Compile( %s ) - File Check Failed - \"%s\"\n", GetTypeString(), sName.String(), sAbsPath.String() );
		return false;
	}

	if ( CItemUpload::Manifest()->UseTerseMessages() )
	{
		Msg( " - Compilation successful.\n" );
	}
	else
	{
		Msg( "CTarget%s::Compile OK! - %s\n", GetTypeString(), sRelPath.String() );
	}

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const ExtensionList *CTargetQC::GetExtensionsAndCount( void ) const
{
	static ExtensionList vecExtensions;
	if ( !vecExtensions.Count() )
	{
		vecExtensions.AddToTail( ".qc" );
	}

	return &vecExtensions;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetQC::GetInputs( CUtlVector< CTargetBase * > &sInputs ) const
{
	CUtlString sTmp;
	if ( !IsOk( sTmp ) )
	{
		Warning( FUNCTION_LINE_STRING "Error! CTarget%s - Not Valid: %s\n", GetTypeString(), sTmp.String() );
		return false;
	}

	int nOkCount = 0;

	for ( int i = 0; i < m_TargetDMXs.Count(); ++i )
	{
		CTargetBase *pTargetBase = m_TargetDMXs.Element( i ).GetObject();
		if ( !pTargetBase )
			continue;

		nOkCount += ( sInputs.AddToTail( pTargetBase ) >= 0 ? 1 : 0 );
	}

	if ( m_TargetVCD.IsValid() )
	{
		CTargetBase *pTargetBase = m_TargetVCD.GetObject();
		if ( pTargetBase )
		{
			nOkCount += ( sInputs.AddToTail( pTargetBase ) >= 0 ? 1 : 0 );
		}
	}

	return nOkCount > 0;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CTargetQC::TargetDMXCount() const
{
	return m_TargetDMXs.Count();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CTargetQC::SetQCTemplate( const char *pszQCTemplate )
{
	m_QCTemplate.Clear();
	m_QCTemplate.PutString( pszQCTemplate );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const char *CTargetQC::GetQCTemplate()
{
	if ( m_QCTemplate.TellMaxPut() == 0 )
	{
		const char *pszQCTemplate = CItemUpload::Manifest()->GetQCTemplate();
		if ( pszQCTemplate )
		{
			if ( !g_pFullFileSystem->ReadFile( pszQCTemplate, "MOD", m_QCTemplate ) )
			{
				Warning( "Failed to load specified QC template '%s'.\n", pszQCTemplate );
			}
		}
	}
	return (char*)m_QCTemplate.Base();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CTargetQC::AddTargetDMX( const char *pszGeometryFile )
{
	CSmartPtr< CTargetDMX > pTargetDMX = Asset()->NewTarget< CTargetDMX >( this );
	if ( !pTargetDMX )
		return -1;

	const int nIndex = m_TargetDMXs.AddToTail( pTargetDMX );

	if ( nIndex < 0 )
	{
		// Failed to add
		return -1;
	}

	if ( !SetTargetDMX( nIndex, pszGeometryFile ) )
	{
		m_TargetDMXs.RemoveMultipleFromTail( 1 );
		return -1;
	}

	return nIndex;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetQC::SetTargetDMX( int nLOD, const char *pszGeometryFile )
{
	if ( nLOD >= TargetDMXCount() )
		return false;

	CSmartPtr< CTargetDMX > pTargetDMX = m_TargetDMXs.Element( nLOD );
	if ( !pTargetDMX )
		return false;

	pTargetDMX->SetNameSuffix( GetNameSuffix() );
	pTargetDMX->SetLod( nLOD );
	pTargetDMX->SetQCITemplate( m_strQCITemplate.String() );

	return pTargetDMX->SetInputFile( pszGeometryFile );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetQC::RemoveTargetDMX( int nLOD )
{
	if ( nLOD != m_TargetDMXs.Count() - 1 )
		return false;

	m_TargetDMXs.RemoveMultipleFromTail( 1 );

	return false;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CSmartPtr< CTargetDMX > CTargetQC::GetTargetDMX( int nLOD ) const
{
	if ( nLOD >= TargetDMXCount() )
		return NULL;

	return m_TargetDMXs.Element( nLOD );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CSmartPtr< CTargetVCD > CTargetQC::GetTargetVCD()
{
	if ( m_TargetVCD == NULL )
	{
		m_TargetVCD = Asset()->NewTarget< CTargetVCD >( this );
	}

	return m_TargetVCD;
}


//=============================================================================
//
//=============================================================================
CTargetMDL::CTargetMDL( CAsset *pAsset, const CTargetBase *pTargetParent )
	: CTargetBase( pAsset, pTargetParent )
	, m_pTargetQC( NULL )
{
	m_pTargetQC = Asset()->NewTarget< CTargetQC >( this );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetMDL::IsOk( CUtlString &sMsg ) const
{
	CUtlString sTmp;
	if ( !m_pTargetQC || !m_pTargetQC->IsOk( sTmp ) )
	{
		sMsg = "Invalid QC: ";
		sMsg += sTmp;
		return false;
	}

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetMDL::Compile()
{
	if ( !CTargetBase::Compile() )
		return false;

	CUtlString sName;
	if ( !GetOutputPath( sName, 0, PATH_FLAG_FILE | PATH_FLAG_EXTENSION ) )
		return false;

	CUtlString sBinDir;
	if ( !CItemUpload::GetBinDirectory( sBinDir ) )
	{
		Warning( "CTarget%s::Compile( %s ) - GetBinDirectory Failed\n", GetTypeString(), sName.String() );
		return false;
	}

	CUtlVector< CUtlString > sAbsPaths;
	GetOutputPaths( sAbsPaths );
	if ( sAbsPaths.Count() <= 0 )
	{
		Warning( "CTarget%s::Compile( %s ) - GetOutputPaths failed\n", GetTypeString(), sName.String() );
		return false;
	}

	CUtlString sAbsPath = sAbsPaths.Element( 0 );

	CUtlString sRelPath;
	GetOutputPath( sRelPath, 0, PATH_FLAG_ALL & ~PATH_FLAG_ABSOLUTE );

	if ( sAbsPath.IsEmpty() || sRelPath.IsEmpty() )
	{
		Warning( "CTarget%s::Compile( %s ) - GetOutputPath failed\n", GetTypeString(), sName.String() );
		return false;
	}

	CUtlVector< CUtlString > sAbsInputPaths;
	if ( !GetInputPaths( sAbsInputPaths, false, false ) )
	{
		Warning( "CTarget%s::Compile( %s ) - GetInputPaths failed\n", GetTypeString(), sName.String() );
		return false;
	}

	if ( sAbsInputPaths.Count() != 1 )
	{
		Warning( "CTarget%s::Compile( %s ) - GetPaths returned %d paths, expected 1\n", GetTypeString(), sName.String(), sAbsInputPaths.Count() );
		return false;
	}

	if ( !Asset()->Mkdir( NULL, this ) )
	{
		Warning( "CTarget%s::Compile - Mkdir failed\n", GetTypeString() );
		return false;
	}

	Msg( "Compiling %s: %s\n", GetTypeString(), sRelPath.String() );

	if ( CItemUpload::GetP4() )
	{
		for ( int i = 0; i < sAbsPaths.Count(); ++i )
		{
			AddOrEditP4File( sAbsPaths.Element( i ).String() );
		}
	}

	CUtlString sWorkingDir;
	CFmtStrN< k64KB > sCmd;
	if ( CItemUpload::IgnoreEnvironmentVariables() )
	{
		sWorkingDir = sAbsPath;
		sWorkingDir.SetLength( sWorkingDir.Length() - sRelPath.Length() );
		V_StripTrailingSlash( sWorkingDir.GetForModify() );

		sCmd.sprintf( "\"%s\\studiomdl.exe\" -nop4 -game \"%s\" \"%s\"", sBinDir.String(), sWorkingDir.String(), sAbsInputPaths.Element( 0 ).String() );
	}
	else
	{
		CItemUpload::GetVProjectDir( sWorkingDir );
		sCmd.sprintf( "\"%s\\studiomdl.exe\" -nop4 -vproject \"%s\" \"%s\"", sBinDir.String(), sWorkingDir.String(), sAbsInputPaths.Element( 0 ).String() );
	}

	bool bOk = true;

	if ( CItemUpload::RunCommandLine( sCmd.Access(), sBinDir.String(), this ) )
	{
		for ( int i = 0; i < sAbsPaths.Count(); ++i )
		{
			if ( !CheckFile( sAbsPaths.Element( i ).String() ) )
			{
				Warning( "CTarget%s::Compile( %s ) - CheckFile failed - \"%s\"\n", GetTypeString(), sName.String(), sAbsPaths.Element( i ).String() );
				bOk = false;
				break;
			}
		}
	}
	else
	{
		bOk = false;
	}

	if ( bOk )
	{
		if ( CItemUpload::Manifest()->UseTerseMessages() )
		{
			Msg( " - Compilation successful.\n" );
		}
		else
		{
			Msg( "CTarget%s::Compile OK! - %s\n", GetTypeString(), sRelPath.String() );
		}
	}
	else
	{
		Warning( "CTarget%s::Compile Failed - %s\n", GetTypeString(), sAbsPath.String() );
		return false;
	}

	if ( GetCustomModPath() )
	{
		char szCustomPath[MAX_PATH];
		V_MakeAbsolutePath( szCustomPath, sizeof( szCustomPath ), CFmtStr( "%s/%s", GetCustomModPath(), sRelPath.String() ), sWorkingDir.String() );
		V_FixSlashes( szCustomPath );
		DoFileCopy( sAbsPath.String(), szCustomPath );
		Asset()->AddModOutput( szCustomPath );
	}

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const ExtensionList *CTargetMDL::GetExtensionsAndCount( void ) const
{
	// Only output .mdl for animation
	CSmartPtr< CTargetQC > pTargetQC = GetTargetQC();
	if ( pTargetQC.IsValid() && pTargetQC->GetQCITemplate() )
	{
		return CItemUpload::Manifest()->GetAnimationMDLExtentions();
	}

	return CItemUpload::Manifest()->GetMDLExtensions();
	
/*
	static const char *s_szExtensionsDX8[] = {
		".mdl",
		".dx80.vtx",
		".dx90.vtx",
		".sw.vtx",
		".phy",
		".vvd"
	};

	static const char *s_szExtensions[] = {
		".mdl",
		".dx90.vtx",
		".phy",
		".vvd"
	};

	static const char **s_ppszExtensions = s_szExtensionsDX8;
	static bool s_bGameInfoParsed = false;

	if ( !s_bGameInfoParsed )
	{
		KeyValues *pKeyValues = new KeyValues( "gameinfo.txt" );
		if ( pKeyValues != NULL )
		{
			if ( g_pFullFileSystem && pKeyValues->LoadFromFile( g_pFullFileSystem, "gameinfo.txt" ) )
			{
				if ( pKeyValues->GetInt( "SupportsDX8" ) != 0 )
				{
					s_ppszExtensions = s_szExtensionsDX8;
				}
				else
				{
					s_ppszExtensions = s_szExtensions;
				}

				s_bGameInfoParsed = true;
			}
			pKeyValues->deleteThis();
		}
	}

	if ( s_ppszExtensions == s_szExtensions  )
	{
		nExtensionCount = ARRAYSIZE( s_szExtensions );
	}
	else
	{
		nExtensionCount = ARRAYSIZE( s_szExtensionsDX8 );
	}

	return s_ppszExtensions;
*/
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CTargetMDL::GetInputs( CUtlVector< CTargetBase * > &inputs ) const
{
	CUtlString sTmp;
	if ( !IsOk( sTmp ) )
	{
		Warning( FUNCTION_LINE_STRING "Error! CTarget%s - Not Valid: %s\n", GetTypeString(), sTmp.String() );
		return false;
	}

	inputs.RemoveAll();

	inputs.AddToTail( m_pTargetQC.GetObject() );

	return true;
}


//=============================================================================
//
//=============================================================================
CAsset::CAsset()
: CTargetBase( NULL, NULL )
, m_bSkinToBipHead( false )
, m_nCurrentModel( -1 )
, m_vmtMap( UtlStringLessThan )
, m_bShouldBuildScenesImage( false )
{
	m_pAsset = this;

	CItemUpload::GetSteamId( m_sSteamId );

	AddModel();

	m_vecTargetIcons.SetCount( CItemUpload::Manifest()->GetNumIconTypes() );

	m_sExcludeFileExtensions.AddToTail( "zip" );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CAsset::CAsset( const char *pszName, bool *pbOk /* = NULL */ )
: CTargetBase( NULL, NULL )
, m_nCurrentModel( -1 )
, m_bShouldBuildScenesImage( false )
{
	m_pAsset = this;

	CItemUpload::GetSteamId( m_sSteamId );

	m_sName = pszName;

	AddModel();

	m_vecTargetIcons.SetCount( CItemUpload::Manifest()->GetNumIconTypes() );

	if ( pbOk )
	{
		CUtlString sTmp;
		*pbOk = IsOk( sTmp );

		if ( !( *pbOk ) )
		{
			Warning( FUNCTION_LINE_STRING "Error! CTarget%s - Not Valid: %s\n", GetTypeString(), sTmp.String() );
		}
	}

	m_sExcludeFileExtensions.AddToTail( "zip" );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CAsset::~CAsset()
{
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CAsset::IsOk( CUtlString &sMsg ) const
{
	if ( !IsNameValid() )
	{
		sMsg = "Invalid asset name";
		return false;
	}

	if ( !IsSteamIdValid() )
	{
		sMsg = "Invalid steam id";
		return false;
	}

	if ( !GetTargetMDL().IsValid() )
	{
		sMsg = "No target MDL";
		return false;
	}

	CUtlString sTmp;
	if ( !CItemUpload::GetContentDir( sTmp ) || sTmp.IsEmpty() )
	{
		sMsg = "Cannot figure out content directory, have you installed the Source SDK and restarted Steam?";
		return false;
	}

	return true;
}


bool CAsset::BuildScenesImage()
{
	CUtlString sBinDir;
	if ( !CItemUpload::GetBinDirectory( sBinDir ) )
	{
		Warning( "CTarget%s::Compile( %s ) - GetBinDirectory Failed\n", GetTypeString(), GetAssetName() );
		return false;
	}

	CUtlString sAbsPath;
	GetOutputPath( sAbsPath );

	CUtlString sRelPath;
	GetOutputPath( sRelPath, 0, PATH_FLAG_ALL & ~PATH_FLAG_ABSOLUTE );

	CUtlString sWorkingDir;
	if ( CItemUpload::IgnoreEnvironmentVariables() )
	{
		sWorkingDir = sAbsPath;
		sWorkingDir.SetLength( sWorkingDir.Length() - sRelPath.Length() );
		V_StripTrailingSlash( sWorkingDir.GetForModify() );
	}
	else
	{
		CItemUpload::GetVProjectDir( sWorkingDir );
	}

	char szScenesImage[MAX_PATH];
	V_MakeAbsolutePath( szScenesImage, sizeof( szScenesImage ), "scenes\\scenes.image", sWorkingDir.String() );
	AddOrEditP4File( szScenesImage );

	CFmtStrN< k64KB > sCmd;
	sCmd.sprintf( "%s\\makescenesimage.exe", sBinDir.String() );

	if ( !CItemUpload::RunCommandLine( sCmd.Access(), sWorkingDir.String(), this ) )
	{
		Warning( "Failed to build %s\n", szScenesImage );
		return false;
	}

	Msg( "Build scenes.image OK!\n" );

	if ( GetCustomModPath() )
	{
		char szCustomPath[MAX_PATH];
		V_MakeAbsolutePath( szCustomPath, sizeof( szCustomPath ), CFmtStr( "%s/%s", GetCustomModPath(), "scenes/scenes.image" ), sWorkingDir.String() );
		V_FixSlashes( szCustomPath );
		DoFileCopy( szScenesImage, szCustomPath );
		Asset()->AddModOutput( szCustomPath );
	}

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CAsset::CompilePreview()
{
	bool bOldP4State = CItemUpload::GetP4();
	CItemUpload::SetP4( false );
	m_CompileOutputFiles.RemoveAll();
	g_bCompilePreview = true;

	// Record the files that are created so they can be removed after preview
	m_sAbsPaths.RemoveAll();
	m_sRelPaths.RemoveAll();
	m_sModOutputs.RemoveAll();
	CUtlVector< CTargetBase * > inputs;
	if ( GetInputs( inputs ) )
	{
		for ( int i = 0; i < inputs.Count(); ++i )
		{
			CTargetBase *pTargetBase = inputs.Element( i );
			if ( !pTargetBase )
				continue;

			pTargetBase->GetOutputPaths( m_sAbsPaths, PATH_FLAG_ALL, true );
			pTargetBase->GetOutputPaths( m_sRelPaths, (PATH_FLAG_ALL & ~PATH_FLAG_ABSOLUTE) | PATH_FLAG_ZIP, true );
		}
	}

	bool bResult = CTargetBase::Compile();
	g_bCompilePreview = false;
	CItemUpload::SetP4( bOldP4State );

	if ( bResult )
	{
		PostCompilePreview();
	}

	return bResult;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CAsset::PostCompilePreview()
{
	Msg( "CAsset::PostCompilePreview Start:\n");

	bool bResult = true;
	if ( m_bShouldBuildScenesImage )
	{
		bResult = BuildScenesImage();
	}

	if ( bResult )
	{
		Msg( "CAsset::PostCompilePreview OK!\n" );
	}
	else
	{
		Msg( "CAsset::PostCompilePreview FAILED!\n" );
	}

	return bResult;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CAsset::Compile()
{
	m_CompileOutputFiles.RemoveAll();

	if ( m_sArchivePath.IsEmpty() )
	{
		GetOutputPath( m_sArchivePath, 0 );
	}

	CUtlString sRelPath;
	GetOutputPath( sRelPath, 0, PATH_FLAG_ALL & ~PATH_FLAG_ABSOLUTE );

	if ( m_sArchivePath.IsEmpty() || sRelPath.IsEmpty() )
	{
		Warning( "CTarget%s::Compile - GetOutputPath failed\n", GetTypeString() );
		return false;
	}

	// this gets filled by Compile()
	m_sModOutputs.RemoveAll();

	if ( !CTargetBase::Compile() )
		return false;

	m_sAbsPaths.RemoveAll();
	GetOutputPaths( m_sAbsPaths, PATH_FLAG_ALL, true );

	m_sRelPaths.RemoveAll();
	GetOutputPaths( m_sRelPaths, (PATH_FLAG_ALL & ~PATH_FLAG_ABSOLUTE) | PATH_FLAG_ZIP, true );

	Assert( m_sAbsPaths.Count() == m_sRelPaths.Count() );

	if ( m_sAbsPaths.Count() <= 0 || m_sAbsPaths.Count() != m_sRelPaths.Count() )
	{
		Warning( "CTarget%s::Compile - GetOutputPaths failed\n", GetTypeString() );
		return false;
	}

	Msg( "Compiling %s: %s\n", GetTypeString(), sRelPath.String() );

	AddOrEditP4File( m_sArchivePath.String() );

	if ( CItemUpload::FileExists( m_sArchivePath.String() ) )
	{
		_unlink( m_sArchivePath.String() );
	}

	HANDLE m_hOutputZipFile = CreateFile( m_sArchivePath.String(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
	if ( m_hOutputZipFile == INVALID_HANDLE_VALUE )
	{
		Warning( "CTarget%s::Compile CreateZip Failed - Unable to create ZIP file %s.\n", GetTypeString(), m_sArchivePath.String() );
		return false;
	}

	IZip *pZip = IZip::CreateZip();
	if ( pZip == NULL )
	{
		Warning( "CTarget%s::Compile CreateZip Failed - %s\n", GetTypeString(), sRelPath.String() );
		CloseHandle( m_hOutputZipFile );
		return false;
	}

	CUtlBuffer kvBuf( 0, 0, CUtlBuffer::TEXT_BUFFER );
	CreateManifest( kvBuf );

	pZip->AddBufferToZip( "manifest.txt", kvBuf.Base(), kvBuf.TellPut(), true );

	for ( int i = 0; i < m_sAbsPaths.Count(); ++i )
	{
		const char *pszFileExtension = V_GetFileExtension( m_sAbsPaths.Element( i ).String() );

		bool bExcluded = false;
		FOR_EACH_VEC( m_sExcludeFileExtensions, e )
		{
			if ( m_sExcludeFileExtensions[e] == pszFileExtension )
			{
				bExcluded = true;
				break;
			}
		}

		if ( bExcluded )
			continue;

		if ( CItemUpload::Manifest()->UseTerseMessages() )
		{
			Msg( " - added %s\n", m_sRelPaths.Element( i ).String() );
		}
		else
		{
			Msg( " + Zip Add: %s\n", m_sRelPaths.Element( i ).String() );
		}

		pZip->AddFileToZip( m_sRelPaths.Element( i ).String(), m_sAbsPaths.Element( i ).String() );
	}

	pZip->SaveToDisk( m_hOutputZipFile );
	IZip::ReleaseZip( pZip );

	CloseHandle( m_hOutputZipFile );

	if ( !CheckFile( m_sArchivePath.String() ) )
	{
		if ( CItemUpload::Manifest()->UseTerseMessages() )
		{
			Warning( "Failed to write .zip file: \"%s\"\n", m_sArchivePath.String() );
		}
		else
		{
			Warning( "CTarget%s::Compile - File Check Failed - \"%s\"\n", GetTypeString(), m_sArchivePath.String() );
		}
		return false;
	}

	if ( CItemUpload::Manifest()->UseTerseMessages() )
	{
		Msg( " - Compilation successful.\n" );
	}
	else
	{
		Msg( "CTarget%s::Compile OK! - %s\n", GetTypeString(), sRelPath.String() );
	}

	return PostCompile();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CAsset::PostCompile()
{
	Msg( "CAsset::PostCompile Start:\n");

	bool bResult = true;
	if ( m_bShouldBuildScenesImage )
	{
		bResult = BuildScenesImage();
	}

	if ( bResult )
	{
		Msg( "CAsset::PostCompile OK!\n" );
	}
	else
	{
		Msg( "CAsset::PostCompile FAILED!\n" );
	}

	return bResult;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CAsset::GetInputs( CUtlVector< CTargetBase * > &inputs ) const
{
	CUtlString sTmp;
	if ( !IsOk( sTmp ) )
	{
		Warning( FUNCTION_LINE_STRING "Error! CTarget%s - Not Valid: %s\n", GetTypeString(), sTmp.String() );
		return false;
	}

	for ( int nIcon = 0; nIcon < m_vecTargetIcons.Count(); ++nIcon )
	{
		if ( m_vecTargetIcons[ nIcon ].IsValid() )
		{
			inputs.AddToTail( m_vecTargetIcons[ nIcon ].GetObject() );
		}
	}

	for ( int i = 0; i < GetTargetVMTCount(); ++i )
	{
		inputs.AddToTail( GetTargetVMT( i ) );
	}

	FOR_EACH_VEC( m_vecModels, nModelIndex )
	{
		if ( m_vecModels[ nModelIndex ].IsValid() )
		{
			inputs.AddToTail( m_vecModels[ nModelIndex ].GetObject() );
		}
	}

	return inputs.Count() > 0;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const ExtensionList *CAsset::GetExtensionsAndCount( void ) const
{
	static ExtensionList vecExtensions;
	if ( !vecExtensions.Count() )
	{
		vecExtensions.AddToTail( ".zip" );
	}

	return &vecExtensions;
}


//-----------------------------------------------------------------------------
// Returns items/<steamid>/<name>, false if there's something wrong
//-----------------------------------------------------------------------------
bool CAsset::GetRelativeDir( CUtlString &sRelativeDir, const char *pszPrefix, const CTargetBase *pTarget ) const
{
	CUtlString sTmp;

	if ( !IsOk( sTmp ) )
	{
		Warning( FUNCTION_LINE_STRING "Error! CTarget%s - Not Valid: %s\n", GetTypeString(), sTmp.String() );
		return false;
	}

	if ( pszPrefix )
	{
		sRelativeDir = pszPrefix;
		sRelativeDir += "/";
	}
	else
	{
		sRelativeDir = "";
	}

	if ( pTarget->IsModelPath() )
		sRelativeDir += "models/";

	if ( pTarget->GetCustomRelativeDir() )
	{
		sRelativeDir += pTarget->GetCustomRelativeDir();
	}
	else
	{
		sRelativeDir += pTarget->GetItemDirectory();

		const char *pszClass = GetClass();
		if ( pszClass )
		{
			sRelativeDir += pszClass;
			sRelativeDir += "/";
		}

		if ( CItemUpload::Manifest()->GetItemPathUsesSteamId() )
		{
			if ( !m_sSteamId.IsEmpty() )
			{
				sRelativeDir += m_sSteamId;
				sRelativeDir += "/";
			}
		}

		sRelativeDir += m_sName;
	}

	sRelativeDir.FixSlashes();

	return true;
}


//-----------------------------------------------------------------------------
// Returns GetAbsoluteContentDir()/GetRelativeDir() or GetAbsoluteGameDir()/GetRelativeDir()
//-----------------------------------------------------------------------------
bool CAsset::GetAbsoluteDir( CUtlString &sAbsoluteDir, const char *pszPrefix /* = NULL */, const CTargetBase *pTarget  ) const
{
	CUtlString sDirA;

	if ( pTarget->IsContent() )
	{
		if ( !CItemUpload::GetContentDir( sDirA ) )
			return false;
	}
	else
	{
		if ( !CItemUpload::GetVProjectDir( sDirA ) )
			return false;
	}

	CUtlString sDirB;

	if ( !GetRelativeDir( sDirB, pszPrefix, pTarget ) )
		return false;

	sAbsoluteDir = sDirA;
	sAbsoluteDir += "/";
	sAbsoluteDir += sDirB;

	sAbsoluteDir.FixSlashes();

	return true;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CAsset::SetName( const char *pszName )
{
	if ( !CItemUpload::SanitizeName( pszName, m_sName ) )
		return false;

	return IsNameValid();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CAsset::IsNameValid() const
{
	return m_sName.Length() > 0;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const char *CAsset::GetSteamId() const
{
	return m_sSteamId.String();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CAsset::IsSteamIdValid() const
{
	return CItemUpload::GetDevMode() || m_sSteamId.Length() > 0;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CAsset::SetTargetIcon( int nIcon, const char *pszIconFile )
{
	if ( !m_vecTargetIcons[ nIcon ].IsValid() )
	{
		m_vecTargetIcons[ nIcon ] = new CTargetIcon( this, nIcon );
	}
	return m_vecTargetIcons[ nIcon ]->SetTargetVTF( pszIconFile );
}


//-----------------------------------------------------------------------------
// Add a new model to the output and make it current
//-----------------------------------------------------------------------------
int CAsset::AddModel()
{
	CSmartPtr< CTargetMDL > pModel = NewTarget< CTargetMDL >( this );
	m_nCurrentModel = m_vecModels.AddToTail( pModel );
	return m_nCurrentModel;
}

//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CAsset::SetCurrentModel( int nModel )
{
	if ( nModel >= 0 && nModel < GetNumModels() )
	{
		m_nCurrentModel = nModel;
		return true;
	}
	return false;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CAsset::RemoveModels()
{
	m_vecModels.RemoveAll();
	m_nCurrentModel = -1;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CAsset::TargetDMXCount() const
{
	CSmartPtr< CTargetQC > pTargetQC = GetTargetQC();
	if ( !pTargetQC )
		return false;

	return pTargetQC->TargetDMXCount();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CAsset::AddTargetDMX( const char *pszGeometryFile )
{
	CSmartPtr< CTargetQC > pTargetQC = GetTargetQC();
	if ( !pTargetQC )
		return -1;

	return pTargetQC->AddTargetDMX( pszGeometryFile );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CAsset::SetTargetDMX( int nLOD, const char *pszGeometryFile )
{
	CSmartPtr< CTargetQC > pTargetQC = GetTargetQC();
	if ( !pTargetQC )
		return false;

	return pTargetQC->SetTargetDMX( nLOD, pszGeometryFile );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CAsset::RemoveTargetDMX( int nLOD )
{
	CSmartPtr< CTargetQC > pTargetQC = GetTargetQC();
	if ( !pTargetQC )
		return false;

	return pTargetQC->RemoveTargetDMX( nLOD );
}


//-----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------
CSmartPtr< CTargetDMX > CAsset::GetTargetDMX( int nLOD )
{
	CSmartPtr< CTargetQC > pTargetQC = GetTargetQC();
	if ( !pTargetQC )
		return false;

	return pTargetQC->GetTargetDMX( nLOD );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CSmartPtr< CTargetMDL > CAsset::GetTargetMDL() const
{
	if ( m_nCurrentModel >= 0 )
	{
		return m_vecModels[ m_nCurrentModel ];
	}
	return CSmartPtr< CTargetMDL >();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CSmartPtr< CTargetQC > CAsset::GetTargetQC() const
{
	CSmartPtr< CTargetMDL > pTargetMDL = GetTargetMDL();
	if ( !pTargetMDL )
		return NULL;

	return pTargetMDL->GetTargetQC();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
int CAsset::GetTargetVMTCount() const
{
	return m_vmtMap.Count();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CTargetVMT *CAsset::GetTargetVMT( int nIndex ) const
{
	if ( nIndex < 0 || nIndex >= GetTargetVMTCount() )
		return NULL;

	int nMapIndex = 0;

	FOR_EACH_MAP( m_vmtMap, nMapIt )
	{
		if ( nIndex == nMapIndex )
			return m_vmtMap.Element( nMapIt );

		++nMapIndex;
	}

	return NULL;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CSmartPtr< CTargetVMT > CAsset::FindOrAddMaterial( const char *pszMaterial, int nMaterialType )
{
	const CUtlString sMaterial( pszMaterial );

	CUtlMap< CUtlString, CTargetVMT * >::IndexType_t nIndex = m_vmtMap.Find( sMaterial );

	if ( !m_vmtMap.IsValidIndex( nIndex ) )
	{
		CSmartPtr< CTargetVMT > pTargetVMT = NewTarget< CTargetVMT >( this );
		if ( !pTargetVMT )
			return NULL;

		pTargetVMT->SetMaterialId( pszMaterial );

		m_vmtMap.Insert( sMaterial, pTargetVMT.GetObject() );

		// See if this is a duplicate of an existing material
		for ( int i = 0; i < GetTargetVMTCount(); ++i )
		{
			CTargetVMT *pTmpTargetVMT = GetTargetVMT( i );
			if ( !pTmpTargetVMT )
				continue;

			if ( pTmpTargetVMT->GetMaterialType() == nMaterialType )
			{
				pTargetVMT->SetDuplicate( nMaterialType );
				break;
			}
		}
		if ( !pTargetVMT->GetDuplicate() )
		{
			pTargetVMT->SetMaterialType( nMaterialType );
		}

		return pTargetVMT;
	}
	else
	{
		return m_vmtMap.Element( nIndex );
	}

	return NULL;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
CSmartPtr< CTargetVMT > CAsset::FindMaterial( const char *pszMaterial )
{
	const CUtlString sMaterial( pszMaterial );

	CUtlMap< CUtlString, CTargetVMT * >::IndexType_t nIndex = m_vmtMap.Find( sMaterial );

	if ( m_vmtMap.IsValidIndex( nIndex ) )
		return m_vmtMap.Element( nIndex );

	return NULL;
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CAsset::Mkdir( const char *pszPrefix, const CTargetBase *pTarget )
{
	CUtlString sAbsolute;

	if ( !GetAbsoluteDir( sAbsolute, pszPrefix, pTarget ) )
		return false;

	char szBuf[ k64KB ];
	if ( _fullpath( szBuf, sAbsolute.String(), ARRAYSIZE( szBuf ) ) == NULL )
		return false;

	return CItemUpload::CreateDirectory( szBuf );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CAsset::CreateManifest( CUtlBuffer &manifestBuf )
{
	KeyValues *pAssetKV = new KeyValues( "asset" );

	UpdateManifest( pAssetKV );

	manifestBuf.Clear();
	manifestBuf.SetBufferType( true, true );
	pAssetKV->RecursiveSaveToFile( manifestBuf, 0 );

	pAssetKV->deleteThis();
}


const char* CAsset::CheckRedundantOutputFilePath( const char* pszInputFilePath, const char* pszVTEXConfig, const char* pszOutputFilePath )
{
	const char* pszLocalVTEXConfig = pszVTEXConfig ? pszVTEXConfig : "";

	// we don't want to output multiple of the same texture file with the same vtex config
	for ( int i=0; i<m_CompileOutputFiles.Count(); ++i )
	{
		const CompileOutputFile_t& tga = m_CompileOutputFiles[i];
		if ( !V_stricmp( tga.m_strInputFilePath.String(), pszInputFilePath ) )
		{
			if ( !V_stricmp( tga.m_strVTEXConfig.String(), pszLocalVTEXConfig ) )
			{
				return tga.m_strOutputFilePath.String();
			}
		}
	}

	int index = m_CompileOutputFiles.AddToTail();
	CompileOutputFile_t& newTGA		= m_CompileOutputFiles[index];
	newTGA.m_strInputFilePath		= pszInputFilePath;
	newTGA.m_strVTEXConfig			= pszLocalVTEXConfig;
	newTGA.m_strOutputFilePath		= pszOutputFilePath;

	return newTGA.m_strOutputFilePath.String();
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CAsset::RemoveMaterial( const char *pszMaterial )
{
	const CUtlString sMaterial( pszMaterial );

	return m_vmtMap.Remove( sMaterial );
}


//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
const CUtlString &CAsset::GetAssetName() const
{
	return m_sName;
}