summaryrefslogtreecommitdiff
path: root/utils/itemtest_lib
diff options
context:
space:
mode:
Diffstat (limited to 'utils/itemtest_lib')
-rw-r--r--utils/itemtest_lib/itemtest.cpp6226
-rw-r--r--utils/itemtest_lib/itemtest_lib.vpc40
-rw-r--r--utils/itemtest_lib/itemtest_lib_support.vpc44
-rw-r--r--utils/itemtest_lib/systemutils.cpp265
4 files changed, 6575 insertions, 0 deletions
diff --git a/utils/itemtest_lib/itemtest.cpp b/utils/itemtest_lib/itemtest.cpp
new file mode 100644
index 0000000..7073627
--- /dev/null
+++ b/utils/itemtest_lib/itemtest.cpp
@@ -0,0 +1,6226 @@
+//========= 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;
+}
diff --git a/utils/itemtest_lib/itemtest_lib.vpc b/utils/itemtest_lib/itemtest_lib.vpc
new file mode 100644
index 0000000..7d378c4
--- /dev/null
+++ b/utils/itemtest_lib/itemtest_lib.vpc
@@ -0,0 +1,40 @@
+//============ Copyright (c) Valve Corporation, All rights reserved. ==========
+//
+// Converts various input bitmap & geometry formats into standard
+// Valve formats, renames and places items in the proper directories
+// and calls vtex/studiomdl and can ZIP archive the results.
+//
+// NOTE: Projects which link this also need to include itemtest_lib_support.vpc
+//
+//=============================================================================
+
+
+$Macro SRCDIR "..\.."
+
+$include "$SRCDIR\vpc_scripts\source_lib_base.vpc"
+
+$Include "$SRCDIR\vpc_scripts\fbx_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $PreprocessorDefinitions "$BASE;VERSION_SAFE_STEAM_API_INTERFACES;ITEMUPLIB_LIB"
+ }
+}
+
+$Project "itemtest_lib"
+{
+ $Folder "Source Files"
+ {
+ $File "itemtest.cpp"
+ $File "systemutils.cpp"
+ $File "$SRCDIR\public\zip_utils.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ $File "$SRCDIR\public\itemtest\itemtest.h"
+ $File "$SRCDIR\public\zip_utils.h"
+ }
+}
diff --git a/utils/itemtest_lib/itemtest_lib_support.vpc b/utils/itemtest_lib/itemtest_lib_support.vpc
new file mode 100644
index 0000000..237c8a8
--- /dev/null
+++ b/utils/itemtest_lib/itemtest_lib_support.vpc
@@ -0,0 +1,44 @@
+//============ Copyright (c) Valve Corporation, All rights reserved. ==========
+//
+// Adds ZIP support to your project which is used by the itemtest_lib project
+//
+//=============================================================================
+
+$Include "$SRCDIR\vpc_scripts\fbx.vpc"
+
+$Project
+{
+ $Folder "ZIP Support"
+ {
+ $File "$SRCDIR\public\XZip.cpp"
+ {
+ $Configuration
+ {
+ $Compiler
+ {
+ $Create/UsePrecompiledHeader "Not Using Precompiled Headers"
+ }
+ }
+ }
+ $File "$SRCDIR\public\XUnzip.cpp"
+ {
+ $Configuration
+ {
+ $Compiler
+ {
+ $Create/UsePrecompiledHeader "Not Using Precompiled Headers"
+ $EnableC++Exceptions "Yes With SEH Exceptions (/EHa)" [!$X360]
+ }
+ }
+ }
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib datamodel
+ $Lib dmserializers
+ $Lib fbxutils
+ $Lib movieobjects
+ $Lib itemtest_lib
+ }
+}
diff --git a/utils/itemtest_lib/systemutils.cpp b/utils/itemtest_lib/systemutils.cpp
new file mode 100644
index 0000000..00b307f
--- /dev/null
+++ b/utils/itemtest_lib/systemutils.cpp
@@ -0,0 +1,265 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Stuff which interacts with system libraries
+//
+//=============================================================================
+
+
+// Windows includes
+#include <windows.h>
+#include <direct.h>
+
+
+// Valve includes
+#include "itemtest/itemtest.h"
+#include "tier1/fmtstr.h"
+
+
+// Last include
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Globals
+//-----------------------------------------------------------------------------
+HANDLE g_hChildStd_OUT_Rd = NULL;
+HANDLE g_hChildStd_OUT_Wr = NULL;
+
+HANDLE g_hInputFile = NULL;
+
+#define BUFSIZE 4096
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CItemUpload::CreateDirectory( const char *pszDirectory )
+{
+ char szBuf[ BUFSIZE ];
+ V_strncpy( szBuf, pszDirectory, 1024 );
+
+ for ( int i = 0; i < V_strlen( szBuf ); ++i )
+ {
+ if ( szBuf[i] == '/' || szBuf[i] == '\\' )
+ {
+ szBuf[i] = '\0';
+ mkdir( szBuf );
+ szBuf[i] = CORRECT_PATH_SEPARATOR;
+ }
+ }
+
+ mkdir( szBuf );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Read output from the child process's pipe for STDOUT
+// and write to the parent process's pipe for STDOUT.
+// Stop when there is no more data.
+//-----------------------------------------------------------------------------
+static void ReadFromPipe( CItemLog *pLog )
+{
+ DWORD dwRead;
+ char chBuf[BUFSIZE + 1];
+ BOOL bSuccess = FALSE;
+
+ // Close the write end of the pipe before reading from the
+ // read end of the pipe, to control child process execution.
+ // The pipe is assumed to have enough buffer space to hold the
+ // data the child process has already written to it.
+
+ if ( !CloseHandle( g_hChildStd_OUT_Wr ) )
+ return;
+
+ // TODO: Prefix each line, print in color?
+
+ for (;;)
+ {
+ // This can hang if the process is waiting for input, for example...
+ bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL );
+
+ if ( !bSuccess || dwRead == 0 )
+ break;
+
+ chBuf[dwRead] = '\0';
+
+ pLog->Warning( chBuf );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CItemUpload::RunCommandLine( const char *pszCmdLine, const char *pszWorkingDir, CItemLog *pLog )
+{
+ bool bOk = false;
+
+ Msg( "Launching: %s\n", pszCmdLine );
+ Msg( "Directory: %s\n", pszWorkingDir );
+
+ if ( pLog )
+ {
+ SECURITY_ATTRIBUTES saAttr;
+
+ // Set the bInheritHandle flag so pipe handles are inherited.
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ // Create a pipe for the child process's STDOUT.
+ if ( !CreatePipe( &g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0 ) )
+ return false;
+
+ // Ensure the read handle to the pipe for STDOUT is not inherited.
+ if ( !SetHandleInformation( g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0 ) )
+ return false;
+ }
+
+ // Create the child process.
+ PROCESS_INFORMATION piProcInfo;
+ STARTUPINFO siStartInfo;
+
+ // Set up members of the PROCESS_INFORMATION structure.
+ V_memset( &piProcInfo, 0, sizeof( PROCESS_INFORMATION ) );
+
+ // Set up members of the STARTUPINFO structure.
+ // This structure specifies the STDIN and STDOUT handles for redirection.
+ V_memset( &siStartInfo, 0, sizeof( STARTUPINFO ) );
+ siStartInfo.cb = sizeof( STARTUPINFO );
+
+ if ( pLog )
+ {
+ siStartInfo.hStdError = g_hChildStd_OUT_Wr;
+ siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
+ siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
+ }
+
+ // Create the child process.
+ const BOOL bSuccess = CreateProcess(NULL,
+ const_cast< char * >( pszCmdLine ), // command line
+ NULL, // process security attributes
+ NULL, // primary thread security attributes
+ TRUE, // handles are inherited
+ CREATE_NO_WINDOW, // creation flags
+ NULL, // use parent's environment
+ pszWorkingDir, // use parent's current directory
+ &siStartInfo, // STARTUPINFO pointer
+ &piProcInfo ); // receives PROCESS_INFORMATION
+
+ // If an error occurs, return
+ if ( !bSuccess )
+ return false;
+
+ if ( pLog )
+ {
+ ReadFromPipe( pLog );
+
+ WaitForSingleObject( piProcInfo.hProcess, INFINITE );
+
+ bOk = false;
+
+ DWORD nExitCode = 0;
+ if ( GetExitCodeProcess( piProcInfo.hProcess, &nExitCode ) )
+ {
+ bOk = ( nExitCode == 0 );
+ }
+
+ CloseHandle( piProcInfo.hProcess );
+ CloseHandle( piProcInfo.hThread );
+ }
+ else
+ {
+ bOk = true;
+ }
+
+ return bOk;
+}
+
+
+//-----------------------------------------------------------------------------
+// Determine if 2 paths point ot the same file...
+// Note: This only works if the file exists
+//-----------------------------------------------------------------------------
+bool CItemUpload::IsSameFile( const char *szPath1, const char *szPath2 )
+{
+ if ( !szPath1 || !szPath2 )
+ return false;
+
+ HANDLE handle1 = ::CreateFile( szPath1, 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
+ HANDLE handle2 = ::CreateFile( szPath2, 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
+
+ bool bResult = false;
+
+ if ( handle1 != NULL && handle2 != NULL )
+ {
+ BY_HANDLE_FILE_INFORMATION fileInfo1;
+ BY_HANDLE_FILE_INFORMATION fileInfo2;
+ if ( ::GetFileInformationByHandle( handle1, &fileInfo1 ) && ::GetFileInformationByHandle( handle2, &fileInfo2 ) )
+ {
+ bResult = fileInfo1.dwVolumeSerialNumber == fileInfo2.dwVolumeSerialNumber &&
+ fileInfo1.nFileIndexHigh == fileInfo2.nFileIndexHigh &&
+ fileInfo1.nFileIndexLow == fileInfo2.nFileIndexLow;
+ }
+ }
+
+ if ( handle1 != NULL )
+ {
+ ::CloseHandle(handle1);
+ }
+
+ if ( handle2 != NULL )
+ {
+ ::CloseHandle( handle2 );
+ }
+
+ return bResult;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the filename to the current executable
+//-----------------------------------------------------------------------------
+bool CItemUpload::GetCurrentExecutableFileName( CUtlString &sCurrentExecutableFileName )
+{
+ char szModuleFileName[MAX_PATH];
+ szModuleFileName[0] = '\0';
+
+ if ( !::GetModuleFileName( (HMODULE)NULL, szModuleFileName, ARRAYSIZE( szModuleFileName ) ) )
+ return false;
+
+ sCurrentExecutableFileName = szModuleFileName;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Get the install location of the app from the registry via
+//
+// HKEY_LOCAL_MACHINE :
+// "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Steam App %d"
+//
+//-----------------------------------------------------------------------------
+bool CItemUpload::GetSteamAppInstallLocation( CUtlString &sSteamAppInstallLocation, int nAppId )
+{
+ HKEY hKey;
+ char szSteamAppInstallLocation[ 65536 ] = "";
+
+ const CFmtStr sRegKey( "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Steam App %d", nAppId );
+
+ if ( ERROR_SUCCESS == RegOpenKey( HKEY_LOCAL_MACHINE, sRegKey.String(), &hKey ) )
+ {
+ DWORD dwSize = sizeof( szSteamAppInstallLocation );
+ RegQueryValueEx( hKey, "InstallLocation", NULL, NULL, (LPBYTE)szSteamAppInstallLocation, &dwSize );
+ RegCloseKey( hKey );
+
+ sSteamAppInstallLocation = szSteamAppInstallLocation;
+
+ return true;
+ }
+
+ return false;
+} \ No newline at end of file