summaryrefslogtreecommitdiff
path: root/utils/vmf_tweak/vmf_tweak.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/vmf_tweak/vmf_tweak.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'utils/vmf_tweak/vmf_tweak.cpp')
-rw-r--r--utils/vmf_tweak/vmf_tweak.cpp540
1 files changed, 540 insertions, 0 deletions
diff --git a/utils/vmf_tweak/vmf_tweak.cpp b/utils/vmf_tweak/vmf_tweak.cpp
new file mode 100644
index 0000000..d133d9e
--- /dev/null
+++ b/utils/vmf_tweak/vmf_tweak.cpp
@@ -0,0 +1,540 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// vmf_tweak.cpp : Defines the entry point for the console application.
+//
+
+#include "stdafx.h"
+#include "chunkfile.h"
+#include "utllinkedlist.h"
+#include <stdlib.h>
+#include "tier1/strtools.h"
+#include "tier0/icommandline.h"
+#include "tier1/utlrbtree.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/KeyValues.h"
+#include "filesystem.h"
+#include "FileSystem_Tools.h"
+#include "cmdlib.h"
+
+#include <windows.h>
+
+char const *g_pInFilename = NULL;
+
+char *CopyString( char const *pStr );
+
+unsigned long g_CurLoadOrder = 0;
+
+
+//-----------------------------------------------------------------------------
+// default spec function
+//-----------------------------------------------------------------------------
+SpewRetval_t VMFTweakSpewFunc( SpewType_t spewType, char const *pMsg )
+{
+ OutputDebugString( pMsg );
+ printf( pMsg );
+ switch( spewType )
+ {
+ case SPEW_MESSAGE:
+ case SPEW_WARNING:
+ case SPEW_LOG:
+ return SPEW_CONTINUE;
+
+ case SPEW_ASSERT:
+ case SPEW_ERROR:
+ default:
+ return SPEW_DEBUGGER;
+ }
+}
+
+void logprint( char const *logfile, const char *fmt, ... )
+{
+ char string[ 8192 ];
+ va_list va;
+ va_start( va, fmt );
+ vsprintf( string, fmt, va );
+ va_end( va );
+
+ FILE *fp = NULL;
+ static bool first = false;
+ if ( first )
+ {
+ first = false;
+ fp = fopen( logfile, "wb" );
+ }
+ else
+ {
+ fp = fopen( logfile, "ab" );
+ }
+ if ( fp )
+ {
+ char *p = string;
+ while ( *p )
+ {
+ if ( *p == '\n' )
+ {
+ fputc( '\r', fp );
+ }
+ fputc( *p, fp );
+ p++;
+ }
+ fclose( fp );
+ }
+}
+
+
+class CChunkKeyBase
+{
+public:
+ unsigned long m_LoadOrder; // Which order it appeared in the file.
+};
+
+
+class CKeyValue : public CChunkKeyBase
+{
+public:
+ void SetKey( const char *pStr )
+ {
+ delete [] m_pKey;
+ m_pKey = CopyString( pStr );
+ }
+
+ void SetValue( const char *pStr )
+ {
+ delete [] m_pValue;
+ m_pValue = CopyString( pStr );
+ }
+
+
+public:
+ char *m_pKey;
+ char *m_pValue;
+};
+
+
+class CChunk : public CChunkKeyBase
+{
+public:
+ // Returns true if the chunk has the specified key and if its value
+ // is equal to pValueStr (case-insensitive).
+ bool CompareKey( char const *pKeyName, char const *pValueStr );
+
+ // Look for a key by name.
+ CKeyValue* FindKey( char const *pKeyName );
+ CKeyValue* FindKey( char const *pKeyName, const char *pValue );
+
+ CChunk* FindChunk( char const *pKeyName );
+
+ // Find a key by name, and replace any occurences with the new name.
+ void RenameKey( const char *szOldName, const char *szNewName );
+
+public:
+
+ char *m_pChunkName;
+ CUtlLinkedList<CKeyValue*, unsigned short> m_Keys;
+ CUtlLinkedList<CChunk*, unsigned short> m_Chunks;
+};
+
+
+CChunk* ParseChunk( char const *pChunkName, bool bOnlyOne );
+
+
+CChunk *g_pCurChunk = 0;
+CChunkFile *g_pChunkFile = 0;
+int g_DotCounter = 0;
+
+
+
+// --------------------------------------------------------------------------------- //
+// CChunk implementation.
+// --------------------------------------------------------------------------------- //
+
+bool CChunk::CompareKey( char const *pKeyName, char const *pValueStr )
+{
+ CKeyValue *pKey = FindKey( pKeyName );
+
+ if( pKey && stricmp( pKey->m_pValue, pValueStr ) == 0 )
+ return true;
+ else
+ return false;
+}
+
+
+CKeyValue* CChunk::FindKey( char const *pKeyName )
+{
+ for( unsigned short i=m_Keys.Head(); i != m_Keys.InvalidIndex(); i=m_Keys.Next(i) )
+ {
+ CKeyValue *pKey = m_Keys[i];
+
+ if( stricmp( pKey->m_pKey, pKeyName ) == 0 )
+ return pKey;
+ }
+
+ return NULL;
+}
+
+
+CChunk* CChunk::FindChunk( char const *pChunkName )
+{
+ for( unsigned short i=m_Chunks.Head(); i != m_Chunks.InvalidIndex(); i=m_Chunks.Next(i) )
+ {
+ CChunk *pChunk = m_Chunks[i];
+
+ if( stricmp( pChunk->m_pChunkName, pChunkName ) == 0 )
+ return pChunk;
+ }
+
+ return NULL;
+}
+
+CKeyValue* CChunk::FindKey( char const *pKeyName, const char *pValue )
+{
+ for( unsigned short i=m_Keys.Head(); i != m_Keys.InvalidIndex(); i=m_Keys.Next(i) )
+ {
+ CKeyValue *pKey = m_Keys[i];
+
+ if( stricmp( pKey->m_pKey, pKeyName ) == 0 && stricmp( pKey->m_pValue, pValue ) == 0 )
+ return pKey;
+ }
+
+ return NULL;
+}
+
+
+void CChunk::RenameKey( const char *szOldName, const char *szNewName )
+{
+ if ((!szOldName) || (!szNewName))
+ return;
+
+ CKeyValue *pKey = FindKey( szOldName );
+ if ( pKey )
+ {
+ delete pKey->m_pKey;
+ pKey->m_pKey = CopyString( szNewName );
+ }
+}
+
+
+// --------------------------------------------------------------------------------- //
+// Util functions.
+// --------------------------------------------------------------------------------- //
+char *CopyString( char const *pStr )
+{
+ char *pRet = new char[ strlen(pStr) + 1 ];
+ strcpy( pRet, pStr );
+ return pRet;
+}
+
+ChunkFileResult_t MyDefaultHandler( CChunkFile *pFile, void *pData, char const *pChunkName )
+{
+ CChunk *pChunk = ParseChunk( pChunkName, true );
+ g_pCurChunk->m_Chunks.AddToTail( pChunk );
+ return ChunkFile_Ok;
+}
+
+
+ChunkFileResult_t MyKeyHandler( const char *szKey, const char *szValue, void *pData )
+{
+ // Add the key to the current chunk.
+ CKeyValue *pKey = new CKeyValue;
+ pKey->m_LoadOrder = g_CurLoadOrder++;
+
+ pKey->m_pKey = CopyString( szKey );
+ pKey->m_pValue = CopyString( szValue );
+
+ g_pCurChunk->m_Keys.AddToTail( pKey );
+ return ChunkFile_Ok;
+}
+
+
+CChunk* ParseChunk( char const *pChunkName, bool bOnlyOne )
+{
+ // Add the new chunk.
+ CChunk *pChunk = new CChunk;
+ pChunk->m_pChunkName = CopyString( pChunkName );
+ pChunk->m_LoadOrder = g_CurLoadOrder++;
+
+ // Parse it out..
+ CChunk *pOldChunk = g_pCurChunk;
+ g_pCurChunk = pChunk;
+
+ //if( ++g_DotCounter % 16 == 0 )
+ // printf( "." );
+
+ while( 1 )
+ {
+ if( g_pChunkFile->ReadChunk( MyKeyHandler ) != ChunkFile_Ok )
+ break;
+
+ if( bOnlyOne )
+ break;
+ }
+
+ g_pCurChunk = pOldChunk;
+ return pChunk;
+}
+
+
+CChunk* ReadChunkFile( char const *pInFilename )
+{
+ CChunkFile chunkFile;
+
+ if( chunkFile.Open( pInFilename, ChunkFile_Read ) != ChunkFile_Ok )
+ {
+ printf( "Error opening chunk file %s for reading.\n", pInFilename );
+ return NULL;
+ }
+
+ printf( "Reading.." );
+ chunkFile.SetDefaultChunkHandler( MyDefaultHandler, 0 );
+ g_pChunkFile = &chunkFile;
+
+ CChunk *pRet = ParseChunk( "***ROOT***", false );
+ printf( "\n\n" );
+
+ return pRet;
+}
+
+class CChunkHolder
+{
+public:
+ static bool SortChunkFn( const CChunkHolder &a, const CChunkHolder &b )
+ {
+ return a.m_LoadOrder < b.m_LoadOrder;
+ }
+
+ unsigned long m_LoadOrder;
+ CKeyValue *m_pKey;
+ CChunk *m_pChunk;
+};
+
+
+void WriteChunks_R( CChunkFile *pFile, CChunk *pChunk, bool bRoot )
+{
+ if( !bRoot )
+ {
+ pFile->BeginChunk( pChunk->m_pChunkName );
+ }
+
+ // Sort them..
+ CUtlRBTree<CChunkHolder,int> sortedStuff( 0, 0, &CChunkHolder::SortChunkFn );
+
+ // Write keys.
+ for( unsigned short i=pChunk->m_Keys.Head(); i != pChunk->m_Keys.InvalidIndex(); i = pChunk->m_Keys.Next( i ) )
+ {
+ CChunkHolder holder;
+ holder.m_pKey = pChunk->m_Keys[i];
+ holder.m_LoadOrder = holder.m_pKey->m_LoadOrder;
+ holder.m_pChunk = NULL;
+ sortedStuff.Insert( holder );
+ }
+
+ // Write subchunks.
+ for( int i=pChunk->m_Chunks.Head(); i != pChunk->m_Chunks.InvalidIndex(); i = pChunk->m_Chunks.Next( i ) )
+ {
+ CChunkHolder holder;
+ holder.m_pChunk = pChunk->m_Chunks[i];
+ holder.m_LoadOrder = holder.m_pChunk->m_LoadOrder;
+ holder.m_pKey = NULL;
+ sortedStuff.Insert( holder );
+ }
+
+ // Write stuff in sorted order.
+ int i = sortedStuff.FirstInorder();
+ if ( i != sortedStuff.InvalidIndex() )
+ {
+ while ( 1 )
+ {
+ CChunkHolder &h = sortedStuff[i];
+ if ( h.m_pKey )
+ {
+ pFile->WriteKeyValue( h.m_pKey->m_pKey, h.m_pKey->m_pValue );
+ }
+ else
+ {
+ WriteChunks_R( pFile, h.m_pChunk, false );
+ }
+ if ( i == sortedStuff.LastInorder() )
+ break;
+ i = sortedStuff.NextInorder( i );
+ }
+ }
+
+ if( !bRoot )
+ {
+ pFile->EndChunk();
+ }
+}
+
+
+bool WriteChunkFile( char const *pOutFilename, CChunk *pRoot )
+{
+ CChunkFile chunkFile;
+
+ if( chunkFile.Open( pOutFilename, ChunkFile_Write ) != ChunkFile_Ok )
+ {
+ printf( "Error opening chunk file %s for writing.\n", pOutFilename );
+ return false;
+ }
+
+ printf( "Writing.." );
+ WriteChunks_R( &chunkFile, pRoot, true );
+ printf( "\n\n" );
+
+ return true;
+}
+
+
+//
+//
+// EXAMPLE
+//
+//
+void ScanRopeSlack( CChunk *pChunk )
+{
+ if( stricmp( pChunk->m_pChunkName, "entity" ) == 0 )
+ {
+ if( pChunk->CompareKey( "classname", "keyframe_rope" ) ||
+ pChunk->CompareKey( "classname", "move_rope" ) )
+ {
+ CKeyValue *pKey = pChunk->FindKey( "slack" );
+ if( pKey )
+ {
+ // Subtract 100 from all the Slack properties.
+ float flCur = (float)atof( pKey->m_pValue );
+ char str[256];
+ sprintf( str, "%f", flCur + 100 );
+ pKey->m_pValue = CopyString( str );
+ }
+ }
+ }
+}
+
+int g_nLogicAutoReplacementsMade = 0;
+void LogicAuto( CChunk *pChunk )
+{
+ if ( pChunk->CompareKey( "classname", "logic_auto" ) )
+ {
+ CChunk *pConnections = pChunk->FindChunk( "connections" );
+ if ( !pConnections )
+ return;
+
+ bool bFound = false;
+ for ( int i=0; i < pConnections->m_Keys.Count(); i++ )
+ {
+ CKeyValue *pTestKV = pConnections->m_Keys[i];
+ if ( V_stristr( pTestKV->m_pValue, "tonemap" ) == pTestKV->m_pValue )
+ {
+ bFound = true;
+ break;
+ }
+ }
+
+ if ( !bFound )
+ return;
+
+ ++g_nLogicAutoReplacementsMade;
+
+ // Ok, this is a logic_auto with OnMapSpawn outputs in its connections.
+ CUtlLinkedList<CKeyValue*,int> newKeys;
+ FOR_EACH_LL( pConnections->m_Keys, i )
+ {
+ CKeyValue *pTestKV = pConnections->m_Keys[i];
+ if ( V_stricmp( pTestKV->m_pKey, "OnMapSpawn" ) == 0 )
+ {
+ if ( V_stristr( pTestKV->m_pValue, "tonemap" ) == pTestKV->m_pValue )
+ {
+ CKeyValue *pNewKV = new CKeyValue;
+ pNewKV->SetKey( "OnMapTransition" );
+ pNewKV->SetValue( pTestKV->m_pValue );
+ newKeys.AddToTail( pNewKV );
+ }
+ }
+ }
+
+ FOR_EACH_LL( newKeys, i )
+ {
+ if ( !pConnections->FindKey( "OnMapTransition", newKeys[i]->m_pValue ) )
+ {
+ pConnections->m_Keys.AddToTail( newKeys[i] );
+ }
+ }
+
+ // Fix spawnflags.
+ CKeyValue *pFlags = pChunk->FindKey("spawnflags");
+ if ( pFlags )
+ {
+ unsigned long curVal;
+ sscanf( pFlags->m_pValue, "%lu", &curVal );
+ if ( curVal & 1 )
+ {
+ --curVal;
+ char str[512];
+ sprintf( str, "%lu", curVal );
+ pFlags->SetValue( str );
+ }
+ }
+ }
+}
+
+
+void ScanChunks( CChunk *pChunk, void (*fn)(CChunk *) )
+{
+ fn( pChunk );
+
+ // Recurse into the children.
+ for( unsigned short i=pChunk->m_Chunks.Head(); i != pChunk->m_Chunks.InvalidIndex(); i = pChunk->m_Chunks.Next( i ) )
+ {
+ ScanChunks( pChunk->m_Chunks[i], fn );
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ CommandLine()->CreateCmdLine( argc, argv );
+ SpewOutputFunc( VMFTweakSpewFunc );
+
+ if( argc < 2 )
+ {
+ printf( "vmf_tweak <input file> [output file]\n" );
+ return 1;
+ }
+
+ g_pInFilename = argv[1];
+ char const *pOutFilename = argc >= 3 ? argv[2] : "";
+
+ CChunk *pRoot = ReadChunkFile( g_pInFilename );
+ if( !pRoot )
+ return 2;
+
+ //
+ //
+ //
+ // This is where you can put code to modify the VMF.
+ //
+ //
+ //
+
+ // If they didn't specify -game on the command line, use VPROJECT.
+ char workingdir[ 256 ];
+ workingdir[0] = 0;
+ Q_getwd( workingdir, sizeof(workingdir) );
+ CmdLib_InitFileSystem( workingdir );
+ ScanChunks( pRoot, LogicAuto );
+ FileSystem_Term();
+
+ Msg( "%s: %d logic_auto replacements made.\n", g_pInFilename, g_nLogicAutoReplacementsMade );
+
+ if ( argc >= 3 )
+ {
+ if( !WriteChunkFile( pOutFilename, pRoot ) )
+ return 3;
+ }
+
+ return 0;
+}
+