mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-07 17:29:36 +00:00
1
This commit is contained in:
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
Reference in New Issue
Block a user