summaryrefslogtreecommitdiff
path: root/utils/kvc
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/kvc
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'utils/kvc')
-rw-r--r--utils/kvc/cbase.h19
-rw-r--r--utils/kvc/kvc.cpp710
-rw-r--r--utils/kvc/kvc.vpc47
-rw-r--r--utils/kvc/kvc_paintkit.cpp421
-rw-r--r--utils/kvc/kvc_paintkit.h17
5 files changed, 1214 insertions, 0 deletions
diff --git a/utils/kvc/cbase.h b/utils/kvc/cbase.h
new file mode 100644
index 0000000..42d73c9
--- /dev/null
+++ b/utils/kvc/cbase.h
@@ -0,0 +1,19 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef CBASE_H
+#define CBASE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basetypes.h"
+
+// This is just a dummy file to make this tool compile
+#include "ai_activity.h"
+#include "utlvector.h"
+
+#endif // CBASE_H
diff --git a/utils/kvc/kvc.cpp b/utils/kvc/kvc.cpp
new file mode 100644
index 0000000..202c945
--- /dev/null
+++ b/utils/kvc/kvc.cpp
@@ -0,0 +1,710 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: vcd_sound_check.cpp : Defines the entry point for the console application.
+//
+//=============================================================================//
+#include <stdio.h>
+#include <windows.h>
+#include "tier0/dbg.h"
+#include "tier1/utldict.h"
+#include "filesystem.h"
+#include "FileSystem_Tools.h"
+#include "tier1/KeyValues.h"
+#include "cmdlib.h"
+#include "scriplib.h"
+#include "vstdlib/random.h"
+#include "tier1/UtlBuffer.h"
+#include "pacifier.h"
+#include "appframework/appframework.h"
+#include "tier0/icommandline.h"
+#include "keyvaluescompiler.h"
+#include "tier2/keyvaluesmacros.h"
+
+#include "kvc_paintkit.h"
+
+#include "tier0/fasttimer.h"
+
+// #define TESTING 1
+
+
+bool uselogfile = false;
+bool timing = false;
+qboolean verbose = false;
+
+struct AnalysisData
+{
+ CUtlSymbolTable symbols;
+};
+
+static AnalysisData g_Analysis;
+
+IBaseFileSystem *filesystem = NULL;
+//IFileSystem *g_pFullFileSystem = NULL;
+
+static bool spewed = false;
+
+SpewRetval_t SpewFunc( SpewType_t type, char const *pMsg )
+{
+ spewed = true;
+
+ printf( "%s", pMsg );
+ OutputDebugString( pMsg );
+
+ if ( type == SPEW_ERROR )
+ {
+ printf( "\n" );
+ OutputDebugString( "\n" );
+ }
+
+ return SPEW_CONTINUE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : depth -
+// *fmt -
+// ... -
+//-----------------------------------------------------------------------------
+void vprint( int depth, const char *fmt, ... )
+{
+ char string[ 8192 ];
+ va_list va;
+ va_start( va, fmt );
+ vsprintf( string, fmt, va );
+ va_end( va );
+
+ FILE *fp = NULL;
+
+ if ( uselogfile )
+ {
+ fp = fopen( "log.txt", "ab" );
+ }
+
+ while ( depth-- > 0 )
+ {
+ printf( " " );
+ OutputDebugString( " " );
+ if ( fp )
+ {
+ fprintf( fp, " " );
+ }
+ }
+
+ ::printf( "%s", string );
+ OutputDebugString( string );
+
+ if ( fp )
+ {
+ char *p = string;
+ while ( *p )
+ {
+ if ( *p == '\n' )
+ {
+ fputc( '\r', fp );
+ }
+ fputc( *p, fp );
+ p++;
+ }
+ fclose( fp );
+ }
+}
+
+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 = true;
+ 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 );
+ }
+}
+
+
+void Con_Printf( const char *fmt, ... )
+{
+ va_list args;
+ static char output[1024];
+
+ va_start( args, fmt );
+ vprintf( fmt, args );
+ vsprintf( output, fmt, args );
+
+ vprint( 0, output );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void printusage( void )
+{
+ vprint( 0, "usage: kvc <wildcard path> [<wildcard path>] <outputfile>\n\
+\n\
+ -v = verbose output\n\
+ -l = log to file log.txt\n\
+ -t = perform load timing tests\n\
+ -p = perform paint kit macro expansion\n\
+ in this mode if no output file is specified,\n\
+ the input file is copied to <input>_bak and <input> is overwritten\n\
+ with -p, each file specified is processed separately without wildcards\n\
+ -f = With -p, output is to <input>_fix and <input> is unchanged\n\
+\n\
+e.g.: kvc -l u:/xbox/game/hl2x/materials/*.vmt u:/xbox/game/hl2x/kvc/vmt.kv\n\
+\n\
+ kvc -v -p americanpastoral_rocketlauncher.paintkit\n\
+\n" );
+
+ // Exit app
+ exit( 1 );
+}
+
+void BuildFileList_R( CUtlVector< CUtlSymbol >& files, char const *dir, char const *extension )
+{
+ WIN32_FIND_DATA wfd;
+
+ char directory[ 256 ];
+ char filename[ 256 ];
+ HANDLE ff;
+
+ sprintf( directory, "%s\\*.*", dir );
+
+#if defined( TESTING )
+ if ( files.Count() > 100 )
+ return;
+#endif
+
+ if ( ( ff = FindFirstFile( directory, &wfd ) ) == INVALID_HANDLE_VALUE )
+ return;
+
+ int extlen = strlen( extension );
+
+ do
+ {
+#if defined( TESTING )
+ if ( files.Count() > 100 )
+ return;
+#endif
+ if ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
+ {
+
+ if ( wfd.cFileName[ 0 ] == '.' )
+ continue;
+
+ // Recurse down directory
+ sprintf( filename, "%s\\%s", dir, wfd.cFileName );
+ BuildFileList_R( files, filename, extension );
+ }
+ else
+ {
+ int len = strlen( wfd.cFileName );
+ if ( len > extlen )
+ {
+ if ( !stricmp( &wfd.cFileName[ len - extlen ], extension ) )
+ {
+ char filename[ MAX_PATH ];
+ Q_snprintf( filename, sizeof( filename ), "%s\\%s", dir, wfd.cFileName );
+ _strlwr( filename );
+
+ Q_FixSlashes( filename );
+
+ CUtlSymbol sym = g_Analysis.symbols.AddString( filename );
+ files.AddToTail( sym );
+
+ if ( !( files.Count() % 3000 ) )
+ {
+ vprint( 0, "...found %i .%s files\n", files.Count(), extension );
+ }
+ }
+ }
+ }
+ } while ( FindNextFile( ff, &wfd ) );
+}
+
+void BuildFileList( CUtlVector< CUtlSymbol >& files, char const *rootdir, char const *extension )
+{
+ files.RemoveAll();
+ BuildFileList_R( files, rootdir, extension );
+}
+
+
+void BuildFileListWildcard_R( CUtlVector< CUtlSymbol >& files, char const *dir, char const *wildcard )
+{
+ // Match files in current directory againsxt the wildcard
+ char filesearch[ 256 ];
+ WIN32_FIND_DATA filedata;
+ HANDLE h;
+
+ Q_snprintf( filesearch, sizeof( filesearch ), "%s\\%s", dir, wildcard );
+
+ if ( ( h = FindFirstFile( filesearch, &filedata ) ) != INVALID_HANDLE_VALUE )
+ {
+ do
+ {
+ #if defined( TESTING )
+ if ( files.Count() > 100 )
+ return;
+ #endif
+ if ( filedata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
+ {
+ continue;
+ }
+
+ char filename[ MAX_PATH ];
+ Q_snprintf( filename, sizeof( filename ), "%s\\%s", dir, filedata.cFileName );
+ _strlwr( filename );
+
+ Q_FixSlashes( filename );
+
+ CUtlSymbol sym = g_Analysis.symbols.AddString( filename );
+ files.AddToTail( sym );
+
+ if ( !( files.Count() % 3000 ) )
+ {
+ vprint( 0, "...found %i files\n", files.Count() );
+ }
+
+ } while ( FindNextFile( h, &filedata ) );
+ }
+
+ // Now iterate the subdirectories and try them, too
+ WIN32_FIND_DATA wfd;
+
+ char directory[ 256 ];
+ char filename[ 256 ];
+ HANDLE ff;
+
+ sprintf( directory, "%s\\*.*", dir );
+
+#if defined( TESTING )
+ if ( files.Count() > 100 )
+ return;
+#endif
+
+ if ( ( ff = FindFirstFile( directory, &wfd ) ) == INVALID_HANDLE_VALUE )
+ return;
+
+ do
+ {
+#if defined( TESTING )
+ if ( files.Count() > 100 )
+ return;
+#endif
+ if ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
+ {
+ if ( wfd.cFileName[ 0 ] == '.' )
+ continue;
+
+ // Recurse down directory
+ sprintf( filename, "%s\\%s", dir, wfd.cFileName );
+ BuildFileListWildcard_R( files, filename, wildcard );
+ }
+ } while ( FindNextFile( ff, &wfd ) );
+}
+
+void BuildFileListFromWildcard( CUtlVector< CUtlSymbol >& files, char const *search )
+{
+ char basepath[ 512 ];
+ char wildcard[ 512 ];
+
+ Q_ExtractFilePath( search, basepath, sizeof( basepath ) );
+ Q_StripTrailingSlash( basepath );
+ Q_strncpy( wildcard, &search[ Q_strlen( basepath ) + 1 ], sizeof( wildcard ) );
+
+ BuildFileListWildcard_R( files, basepath, wildcard );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CheckLogFile( void )
+{
+ if ( uselogfile )
+ {
+ _unlink( "log.txt" );
+ vprint( 0, " Outputting to log.txt\n" );
+ }
+}
+
+void PrintHeader()
+{
+ vprint( 0, "Valve Software - kvc.exe (%s)\n", __DATE__ );
+ vprint( 0, "--- KeyValues File compiler ---\n" );
+}
+
+void CompileKeyValuesFiles( CUtlVector< CUtlSymbol >& scriptFiles, CCompiledKeyValuesWriter& writer )
+{
+ CUtlVector< CUtlSymbol > disposition;
+
+ StartPacifier( "CompileKeyValuesFiles" );
+ int i;
+ int c = scriptFiles.Count();
+ for ( i = 0 ; i < c; ++i )
+ {
+ UpdatePacifier( (float)i / (float)c );
+ char filename[ 512 ];
+ Q_strncpy( filename, g_Analysis.symbols.String( scriptFiles[ i ] ), sizeof( filename ) );
+ writer.AppendKeyValuesFile( &filename[ Q_strlen( gamedir ) ] );
+ }
+ EndPacifier();
+}
+
+void DescribeKV( int depth, KeyValues *parent, KeyValues *kv )
+{
+ switch ( kv->GetDataType() )
+ {
+ default:
+ case KeyValues::TYPE_NONE:
+ vprint( depth, "%s\n", kv->GetName() );
+ break;
+ case KeyValues::TYPE_STRING:
+ case KeyValues::TYPE_INT:
+ case KeyValues::TYPE_FLOAT:
+ case KeyValues::TYPE_PTR:
+ case KeyValues::TYPE_WSTRING:
+ case KeyValues::TYPE_COLOR:
+ {
+ vprint( depth, "%s = %s\n", kv->GetName(), kv->GetString() );
+ }
+ break;
+ }
+
+ // Describe children
+ for ( KeyValues *sub = kv->GetFirstSubKey(); sub; sub = sub->GetNextKey() )
+ {
+ DescribeKV( depth + 1, kv, sub );
+ }
+
+ // Then add peers
+ if ( !parent && kv->GetNextKey() )
+ {
+ DescribeKV( depth, NULL, kv->GetNextKey() );
+ }
+}
+
+void ExamineKVFile( char const *filename )
+{
+ int i;
+ CCompiledKeyValuesReader reader;
+
+ CFastTimer timer;
+ CFastTimer timer2;
+ CFastTimer timer3;
+
+ CUtlDict< KeyValues *, unsigned short > results;
+
+ timer.Start();
+
+ reader.LoadFile( filename );
+
+ timer.End();
+ timer2.Start();
+
+ // Now get the actual files
+ for ( i = reader.First(); i != reader.InvalidIndex(); i = reader.Next( i ) )
+ {
+ char fn[ 256 ];
+ reader.GetFileName( i, fn, sizeof( fn ) );
+ // Instance keyvalues
+ KeyValues *kv = reader.Instance( fn );
+ results.Insert( fn, kv );
+ }
+
+ timer2.End();
+
+ timer3.Start();
+
+ KeyValues *foo = new KeyValues( "bar" );
+
+ // Now get the actual files
+ for ( i = reader.First(); i != reader.InvalidIndex(); i = reader.Next( i ) )
+ {
+ foo->Clear();
+ char fn[ 256 ];
+ reader.GetFileName( i, fn, sizeof( fn ) );
+ // Instance keyvalues
+ reader.InstanceInPlace( *foo, fn );
+ }
+
+ foo->deleteThis();
+
+ timer3.End();
+ /*
+ for ( i = results.First(); i != results.InvalidIndex(); i = results.Next( i ) )
+ {
+ vprint( 1, "%s\n", results.GetElementName( i ) );
+ KeyValues *kv = results[ i ];
+ DescribeKV( 1, NULL, kv );
+ }
+ */
+ vprint( 0, "loading of %d elements took %.3f msec, create %.3f in-place %.3f\n",
+ results.Count(),
+ (float)timer.GetDuration().GetMillisecondsF(),
+ (float)timer2.GetDuration().GetMillisecondsF(),
+ (float)timer3.GetDuration().GetMillisecondsF());
+
+ CUtlVector< KeyValues * > test;
+ // Now load them the hard way
+ CFastTimer kvt;
+ kvt.Start();
+ for ( i = results.First(); i != results.InvalidIndex(); i = results.Next( i ) )
+ {
+ KeyValues *kv = new KeyValues( results.GetElementName( i ) );
+ kv->LoadFromFile( filesystem, results.GetElementName( i ) );
+ test.AddToTail( kv );
+ }
+
+ kvt.End();
+
+ // Clean up
+ int c = test.Count();
+ for ( i = 0; i < c; ++i )
+ {
+ test[ i ]->deleteThis();
+ }
+
+ for ( i = results.First(); i != results.InvalidIndex(); i = results.Next( i ) )
+ {
+ results[ i ]->deleteThis();
+ }
+
+ vprint( 0, "parsing of %d elements took %.3f msec\n", results.Count(), (float)kvt.GetDuration().GetMillisecondsF() );
+}
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+class CCompileKeyValuesApp : public CSteamAppSystemGroup
+{
+ typedef CSteamAppSystemGroup BaseClass;
+
+public:
+ // Methods of IApplication
+ virtual bool Create();
+ virtual bool PreInit();
+ virtual int Main();
+ virtual void PostShutdown();
+ virtual void Destroy();
+
+private:
+ // Sets up the search paths
+ bool SetupSearchPaths();
+};
+
+bool CCompileKeyValuesApp::Create()
+{
+ SpewOutputFunc( SpewFunc );
+ SpewActivate( "kvc", 2 );
+
+ AppSystemInfo_t appSystems[] =
+ {
+ { "", "" } // Required to terminate the list
+ };
+
+ if ( !AddSystems( appSystems ) )
+ return false;
+
+ g_pFullFileSystem = (IFileSystem*)FindSystem( FILESYSTEM_INTERFACE_VERSION );
+ g_pFileSystem = filesystem = g_pFullFileSystem;
+
+ if ( !filesystem )
+ {
+ Error("Unable to load required library interface!\n");
+ }
+
+ return true;
+}
+
+void CCompileKeyValuesApp::Destroy()
+{
+ g_pFullFileSystem = NULL;
+ g_pFileSystem = filesystem = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Sets up the game path
+//-----------------------------------------------------------------------------
+bool CCompileKeyValuesApp::SetupSearchPaths()
+{
+ if ( !BaseClass::SetupSearchPaths( NULL, false, true ) )
+ return false;
+
+ // Set gamedir.
+ Q_MakeAbsolutePath( gamedir, sizeof( gamedir ), GetGameInfoPath() );
+ Q_AppendSlash( gamedir, sizeof( gamedir ) );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Init, shutdown
+//-----------------------------------------------------------------------------
+bool CCompileKeyValuesApp::PreInit( )
+{
+// MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
+ g_pFullFileSystem->SetWarningFunc( Warning );
+
+ // Add paths...
+ if ( !SetupSearchPaths() )
+ return false;
+
+ return true;
+}
+
+void CCompileKeyValuesApp::PostShutdown()
+{
+}
+
+//-----------------------------------------------------------------------------
+// main application
+//-----------------------------------------------------------------------------
+int CCompileKeyValuesApp::Main()
+{
+ bool bOptPaintKit = false;
+
+ CUtlVector< CUtlSymbol > worklist;
+
+ int i;
+ for ( i=1 ; i<CommandLine()->ParmCount() ; i++ )
+ {
+ if ( CommandLine()->GetParm( i )[ 0 ] == '-' )
+ {
+ switch( CommandLine()->GetParm( i )[ 1 ] )
+ {
+ case 'l':
+ uselogfile = true;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ case 'g': // -game
+ ++i;
+ break;
+ case 't':
+ timing = true;
+ break;
+ case 'p':
+ bOptPaintKit = true;
+ break;
+ case 'f': // -f is valid when -p is specified
+ break;
+ default:
+ printusage();
+ break;
+ }
+ }
+ else if ( i != 0 )
+ {
+ CUtlSymbol sym;
+ sym = CommandLine()->GetParm( i );
+ worklist.AddToTail( sym );
+ }
+ }
+
+ if ( bOptPaintKit )
+ {
+ if ( CommandLine()->ParmCount() < 2 || ( i != CommandLine()->ParmCount() ) || ( worklist.Count() < 1 ) )
+ {
+ PrintHeader();
+ printusage(); // This exits app
+ }
+
+ ProcessPaintKitKeyValuesFiles( worklist );
+
+ return 0;
+ }
+
+ if ( CommandLine()->ParmCount() < 2 || ( i != CommandLine()->ParmCount() ) || worklist.Count() < 2 )
+ {
+ PrintHeader();
+ printusage();
+ }
+
+ CheckLogFile();
+
+ PrintHeader();
+
+ char binaries[MAX_PATH];
+ Q_strncpy( binaries, gamedir, MAX_PATH );
+ Q_StripTrailingSlash( binaries );
+ Q_strncat( binaries, "/../bin", MAX_PATH, MAX_PATH );
+
+ char outfile[ 512 ];
+ Q_strncpy( outfile, worklist[ worklist.Count() - 1 ].String() , sizeof( outfile ) );
+
+ g_pFullFileSystem->AddSearchPath( binaries, "EXECUTABLE_PATH");
+
+ vprint( 0, " Compiling keyvalues files...\n" );
+
+ CUtlVector< CUtlSymbol > diskfiles;
+
+ for ( i = 0; i < worklist.Count() - 1; ++i )
+ {
+ char workdir[ 256 ];
+ Q_snprintf( workdir, sizeof( workdir ), "%s", worklist[ i ].String() );
+
+ Q_StripTrailingSlash( workdir );
+
+ vprint( 0, "Processing '%s'\n", workdir );
+
+ int oldc = diskfiles.Count();
+ BuildFileListFromWildcard( diskfiles, workdir );
+ int added = diskfiles.Count() - oldc;
+ vprint( 0, "found %i files\n\n", added );
+ }
+
+ {
+ CCompiledKeyValuesWriter writer;
+ CompileKeyValuesFiles( diskfiles, writer );
+ vprint( 0, "Writing compiled file '%s'\n", outfile );
+ writer.WriteFile( outfile );
+ }
+
+ if ( timing )
+ {
+ ExamineKVFile( outfile );
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : argc -
+// argv[] -
+// Output : int
+//-----------------------------------------------------------------------------
+int main( int argc, char* argv[] )
+{
+ CommandLine()->CreateCmdLine( argc, argv );
+
+ CCompileKeyValuesApp kvCompiler;
+ CSteamApplication steamApplication( &kvCompiler );
+ int nRetVal = steamApplication.Run();
+
+ return nRetVal;
+} \ No newline at end of file
diff --git a/utils/kvc/kvc.vpc b/utils/kvc/kvc.vpc
new file mode 100644
index 0000000..4f4b162
--- /dev/null
+++ b/utils/kvc/kvc.vpc
@@ -0,0 +1,47 @@
+//-----------------------------------------------------------------------------
+// KVC.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_con_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "..\common;$BASE"
+ }
+}
+
+$Project "kvc"
+{
+ $Folder "Source Files"
+ {
+ $File "kvc.cpp"
+ $File "kvc_paintkit.cpp"
+ }
+
+ $Folder "Common Source Files"
+ {
+ $File "$SRCDIR\public\kevvaluescompiler.cpp"
+ $File "..\common\filesystem_tools.cpp"
+ $File "..\common\pacifier.cpp"
+ }
+
+ $Folder "Common Header Files"
+ {
+ $File "$SRCDIR\public\keyvaluescompiler.h"
+ $File "..\common\filesystem_tools.h"
+ $File "..\common\pacifier.h"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib appframework
+ $Lib tier2
+ }
+}
diff --git a/utils/kvc/kvc_paintkit.cpp b/utils/kvc/kvc_paintkit.cpp
new file mode 100644
index 0000000..967c82f
--- /dev/null
+++ b/utils/kvc/kvc_paintkit.cpp
@@ -0,0 +1,421 @@
+//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
+//
+//==================================================================================================
+
+
+#if defined( WIN32 ) && !defined( _X360 )
+#include <windows.h> // SRC only!!
+#endif //
+
+#include "filesystem.h"
+#include "tier0/icommandline.h"
+#include "tier1/keyvalues.h"
+#include "tier2/keyvaluesmacros.h"
+
+
+#include "kvc_paintkit.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//--------------------------------------------------------------------------------------------------
+//
+//--------------------------------------------------------------------------------------------------
+static void WriteIndent( IBaseFileSystem *pFileSystem, FileHandle_t hFile, bool bOptTabs, int nOptTabWidth, int nIndentLevel )
+{
+ if ( nIndentLevel <= 0 )
+ return;
+
+ if ( bOptTabs || nOptTabWidth <= 0 )
+ {
+ char *szTabIndent = ( char * )stackalloc( nIndentLevel + 1 );
+ V_memset( szTabIndent, '\t', nIndentLevel );
+ szTabIndent[nIndentLevel] = '\0';
+ pFileSystem->Write( szTabIndent, nIndentLevel, hFile );
+ }
+ else
+ {
+ const int nSpaceCount = nIndentLevel * nOptTabWidth;
+ char *szSpaceIndent = ( char * )stackalloc( nSpaceCount + 1 );
+ V_memset( szSpaceIndent, ' ', nSpaceCount );
+ szSpaceIndent[nSpaceCount] = '\0';
+ pFileSystem->Write( szSpaceIndent, nSpaceCount, hFile );
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------
+//
+//--------------------------------------------------------------------------------------------------
+static void WritePadding( IBaseFileSystem *pFileSystem, FileHandle_t hFile, bool bOptTabs, int nOptTabWidth, int nCurrentColumn, int nTargetColumn )
+{
+ if ( nCurrentColumn <= 0 || nTargetColumn <= 0 || nTargetColumn <= nCurrentColumn )
+ {
+ WriteIndent( pFileSystem, hFile, bOptTabs, nOptTabWidth, 1 );
+ return;
+ }
+
+ if ( bOptTabs || nOptTabWidth <= 0 )
+ {
+ if ( nOptTabWidth <= 0 )
+ {
+ nOptTabWidth = 4; // Can't handle 0 tab width, use 4
+ }
+
+ nTargetColumn = ( nTargetColumn + nOptTabWidth - 1 ) / nOptTabWidth * nOptTabWidth; // Round to next tab stop
+
+ Assert( nTargetColumn % nOptTabWidth == 0 );
+
+ const int nTabCount = ( nTargetColumn - nCurrentColumn ) / nOptTabWidth;
+
+ char *szTabPadding = ( char * )stackalloc( nTabCount + 1 );
+ V_memset( szTabPadding, '\t', nTabCount );
+ szTabPadding[nTabCount] = '\0';
+ pFileSystem->Write( szTabPadding, nTabCount, hFile );
+ }
+ else
+ {
+ nTargetColumn = ( nTargetColumn + nOptTabWidth - 1 ) / nOptTabWidth * nOptTabWidth; // Round to next tab stop
+
+ Assert( nTargetColumn % nOptTabWidth == 0 );
+
+ const int nSpaceCount = nTargetColumn - nCurrentColumn;
+ char *szSpacePadding = ( char * )stackalloc( nSpaceCount + 1 );
+ V_memset( szSpacePadding, ' ', nSpaceCount );
+ szSpacePadding[nSpaceCount] = '\0';
+ pFileSystem->Write( szSpacePadding, nSpaceCount, hFile );
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------
+//
+//--------------------------------------------------------------------------------------------------
+static void WriteConvertedString( IBaseFileSystem *pFileSystem, FileHandle_t hFile, const char *pszString )
+{
+ // handle double quote chars within the string
+ // the worst possible case is that the whole string is quotes
+ int nSrcLen = Q_strlen( pszString );
+ char *szConvertedString = ( char * )stackalloc( ( nSrcLen + 1 ) * sizeof( char ) * 2 );
+ int j = 0;
+ for ( int i = 0; i <= nSrcLen; ++i )
+ {
+ if ( pszString[i] == '\"' )
+ {
+ szConvertedString[j] = '\\';
+ ++j;
+ }
+ szConvertedString[j] = pszString[i];
+ ++j;
+ }
+
+ pFileSystem->Write( szConvertedString, V_strlen( szConvertedString ), hFile );
+}
+
+
+//--------------------------------------------------------------------------------------------------
+//
+//--------------------------------------------------------------------------------------------------
+template <size_t maxLenInChars> static int CleanFloatString( OUT_Z_ARRAY char (&pszFloatBuf)[maxLenInChars] )
+{
+ int nStrLen = V_strlen( pszFloatBuf );
+
+ const char *pDecimal = V_strnchr( pszFloatBuf, '.', maxLenInChars - 1 );
+ if ( pDecimal )
+ {
+ ++pDecimal; // Keep number after decimal always
+
+ char *pToStrip = pszFloatBuf + nStrLen - 1;
+ while ( pToStrip > pDecimal && *pToStrip == '0' )
+ {
+ *pToStrip = '\0';
+ --pToStrip;
+ --nStrLen;
+ }
+ }
+
+ return nStrLen;
+}
+
+
+//--------------------------------------------------------------------------------------------------
+//
+//--------------------------------------------------------------------------------------------------
+static void SaveToFile_R( KeyValues *pkv, IBaseFileSystem *pFileSystem, FileHandle_t hFile, bool bOptTabs, int nOptTabWidth, int nIndentLevel, int nValueColumn = -1 )
+{
+ WriteIndent( pFileSystem, hFile, bOptTabs, nOptTabWidth, nIndentLevel );
+ pFileSystem->Write( "\"", 1, hFile );
+ WriteConvertedString( pFileSystem, hFile, pkv->GetName() );
+ pFileSystem->Write( "\"", 1, hFile );
+
+ if ( pkv->GetFirstSubKey() )
+ {
+ int nMaxSubKeyNameLength = 0;
+
+ for ( KeyValues *pkvSubKey = pkv->GetFirstSubKey(); pkvSubKey; pkvSubKey = pkvSubKey->GetNextKey() )
+ {
+ const int nNameLength = V_strlen( pkvSubKey->GetName() );
+ nMaxSubKeyNameLength = Max( nNameLength, nMaxSubKeyNameLength );
+ }
+
+ int nSubKeyValueColumn = -1;
+
+ if ( nMaxSubKeyNameLength > 0 )
+ {
+ nSubKeyValueColumn = nMaxSubKeyNameLength + 1;
+ }
+
+ pFileSystem->Write( "\n", 1, hFile );
+ WriteIndent( pFileSystem, hFile, bOptTabs, nOptTabWidth, nIndentLevel );
+ pFileSystem->Write( "{\n", 2, hFile );
+ for ( KeyValues *pkvSubKey = pkv->GetFirstSubKey(); pkvSubKey; pkvSubKey = pkvSubKey->GetNextKey() )
+ {
+ SaveToFile_R( pkvSubKey, pFileSystem, hFile, bOptTabs, nOptTabWidth, nIndentLevel + 1, nSubKeyValueColumn );
+ }
+ WriteIndent( pFileSystem, hFile, bOptTabs, nOptTabWidth, nIndentLevel );
+ pFileSystem->Write( "}\n", 2, hFile );
+ }
+ else
+ {
+ if ( nValueColumn > 0 )
+ {
+ const int nNameLength = V_strlen( pkv->GetName() );
+ WritePadding( pFileSystem, hFile, bOptTabs, nOptTabWidth, nNameLength, nValueColumn );
+ }
+ else
+ {
+ WriteIndent( pFileSystem, hFile, bOptTabs, nOptTabWidth, 1 ); // Don't know which column to align things on
+ }
+
+ pFileSystem->Write( "\"", 1, hFile );
+
+ switch ( pkv->GetDataType() )
+ {
+ case KeyValues::TYPE_STRING:
+ {
+ const char *pszStringValue = pkv->GetString();
+ if ( pszStringValue )
+ {
+ WriteConvertedString( pFileSystem, hFile, pszStringValue );
+ }
+ break;
+ }
+ case KeyValues::TYPE_WSTRING:
+ {
+ const wchar_t *pszWString = pkv->GetWString();
+ if ( pszWString )
+ {
+ const size_t nTmpBufSize = 4096;
+ char *szTmpBuf = ( char * )stackalloc( nTmpBufSize * sizeof( char ) );
+ V_memset( szTmpBuf, '\0', nTmpBufSize );
+ const int nResult = Q_UnicodeToUTF8( pszWString, szTmpBuf, nTmpBufSize );
+ if ( nResult != 0 )
+ {
+ WriteConvertedString( pFileSystem, hFile, szTmpBuf );
+ }
+ }
+ break;
+ }
+ case KeyValues::TYPE_INT:
+ {
+ char szTmpBuf[32] = {};
+ V_snprintf( szTmpBuf, sizeof( szTmpBuf ), "%d", pkv->GetInt() );
+ pFileSystem->Write( szTmpBuf, V_strlen( szTmpBuf ), hFile );
+ break;
+ }
+ case KeyValues::TYPE_UINT64:
+ {
+ char szTmpBuf[32] = {};
+ // write "0x" + 16 char 0-padded hex encoded 64 bit value
+#ifdef WIN32
+ Q_snprintf( szTmpBuf, sizeof( szTmpBuf ), "0x%016I64X", *( ( uint64 * )pkv->GetUint64() ) );
+#else
+ Q_snprintf( szTmpBuf, sizeof( szTmpBuf ), "0x%016llX", *( ( uint64 * )pkv->GetUint64() ) );
+#endif
+ pFileSystem->Write( szTmpBuf, V_strlen( szTmpBuf ), hFile );
+ break;
+ }
+ case KeyValues::TYPE_FLOAT:
+ {
+ char szTmpBuf[32] = {};
+ V_snprintf( szTmpBuf, sizeof( szTmpBuf ), "%f", pkv->GetFloat() );
+ int nStrLen = V_strlen( szTmpBuf );
+ nStrLen = CleanFloatString( szTmpBuf );
+ pFileSystem->Write( szTmpBuf, nStrLen, hFile );
+ break;
+ }
+ case KeyValues::TYPE_COLOR:
+ {
+ char szTmpBuf[32] = {};
+ const Color c = pkv->GetColor();
+ V_snprintf( szTmpBuf, sizeof( szTmpBuf ), "%d", c.r() );
+ pFileSystem->Write( " ", 1, hFile );
+ V_snprintf( szTmpBuf, sizeof( szTmpBuf ), "%d", c.g() );
+ pFileSystem->Write( " ", 1, hFile );
+ V_snprintf( szTmpBuf, sizeof( szTmpBuf ), "%d", c.b() );
+ pFileSystem->Write( " ", 1, hFile );
+ V_snprintf( szTmpBuf, sizeof( szTmpBuf ), "%d", c.a() );
+ break;
+ }
+ default:
+ break;
+ }
+ pFileSystem->Write( "\"\n", 2, hFile );
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------
+//
+//--------------------------------------------------------------------------------------------------
+static bool SaveCleanKeyValuesToFile( KeyValues *pkv, IBaseFileSystem *pFileSystem, const char *pszFileName, const char *pszPathID = nullptr, bool bOptTabs = true, int nOptSpaceIndent = 4 )
+{
+ // Write out KeyValues to the specified file but cleaner than KeyValues::SaveToFile
+ // create a write file
+ FileHandle_t hFile = pFileSystem->Open( pszFileName, "wb", pszPathID );
+
+ if ( hFile == FILESYSTEM_INVALID_HANDLE )
+ {
+ Msg( "CleanSaveKeyValuesToFile: Couldn't open file \"%s\" for writing in path \"%s\".\n",
+ pszFileName ? pszFileName : "NULL", pszPathID ? pszPathID : "NULL" );
+ return false;
+ }
+
+ for ( KeyValues *pkvTmp = pkv; pkvTmp; pkvTmp = pkvTmp->GetNextKey() )
+ {
+ SaveToFile_R( pkvTmp, pFileSystem, hFile, bOptTabs, nOptSpaceIndent, 0 );
+ }
+
+ pFileSystem->Close( hFile );
+
+ return true;
+}
+
+
+//--------------------------------------------------------------------------------------------------
+//
+//--------------------------------------------------------------------------------------------------
+void ProcessPaintKitKeyValuesFile( const char *pszPaintKitKeyValuesFile, bool bOptFix, bool bOptVerbose )
+{
+ if ( bOptVerbose )
+ {
+ Msg( "Processing: %s\n", pszPaintKitKeyValuesFile );
+ }
+
+ char szFullPath[MAX_PATH] = {};
+
+ if ( !V_IsAbsolutePath( pszPaintKitKeyValuesFile ) )
+ {
+ g_pFullFileSystem->RelativePathToFullPath_safe( pszPaintKitKeyValuesFile, nullptr, szFullPath );
+ if ( !V_IsAbsolutePath( szFullPath ) )
+ {
+ char szTmpA[MAX_PATH] = {};
+ char szTmpB[MAX_PATH] = {};
+
+ if ( !g_pFullFileSystem->GetCurrentDirectoryA( szTmpA, ARRAYSIZE( szTmpB ) ) )
+ {
+ Msg( "Error: non-resolvable, non-absolute path specified (%s) and couldn't determine current directory\n", pszPaintKitKeyValuesFile );
+ return;
+ }
+
+ V_ComposeFileName( szTmpA, pszPaintKitKeyValuesFile, szTmpB, ARRAYSIZE( szTmpB ) );
+ V_FixupPathName( szTmpA, ARRAYSIZE( szTmpA ), szTmpB );
+ g_pFullFileSystem->GetCaseCorrectFullPath( szTmpB, szFullPath );
+ }
+ }
+
+ if ( !V_IsAbsolutePath( szFullPath ) )
+ {
+ Msg( "Error: Couldn't find absolute path to file: %s\n", pszPaintKitKeyValuesFile );
+ return;
+ }
+
+ if ( !g_pFullFileSystem->FileExists( szFullPath ) )
+ {
+ Msg( "Error: Couldn't find file: %s\n", szFullPath );
+ return;
+ }
+
+ KeyValues *pkv = new KeyValues( "" );
+ const bool bLoaded = pkv->LoadFromFile( g_pFullFileSystem, szFullPath );
+ if ( bLoaded )
+ {
+ for ( KeyValues *pkvTmp = pkv; pkvTmp; pkvTmp = pkvTmp->GetNextKey() )
+ {
+ HandleKeyValuesMacros( pkvTmp );
+ }
+
+ char szFilename[MAX_PATH] = {};
+
+ if ( bOptFix )
+ {
+ V_strcpy_safe( szFilename, szFullPath );
+ V_strcat_safe( szFilename, "_fix" );
+
+ if ( bOptVerbose )
+ {
+ Msg( " Saving: %s\n", szFilename );
+ }
+
+ if ( !SaveCleanKeyValuesToFile( pkv, g_pFullFileSystem, szFilename, nullptr, false ) )
+ {
+ Msg( "Error: Couldn't write: %s\n", szFilename );
+ }
+ }
+ else
+ {
+ V_strcpy_safe( szFilename, szFullPath );
+ V_strcat_safe( szFilename, "_bak" );
+
+ if ( bOptVerbose )
+ {
+ Msg( " Copying: %s -> %s\n", szFullPath, szFilename );
+ }
+
+ if ( CopyFile( szFullPath, szFilename, false ) )
+ {
+ if ( bOptVerbose )
+ {
+ Msg( " Saving: %s\n", szFullPath );
+ }
+
+ if ( !SaveCleanKeyValuesToFile( pkv, g_pFullFileSystem, szFullPath, nullptr, false ) )
+ {
+ Msg( "Error: Couldn't write: %s\n", szFullPath );
+ }
+ }
+ else
+ {
+ Msg( "Error: Couldn't copy %s to %s, aborting\n", szFullPath, szFilename );
+ }
+ }
+
+ pkv->SaveToFile( g_pFullFileSystem, "c:/tmp/bar.txt" );
+ }
+ else
+ {
+ Msg( " + Couldn't Load: %s\n", szFullPath );
+ }
+
+ pkv->deleteThis();
+}
+
+
+//--------------------------------------------------------------------------------------------------
+//
+//--------------------------------------------------------------------------------------------------
+void ProcessPaintKitKeyValuesFiles( const CUtlVector< CUtlSymbol > &workList )
+{
+ // This bit of hackery allows us to access files on the harddrive
+ g_pFullFileSystem->AddSearchPath( "", "LOCAL", PATH_ADD_TO_HEAD );
+
+ const bool bOptFix = CommandLine()->CheckParm( "-f" ) != nullptr;
+ const bool bOptVerbose = CommandLine()->CheckParm( "-v" ) != nullptr;
+
+ for ( int i = 0; i < workList.Count(); ++i )
+ {
+ ProcessPaintKitKeyValuesFile( workList[i].String(), bOptFix, bOptVerbose );
+ }
+} \ No newline at end of file
diff --git a/utils/kvc/kvc_paintkit.h b/utils/kvc/kvc_paintkit.h
new file mode 100644
index 0000000..ab8adf3
--- /dev/null
+++ b/utils/kvc/kvc_paintkit.h
@@ -0,0 +1,17 @@
+//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
+//
+//==================================================================================================
+
+
+#pragma once
+
+
+#include "tier1/utlvector.h"
+#include "tier1/utlsymbol.h"
+
+
+//--------------------------------------------------------------------------------------------------
+// Save KeyValues to a file with cleaner floats (only 1 trailing 0 after a decimal)
+// and values lined up in each block
+//--------------------------------------------------------------------------------------------------
+void ProcessPaintKitKeyValuesFiles( const CUtlVector< CUtlSymbol > &workList ); \ No newline at end of file