|
|
//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include "vbsp.h"
#include "UtlBuffer.h"
#include "UtlSymbol.h"
#include "UtlRBTree.h"
#include "KeyValues.h"
#include "bsplib.h"
#include "materialpatch.h"
#include "tier1/strtools.h"
// case insensitive
static CUtlSymbolTable s_SymbolTable( 0, 32, true );
struct NameTranslationLookup_t { CUtlSymbol m_OriginalFileName; CUtlSymbol m_PatchFileName; };
static bool NameTranslationLessFunc( NameTranslationLookup_t const& src1, NameTranslationLookup_t const& src2 ) { return src1.m_PatchFileName < src2.m_PatchFileName; }
CUtlRBTree<NameTranslationLookup_t, int> s_MapPatchedMatToOriginalMat( 0, 256, NameTranslationLessFunc );
void AddNewTranslation( const char *pOriginalMaterialName, const char *pNewMaterialName ) { NameTranslationLookup_t newEntry; newEntry.m_OriginalFileName = s_SymbolTable.AddString( pOriginalMaterialName ); newEntry.m_PatchFileName = s_SymbolTable.AddString( pNewMaterialName );
s_MapPatchedMatToOriginalMat.Insert( newEntry ); }
const char *GetOriginalMaterialNameForPatchedMaterial( const char *pPatchMaterialName ) { const char *pRetName = NULL; int id; NameTranslationLookup_t lookup; lookup.m_PatchFileName = s_SymbolTable.AddString( pPatchMaterialName ); do { id = s_MapPatchedMatToOriginalMat.Find( lookup ); if( id >= 0 ) { NameTranslationLookup_t &found = s_MapPatchedMatToOriginalMat[id]; lookup.m_PatchFileName = found.m_OriginalFileName; pRetName = s_SymbolTable.String( found.m_OriginalFileName ); } } while( id >= 0 ); if( !pRetName ) { // This isn't a patched material, so just return the original name.
return pPatchMaterialName; } return pRetName; }
void CreateMaterialPatchRecursive( KeyValues *pOriginalKeyValues, KeyValues *pPatchKeyValues, int nKeys, const MaterialPatchInfo_t *pInfo ) { int i; for( i = 0; i < nKeys; i++ ) { const char *pVal = pOriginalKeyValues->GetString( pInfo[i].m_pKey, NULL ); if( !pVal ) continue; if( pInfo[i].m_pRequiredOriginalValue && Q_stricmp( pVal, pInfo[i].m_pRequiredOriginalValue ) != 0 ) continue; pPatchKeyValues->SetString( pInfo[i].m_pKey, pInfo[i].m_pValue ); } KeyValues *pScan; for( pScan = pOriginalKeyValues->GetFirstTrueSubKey(); pScan; pScan = pScan->GetNextTrueSubKey() ) { CreateMaterialPatchRecursive( pScan, pPatchKeyValues->FindKey( pScan->GetName(), true ), nKeys, pInfo ); } }
//-----------------------------------------------------------------------------
// A version which allows you to patch multiple key values
//-----------------------------------------------------------------------------
void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName, int nKeys, const MaterialPatchInfo_t *pInfo, MaterialPatchType_t nPatchType ) { char pOldVMTFile[ 512 ]; char pNewVMTFile[ 512 ];
AddNewTranslation( pOriginalMaterialName, pNewMaterialName ); Q_snprintf( pOldVMTFile, 512, "materials/%s.vmt", pOriginalMaterialName ); Q_snprintf( pNewVMTFile, 512, "materials/%s.vmt", pNewMaterialName );
// printf( "Creating material patch file %s which points at %s\n", newVMTFile, oldVMTFile );
KeyValues *kv = new KeyValues( "patch" ); if ( !kv ) { Error( "Couldn't allocate KeyValues for %s!!!", pNewMaterialName ); }
kv->SetString( "include", pOldVMTFile );
const char *pSectionName = (nPatchType == PATCH_INSERT) ? "insert" : "replace"; KeyValues *section = kv->FindKey( pSectionName, true );
if( nPatchType == PATCH_REPLACE ) { char name[512]; Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pOriginalMaterialName ) ); KeyValues *origkv = new KeyValues( "blah" );
if ( !origkv->LoadFromFile( g_pFileSystem, name ) ) { origkv->deleteThis(); Assert( 0 ); return; }
CreateMaterialPatchRecursive( origkv, section, nKeys, pInfo ); origkv->deleteThis(); } else { for ( int i = 0; i < nKeys; ++i ) { section->SetString( pInfo[i].m_pKey, pInfo[i].m_pValue ); } } // Write patched .vmt into a memory buffer
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); kv->RecursiveSaveToFile( buf, 0 );
// Add to pak file for this .bsp
AddBufferToPak( GetPakFile(), pNewVMTFile, (void*)buf.Base(), buf.TellPut(), true );
// Cleanup
kv->deleteThis(); }
//-----------------------------------------------------------------------------
// Patches a single keyvalue in a material
//-----------------------------------------------------------------------------
void CreateMaterialPatch( const char *pOriginalMaterialName, const char *pNewMaterialName, const char *pNewKey, const char *pNewValue, MaterialPatchType_t nPatchType ) { MaterialPatchInfo_t info; info.m_pKey = pNewKey; info.m_pValue = pNewValue; CreateMaterialPatch( pOriginalMaterialName, pNewMaterialName, 1, &info, nPatchType ); }
//-----------------------------------------------------------------------------
// Scan material + all subsections for key
//-----------------------------------------------------------------------------
static bool DoesMaterialHaveKey( KeyValues *pKeyValues, const char *pKeyName ) { const char *pVal; pVal = pKeyValues->GetString( pKeyName, NULL ); if ( pVal != NULL ) return true;
for( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() ) { if ( DoesMaterialHaveKey( pSubKey, pKeyName) ) return true; } return false; }
//-----------------------------------------------------------------------------
// Scan material + all subsections for key/value pair
//-----------------------------------------------------------------------------
static bool DoesMaterialHaveKeyValuePair( KeyValues *pKeyValues, const char *pKeyName, const char *pSearchValue ) { const char *pVal; pVal = pKeyValues->GetString( pKeyName, NULL ); if ( pVal != NULL && ( Q_stricmp( pSearchValue, pVal ) == 0 ) ) return true;
for( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey; pSubKey = pSubKey->GetNextTrueSubKey() ) { if ( DoesMaterialHaveKeyValuePair( pSubKey, pKeyName, pSearchValue ) ) return true; } return false; }
//-----------------------------------------------------------------------------
// Scan material + all subsections for key
//-----------------------------------------------------------------------------
bool DoesMaterialHaveKey( const char *pMaterialName, const char *pKeyName ) { char name[512]; Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pMaterialName ) ); KeyValues *kv = new KeyValues( "blah" );
if ( !kv->LoadFromFile( g_pFileSystem, name ) ) { kv->deleteThis(); return NULL; }
bool retVal = DoesMaterialHaveKey( kv, pKeyName );
kv->deleteThis(); return retVal; }
//-----------------------------------------------------------------------------
// Scan material + all subsections for key/value pair
//-----------------------------------------------------------------------------
bool DoesMaterialHaveKeyValuePair( const char *pMaterialName, const char *pKeyName, const char *pSearchValue ) { char name[512]; Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pMaterialName ) ); KeyValues *kv = new KeyValues( "blah" );
if ( !kv->LoadFromFile( g_pFileSystem, name ) ) { kv->deleteThis(); return NULL; }
bool retVal = DoesMaterialHaveKeyValuePair( kv, pKeyName, pSearchValue );
kv->deleteThis(); return retVal; }
//-----------------------------------------------------------------------------
// Gets a material value from a material. Ignores all patches
//-----------------------------------------------------------------------------
bool GetValueFromMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len ) { char name[512]; Q_snprintf( name, 512, "materials/%s.vmt", GetOriginalMaterialNameForPatchedMaterial( pMaterialName ) ); KeyValues *kv = new KeyValues( "blah" );
if ( !kv->LoadFromFile( g_pFileSystem, name ) ) { // Assert( 0 );
kv->deleteThis(); return NULL; }
const char *pTmpValue = kv->GetString( pKey, NULL ); if( pTmpValue ) { Q_strncpy( pValue, pTmpValue, len ); }
kv->deleteThis(); return ( pTmpValue != NULL ); }
//-----------------------------------------------------------------------------
// Finds the original material associated with a patched material
//-----------------------------------------------------------------------------
MaterialSystemMaterial_t FindOriginalMaterial( const char *materialName, bool *pFound, bool bComplain ) { MaterialSystemMaterial_t matID; matID = FindMaterial( GetOriginalMaterialNameForPatchedMaterial( materialName ), pFound, bComplain ); return matID; }
//-----------------------------------------------------------------------------
// Load keyvalues from the local pack file, or from a file
//-----------------------------------------------------------------------------
bool LoadKeyValuesFromPackOrFile( const char *pFileName, KeyValues *pKeyValues ) { CUtlBuffer buf; if ( ReadFileFromPak( GetPakFile(), pFileName, true, buf ) ) { return pKeyValues->LoadFromBuffer( pFileName, buf ); }
return pKeyValues->LoadFromFile( g_pFileSystem, pFileName ); }
//-----------------------------------------------------------------------------
// VMT parser
//-----------------------------------------------------------------------------
static void InsertKeyValues( KeyValues &dst, KeyValues& src, bool bCheckForExistence ) { KeyValues *pSrcVar = src.GetFirstSubKey(); while( pSrcVar ) { if ( !bCheckForExistence || dst.FindKey( pSrcVar->GetName() ) ) { switch( pSrcVar->GetDataType() ) { case KeyValues::TYPE_STRING: dst.SetString( pSrcVar->GetName(), pSrcVar->GetString() ); break; case KeyValues::TYPE_INT: dst.SetInt( pSrcVar->GetName(), pSrcVar->GetInt() ); break; case KeyValues::TYPE_FLOAT: dst.SetFloat( pSrcVar->GetName(), pSrcVar->GetFloat() ); break; case KeyValues::TYPE_PTR: dst.SetPtr( pSrcVar->GetName(), pSrcVar->GetPtr() ); break; } } pSrcVar = pSrcVar->GetNextKey(); } }
static void ExpandPatchFile( KeyValues &keyValues ) { int nCount = 0; while( nCount < 10 && stricmp( keyValues.GetName(), "patch" ) == 0 ) { // WriteKeyValuesToFile( "patch.txt", keyValues );
const char *pIncludeFileName = keyValues.GetString( "include" ); if( !pIncludeFileName ) return;
KeyValues * includeKeyValues = new KeyValues( "vmt" ); int nBufLen = Q_strlen( pIncludeFileName ) + Q_strlen( "materials/.vmt" ) + 1; char *pFileName = ( char * )stackalloc( nBufLen ); Q_strncpy( pFileName, pIncludeFileName, nBufLen ); bool bSuccess = LoadKeyValuesFromPackOrFile( pFileName, includeKeyValues ); if ( !bSuccess ) { includeKeyValues->deleteThis(); return; }
KeyValues *pInsertSection = keyValues.FindKey( "insert" ); if( pInsertSection ) { InsertKeyValues( *includeKeyValues, *pInsertSection, false ); keyValues = *includeKeyValues; }
KeyValues *pReplaceSection = keyValues.FindKey( "replace" ); if( pReplaceSection ) { InsertKeyValues( *includeKeyValues, *pReplaceSection, true ); keyValues = *includeKeyValues; }
// Could add other commands here, like "delete", "rename", etc.
includeKeyValues->deleteThis(); nCount++; }
if( nCount >= 10 ) { Warning( "Infinite recursion in patch file?\n" ); } }
KeyValues *LoadMaterialKeyValues( const char *pMaterialName, unsigned int nFlags ) { // Load the underlying file
KeyValues *kv = new KeyValues( "blah" );
char pFullMaterialName[512]; Q_snprintf( pFullMaterialName, 512, "materials/%s.vmt", pMaterialName ); if ( !LoadKeyValuesFromPackOrFile( pFullMaterialName, kv ) ) { // Assert( 0 );
kv->deleteThis(); return NULL; }
if( nFlags & LOAD_MATERIAL_KEY_VALUES_FLAGS_EXPAND_PATCH ) { ExpandPatchFile( *kv ); }
return kv; }
void WriteMaterialKeyValuesToPak( const char *pMaterialName, KeyValues *kv ) { char pFullMaterialName[512]; Q_snprintf( pFullMaterialName, 512, "materials/%s.vmt", pMaterialName );
// Write patched .vmt into a memory buffer
CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); kv->RecursiveSaveToFile( buf, 0 );
// Add to pak file for this .bsp
AddBufferToPak( GetPakFile(), pFullMaterialName, (void*)buf.Base(), buf.TellPut(), true );
// Cleanup
kv->deleteThis(); }
//-----------------------------------------------------------------------------
// Gets a keyvalue from a *patched* material
//-----------------------------------------------------------------------------
bool GetValueFromPatchedMaterial( const char *pMaterialName, const char *pKey, char *pValue, int len ) { // Load the underlying file so that we can check if env_cubemap is in there.
KeyValues *kv = new KeyValues( "blah" );
char pFullMaterialName[512]; Q_snprintf( pFullMaterialName, 512, "materials/%s.vmt", pMaterialName ); if ( !LoadKeyValuesFromPackOrFile( pFullMaterialName, kv ) ) { // Assert( 0 );
kv->deleteThis(); return NULL; }
ExpandPatchFile( *kv );
const char *pTmpValue = kv->GetString( pKey, NULL ); if( pTmpValue ) { Q_strncpy( pValue, pTmpValue, len ); }
kv->deleteThis(); return ( pTmpValue != NULL ); }
|