diff options
Diffstat (limited to 'utils/kvc')
| -rw-r--r-- | utils/kvc/cbase.h | 19 | ||||
| -rw-r--r-- | utils/kvc/kvc.cpp | 710 | ||||
| -rw-r--r-- | utils/kvc/kvc.vpc | 47 | ||||
| -rw-r--r-- | utils/kvc/kvc_paintkit.cpp | 421 | ||||
| -rw-r--r-- | utils/kvc/kvc_paintkit.h | 17 |
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 |