summaryrefslogtreecommitdiff
path: root/utils/mdlcheck
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/mdlcheck
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'utils/mdlcheck')
-rw-r--r--utils/mdlcheck/mdlcheck.cpp515
-rw-r--r--utils/mdlcheck/mdlcheck.vpc66
-rw-r--r--utils/mdlcheck/mdlcheck_util.cpp424
-rw-r--r--utils/mdlcheck/mdlcheck_util.h31
-rw-r--r--utils/mdlcheck/stdafx.cpp15
-rw-r--r--utils/mdlcheck/stdafx.h26
6 files changed, 1077 insertions, 0 deletions
diff --git a/utils/mdlcheck/mdlcheck.cpp b/utils/mdlcheck/mdlcheck.cpp
new file mode 100644
index 0000000..9808680
--- /dev/null
+++ b/utils/mdlcheck/mdlcheck.cpp
@@ -0,0 +1,515 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "stdafx.h"
+#include <stdio.h>
+#include <windows.h>
+#include "mdlcheck_util.h"
+#include "tier0/dbg.h"
+#include "utldict.h"
+#include "tier1/utlstring.h"
+
+bool uselogfile = false;
+bool verbose = false;
+bool checkani = false;
+
+struct QCFile
+{
+ char outputmodel[ MAX_PATH ];
+};
+
+struct ModelFile
+{
+ char qcfile[ MAX_PATH ];
+ int version;
+ bool needsrecompile;
+ int toobig;
+};
+
+struct AnalysisData
+{
+ CUtlDict< QCFile, int > files; // .qc to modelname lookup
+ CUtlDict< ModelFile, int > models; // .mdl to .qc/version lookup
+
+ CUtlSymbolTable symbols;
+};
+
+static AnalysisData g_Analysis;
+
+
+SpewRetval_t SpewFunc( SpewType_t type, char const *pMsg )
+{
+ printf( "%s", pMsg );
+ OutputDebugString( pMsg );
+
+ if ( type == SPEW_ERROR )
+ {
+ printf( "\n" );
+ OutputDebugString( "\n" );
+ }
+
+ return SPEW_CONTINUE;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void printusage( void )
+{
+ vprint( 0, "usage: mdlcheck <model source directory> <.mdl file directory>\n\
+ \t-v = verbose output\n\
+ \t-l = log to file log.txt\n\
+ \t-a = check for large animation data\n\
+ \ne.g.: mdlcheck -l u:/hl2/hl2/hl2models u:/hl2/hl2/models\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 ( ( ff = FindFirstFile( directory, &wfd ) ) == INVALID_HANDLE_VALUE )
+ return;
+
+ int extlen = strlen( extension );
+
+ do
+ {
+ 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 ( strstr( wfd.cFileName, ".360." ) )
+ {
+ }
+ else 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 );
+ }
+ }
+ }
+ } while ( FindNextFile( ff, &wfd ) );
+}
+
+void BuildFileList( CUtlVector< CUtlSymbol >& files, char const *rootdir, char const *extension )
+{
+ files.RemoveAll();
+ BuildFileList_R( files, rootdir, extension );
+}
+
+//-----------------------------------------------------------------------------
+// This is here because scriplib.cpp is included in this project but cmdlib.cpp
+// is not, but scriplib.cpp uses some stuff from cmdlib.cpp, same with
+// LoadFile and ExpandPath above. The only thing that currently uses this
+// is $include in scriptlib, if this function returns 0, $include will
+// behave the way it did before this change
+//-----------------------------------------------------------------------------
+int CmdLib_ExpandWithBasePaths( CUtlVector< CUtlString > &expandedPathList, const char *pszPath )
+{
+ return 0;
+}
+
+
+bool GetModelNameFromSourceFile( char const *filename, char *modelname, int maxlen )
+{
+ modelname[0]=0;
+
+ int filelength;
+ char *buffer = (char *)COM_LoadFile( filename, &filelength );
+ if ( !buffer )
+ {
+ vprint( 0, "Couldn't load %s\n", filename );
+ return false;
+ }
+
+ bool valid = false;
+
+ // Parse tokens
+ char *current = buffer;
+ while ( current )
+ {
+ current = CC_ParseToken( current );
+ if ( strlen( com_token ) <= 0 )
+ break;
+
+ if ( stricmp( com_token, "$modelname" ) )
+ continue;
+
+ current = CC_ParseToken( current );
+
+ strcpy( modelname, com_token );
+ _strlwr( modelname );
+
+ Q_FixSlashes( modelname );
+
+ Q_DefaultExtension( modelname, ".mdl", maxlen );
+
+ valid = true;
+ break;
+ }
+
+ COM_FreeFile( (unsigned char *)buffer );
+
+ if ( !valid )
+ {
+ vprint_queued( 0, ".qc file %s missing $modelname directive!!!\n", filename );
+ }
+ return valid;
+}
+
+bool AddModelNameFromSource( CUtlDict< ModelFile, int >& models, char const *filename, char const *modelname, int offset )
+{
+ int idx = models.Find( modelname );
+ if ( idx != models.InvalidIndex() )
+ {
+ char shortname[ MAX_PATH ];
+ char shortname2[ MAX_PATH ];
+ strcpy( shortname, &filename[ offset ] );
+ strcpy( shortname2, &models[ idx ].qcfile[ offset ] );
+
+ vprint_queued( 0, "multiple .qc's build %s\n %s\n %s\n",
+ modelname,
+ shortname,
+ shortname2 );
+ return false;
+ }
+
+ ModelFile mf;
+ strcpy( mf.qcfile, filename );
+ _strlwr( mf.qcfile );
+ mf.version = 0;
+
+ models.Insert( modelname, mf );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *sourcetreebase -
+// *subdir -
+// *baseentityclass -
+//-----------------------------------------------------------------------------
+void ProcessSourceDirectory( char const *basedir )
+{
+ // vprint( 0, "building .qc list\n" );
+
+ CUtlVector< CUtlSymbol > files;
+
+ BuildFileList( files, basedir, ".qc" );
+
+ // vprint( 0, "found %i .qc files\n\n", files.Count() );
+
+ int offset = strlen( basedir ) + 1;
+
+ // Add files to QC Files dictionary
+ int c = files.Count();
+ for ( int i = 0; i < c; i++ )
+ {
+ QCFile qcf;
+ memset( &qcf, 0, sizeof( qcf ) );
+ CUtlSymbol& sym = files[ i ];
+ g_Analysis.files.Insert( g_Analysis.symbols.String( sym ), qcf );
+ }
+
+ vprint_queued( 0, "%s", "\n\n" );
+
+ // Now iterate .qc files, looking into each to find the output model name
+ c = g_Analysis.files.Count();
+ int valid = 0;
+ for ( int i = 0; i < c; i++ )
+ {
+ char modelname[ 256 ];
+ char const *filename = g_Analysis.files.GetElementName( i );
+ if ( verbose )
+ {
+ vprint( 0, "checking %i: %s\n", i, filename );
+ }
+ if ( GetModelNameFromSourceFile( filename, modelname, sizeof( modelname ) ) )
+ {
+ if ( AddModelNameFromSource( g_Analysis.models, filename, modelname, offset ) )
+ {
+ valid++;
+ }
+ }
+ }
+
+ int ecount = c - valid;
+ if (ecount != 0)
+ {
+ // vprint( 0, "\n summary: found %i/%i (%.2f percent) .qc errors\n\n", ecount, c, 100.0 * ecount / max( c, 1 ) );
+ vprint( 0, "\n summary: found %i .qc errors\n\n", ecount );
+ }
+}
+
+#include "studio.h"
+
+#define IDSTUDIOHEADER (('T'<<24)+('S'<<16)+('D'<<8)+'I')
+ // little-endian "IDST"
+#define IDSTUDIOANIMGROUPHEADER (('G'<<24)+('A'<<16)+('D'<<8)+'I')
+ // little-endian "IDAG"
+
+
+byte buffer[1024*1024*4];
+bool ValidateModelFile( char const *modelname, int offset )
+{
+ studiohdr_t *pHdr;
+ FILE *fp;
+
+ pHdr = (studiohdr_t *)buffer;
+
+ fp = fopen( modelname, "rb" );
+ if ( !fp )
+ {
+ vprint_queued( 0, "Unable to open .mdl file %s\n", modelname );
+ return false;
+ }
+
+ // See if there's a .qc which builds this model
+ char shortname[ MAX_PATH ];
+ strcpy( shortname, &modelname[ offset ] );
+
+ Q_FixSlashes( shortname );
+
+ fread( buffer, sizeof( buffer ), 1, fp );
+ fclose( fp );
+
+ if ( pHdr->id != IDSTUDIOHEADER )
+ {
+ vprint_queued( 0, "Bogus studiomdl header for %s, expecting 'IDST' four cc code\n", shortname );
+ return false;
+ }
+
+ bool valid = true;
+ bool needsrecompile = false;
+ int toobig = 0;
+
+ // previous version is compatible
+ if ( pHdr->version < 44 || pHdr->version > STUDIO_VERSION )
+ {
+ vprint_queued( 0, "Outdated model %s (ver %i != %i)\n", shortname, pHdr->version, STUDIO_VERSION );
+ valid = false;
+ }
+
+ if (!Studio_ConvertStudioHdrToNewVersion( pHdr ))
+ {
+ // vprint( 0, "%s needs to be recompiled\n", pHdr->pszName() );
+ needsrecompile = true;
+ }
+
+ if (checkani)
+ {
+ // HACK: since the sequence data is written after the animation data, this is rough way to determine how much anim data there really is
+ int totalanimsize = pHdr->localseqindex - pHdr->localanimindex - pHdr->numlocalanim * sizeof( mstudioanimdesc_t );
+ if (pHdr->pLocalAnimdesc( 0 )->animblock == 0 && totalanimsize > 1024 * 64)
+ {
+ toobig = totalanimsize;
+ }
+ }
+
+ int idx = g_Analysis.models.Find( shortname );
+ if ( idx == g_Analysis.models.InvalidIndex() )
+ {
+ vprint_queued( 0, "Couldn't find a .qc which builds %s\n", shortname );
+ valid = false;
+ }
+ else
+ {
+ g_Analysis.models[idx].version = pHdr->version;
+ g_Analysis.models[idx].needsrecompile = needsrecompile;
+ g_Analysis.models[idx].toobig = toobig;
+ }
+
+
+ return valid;
+}
+
+void ProcessModelsDirectory( char const *basedir )
+{
+ // vprint( 0, "building .mdl list\n" );
+
+ CUtlVector< CUtlSymbol > models;
+
+ BuildFileList( models, basedir, ".mdl" );
+
+ // vprint( 0, "found %i .mdl files\n\n", models.Count() );
+
+ int offset = strlen( basedir ) + 1;
+
+ // Now iterate model files and check version tag and whether a .qc exists which builds the .mdl
+
+ vprint_queued( 0, "%s", "\n\n" );
+
+ // Add files to QC Files dictionary
+ int c = models.Count();
+ int valid = 0;
+ for ( int i = 0; i < c; i++ )
+ {
+ QCFile qcf;
+ memset( &qcf, 0, sizeof( qcf ) );
+ CUtlSymbol& sym = models[ i ];
+
+ char const *modelname = g_Analysis.symbols.String( sym );
+
+ if ( verbose )
+ {
+ vprint( 0, "checking %i .mdl %s\n", i, modelname );
+ }
+
+ if ( ValidateModelFile( modelname, offset ) )
+ {
+ valid++;
+ }
+ }
+
+ int ecount = c - valid;
+ if (ecount != 0)
+ {
+ // vprint( 0, "\n summary: found %i/%i (%.2f percent) .mdl errors\n", ecount, c, 100.0 * ecount / max( c, 1 ) );
+ vprint( 0, "\n summary: found %i .mdl errors\n", ecount );
+ }
+}
+
+
+
+void CheckForUnbuiltModels( )
+{
+ vprint_queued( 0, "%s", "\n\n" );
+
+ int c = g_Analysis.models.Count();
+ int valid = 0;
+ for ( int i = 0; i < c; i++ )
+ {
+ if (g_Analysis.models[i].version == 0)
+ {
+ vprint_queued( 0, "Can't find %s,\n\tbuilt by %s\n", g_Analysis.models.GetElementName( i ), g_Analysis.models[i].qcfile );
+ }
+ else if (g_Analysis.models[i].needsrecompile)
+ {
+ vprint_queued( 0, "%s out of date,\n\tbuilt by %s\n", g_Analysis.models.GetElementName( i ), g_Analysis.models[i].qcfile );
+ }
+ else if (g_Analysis.models[i].toobig)
+ {
+ vprint_queued( 0, "%s needs $animblocksize command (%d of animdata),\n\tbuilt by %s\n", g_Analysis.models.GetElementName( i ), g_Analysis.models[i].toobig, g_Analysis.models[i].qcfile );
+ }
+ else
+ {
+ valid++;
+ }
+ }
+
+ int ecount = c - valid;
+ if (ecount != 0)
+ {
+ // vprint( 0, "\n summary: found %i/%i (%.2f percent) missing .mdl's\n", ecount, c, 100.0 * ecount / max( c, 1 ) );
+ vprint( 0, "\n summary: found %i missing .mdl's\n", ecount );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CheckLogFile( void )
+{
+ if ( uselogfile )
+ {
+ _unlink( "log.txt" );
+ vprint( 0, " Outputting to log.txt\n" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : argc -
+// argv[] -
+// Output : int
+//-----------------------------------------------------------------------------
+int main( int argc, char* argv[] )
+{
+ SpewOutputFunc( SpewFunc );
+
+ int i = 1;
+ for (i ; i<argc ; i++)
+ {
+ if ( argv[ i ][ 0 ] == '-' )
+ {
+ switch( argv[ i ][ 1 ] )
+ {
+ case 'l':
+ uselogfile = true;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ case 'a':
+ checkani = true;
+ break;
+ default:
+ printusage();
+ break;
+ }
+ }
+ }
+
+ vprint( 0, "Valve Software - mdlcheck.exe (%s)\n", __DATE__ );
+ vprint( 0, "--- Source Model Consistency Checker ---\n" );
+
+ if ( argc < 3 || ( i != argc ) )
+ {
+ printusage();
+ }
+
+ CheckLogFile();
+
+ char modelsources[ 256 ];
+ strcpy( modelsources, argv[ i - 2 ] );
+ char modelsdir[ 256 ];
+ strcpy( modelsdir, argv[ i - 1 ] );
+
+ if ( !strstr( modelsdir, "models" ) )
+ {
+ vprint( 0, "Models dir %s looks invalid (format: u:/tf2/hl2/models)\n", modelsdir );
+ return 0;
+ }
+
+ Q_StripTrailingSlash( modelsources );
+ Q_StripTrailingSlash( modelsdir );
+
+ ProcessSourceDirectory( modelsources );
+ ProcessModelsDirectory( modelsdir );
+ CheckForUnbuiltModels( );
+
+ dump_print_queue( );
+
+ return 0;
+}
diff --git a/utils/mdlcheck/mdlcheck.vpc b/utils/mdlcheck/mdlcheck.vpc
new file mode 100644
index 0000000..ad8dd1a
--- /dev/null
+++ b/utils/mdlcheck/mdlcheck.vpc
@@ -0,0 +1,66 @@
+//-----------------------------------------------------------------------------
+// MDLCHECK.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_con_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "$BASE,..\common"
+ $Create/UsePrecompiledHeader "Use Precompiled Header (/Yu)"
+ $PrecompiledHeaderFile "Debug/mdlcheck.pch"
+ }
+}
+
+$Project "Mdlcheck"
+{
+ $Folder "Source Files"
+ {
+ -$File "$SRCDIR\public\tier0\memoverride.cpp"
+ $File "mdlcheck.cpp"
+ $File "mdlcheck_util.cpp"
+ $File "StdAfx.cpp"
+ {
+ $Configuration
+ {
+ $Compiler
+ {
+ $Create/UsePrecompiledHeader "Create Precompiled Header (/Yc)"
+ }
+ }
+ }
+ }
+
+ $Folder "Header Files"
+ {
+ $File "$SRCDIR\public\tier0\basetypes.h"
+ $File "$SRCDIR\public\tier0\commonmacros.h"
+ $File "$SRCDIR\public\tier0\dbg.h"
+ $File "$SRCDIR\public\tier0\fasttimer.h"
+ $File "$SRCDIR\public\mathlib\mathlib.h"
+ $File "mdlcheck_util.h"
+ $File "$SRCDIR\public\tier0\memdbgon.h"
+ $File "$SRCDIR\public\tier0\platform.h"
+ $File "$SRCDIR\public\tier0\protected_things.h"
+ $File "StdAfx.h"
+ $File "$SRCDIR\public\string_t.h"
+ $File "$SRCDIR\public\tier1\strtools.h"
+ $File "$SRCDIR\public\studio.h"
+ $File "$SRCDIR\public\tier1\utldict.h"
+ $File "$SRCDIR\public\tier1\utlmemory.h"
+ $File "$SRCDIR\public\tier1\utlrbtree.h"
+ $File "$SRCDIR\public\tier1\utlsymbol.h"
+ $File "$SRCDIR\public\tier1\utlvector.h"
+ $File "$SRCDIR\public\mathlib\vector.h"
+ $File "$SRCDIR\public\mathlib\vector2d.h"
+ $File "$SRCDIR\public\mathlib\vector4d.h"
+ $File "$SRCDIR\public\vstdlib\vstdlib.h"
+ }
+}
diff --git a/utils/mdlcheck/mdlcheck_util.cpp b/utils/mdlcheck/mdlcheck_util.cpp
new file mode 100644
index 0000000..660c4ea
--- /dev/null
+++ b/utils/mdlcheck/mdlcheck_util.cpp
@@ -0,0 +1,424 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "stdafx.h"
+#include <io.h>
+#include <stdio.h>
+#include <windows.h>
+#include "mdlcheck_util.h"
+#include "tier0/dbg.h"
+#include "utlvector.h"
+
+extern bool uselogfile;
+
+//-----------------------------------------------------------------------------
+// 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 );
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : depth -
+// *fmt -
+// ... -
+//-----------------------------------------------------------------------------
+CUtlVector< char * > printQueue;
+
+void vprint_queued( int depth, const char *fmt, ... )
+{
+ char string[ 8192 ];
+ strnset( string, ' ', depth * 2 );
+
+ va_list va;
+ va_start( va, fmt );
+ vsprintf( &string[depth * 2], fmt, va );
+ va_end( va );
+
+ printQueue.AddToTail( strdup( string ) );
+}
+
+void dump_print_queue( )
+{
+ FILE *fp = NULL;
+
+ if ( uselogfile )
+ {
+ fp = fopen( "log.txt", "ab" );
+ }
+
+ for (int i = 0; i < printQueue.Count(); i++)
+ {
+ ::printf( "%s", printQueue[i] );
+ OutputDebugString( printQueue[i] );
+
+ if ( fp )
+ {
+ char *p = printQueue[i];
+ while ( *p )
+ {
+ if ( *p == '\n' )
+ {
+ fputc( '\r', fp );
+ }
+ fputc( *p, fp );
+ p++;
+ }
+ }
+ }
+ if (fp)
+ {
+ fclose( fp );
+ }
+ printQueue.RemoveAll();
+}
+
+
+
+bool com_ignorecolons = false; // YWB: Ignore colons as token separators in COM_Parse
+bool com_ignoreinlinecomment = false;
+static bool s_com_token_unget = false;
+char com_token[1024];
+int linesprocessed = 0;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CC_UngetToken( void )
+{
+ s_com_token_unget = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : ch -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CC_IsBreakChar( char ch )
+{
+ bool brk = false;
+ switch ( ch )
+ {
+ case '{':
+ case '}':
+ case ')':
+ case '(':
+ case '[':
+ case ']':
+// case '\'':
+// case '/':
+ case ',':
+ case ';':
+ case '<':
+ case '>':
+ brk = true;
+ break;
+
+ case ':':
+ if ( !com_ignorecolons )
+ brk = true;
+ break;
+ default:
+ break;
+ }
+ return brk;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *data -
+// Output : char
+//-----------------------------------------------------------------------------
+char *CC_ParseToken(char *data)
+{
+ int c;
+ int len;
+
+ if ( s_com_token_unget )
+ {
+ s_com_token_unget = false;
+ return data;
+ }
+
+ len = 0;
+ com_token[0] = 0;
+
+ if (!data)
+ return NULL;
+
+// skip whitespace
+skipwhite:
+ while ( (c = *data) <= ' ')
+ {
+ if (c == 0)
+ return NULL; // end of file;
+ if ( c== '\n' )
+ {
+ linesprocessed++;
+ }
+ data++;
+ }
+
+// skip // comments
+ if ( !com_ignoreinlinecomment )
+ {
+ if (c=='/' && data[1] == '/')
+ {
+ while (*data && *data != '\n')
+ data++;
+ goto skipwhite;
+ }
+ }
+
+ if ( c == '/' && data[1] == '*' )
+ {
+ while (data[0] && data[1] && !( data[0] == '*' && data[1] == '/' ) )
+ {
+ if ( *data == '\n' )
+ {
+ linesprocessed++;
+ }
+ data++;
+ }
+
+ if ( data[0] == '*' && data[1] == '/' )
+ {
+ data+=2;
+ }
+ goto skipwhite;
+ }
+
+// handle quoted strings specially
+ bool isLstring = data[0] == 'L' && (data[1] == '\"' );
+ if ( isLstring )
+ {
+ com_token[len++] = (char)c;
+ return data+1;
+ }
+
+ if ( c == '\"' )
+ {
+ data++;
+ bool bEscapeSequence = false;
+ while (1)
+ {
+ Assert( len < 1024 );
+ c = *data++;
+ if ( (c=='\"' && !bEscapeSequence) || !c && len < sizeof( com_token ) - 1 )
+ {
+ com_token[len] = 0;
+ return data;
+ }
+ bEscapeSequence = ( c == '\\' );
+ com_token[len] = (char)c;
+ len++;
+ }
+ }
+
+// parse single characters
+ if ( CC_IsBreakChar( (char)c ) )
+ {
+ Assert( len < 1024 );
+
+ com_token[len] = (char)c;
+ len++;
+ com_token[len] = 0;
+ return data+1;
+ }
+
+// parse a regular word
+ do
+ {
+ Assert( len < 1024 );
+
+ com_token[len] = (char)c;
+ data++;
+ len++;
+ c = *data;
+
+ if ( CC_IsBreakChar( (char)c ) )
+ break;
+ } while (c>32 && len < sizeof( com_token ) - 1);
+
+ com_token[len] = 0;
+ return data;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *name -
+// *len -
+// Output : unsigned char
+//-----------------------------------------------------------------------------
+unsigned char *COM_LoadFile( const char *name, int *len)
+{
+ FILE *fp;
+ fp = fopen( name, "rb" );
+ if ( !fp )
+ {
+ *len = 0;
+ return NULL;
+ }
+
+ fseek( fp, 0, SEEK_END );
+ *len = ftell( fp );
+ fseek( fp, 0, SEEK_SET );
+
+ unsigned char *buffer = new unsigned char[ *len + 1 ];
+ fread( buffer, *len, 1, fp );
+ fclose( fp );
+ buffer[ *len ] = 0;
+
+ return buffer;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *buffer -
+//-----------------------------------------------------------------------------
+void COM_FreeFile( unsigned char *buffer )
+{
+ delete[] buffer;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *dir -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool COM_DirectoryExists( const char *dir )
+{
+ if ( !_access( dir, 0 ) )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *input -
+// Output : char
+//-----------------------------------------------------------------------------
+char *CC_ParseUntilEndOfLine( char *input )
+{
+ while (*input && *input != '\n')
+ input++;
+
+ return input;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *input -
+// *ch -
+// *breakchar -
+// Output : char
+//-----------------------------------------------------------------------------
+char *CC_RawParseChar( char *input, const char *ch, char *breakchar )
+{
+ bool done = false;
+ int listlen = strlen( ch );
+
+ do
+ {
+ input = CC_ParseToken( input );
+ if ( strlen( com_token ) <= 0 )
+ break;
+
+ if ( strlen( com_token ) == 1 )
+ {
+ for ( int i = 0; i < listlen; i++ )
+ {
+ if ( com_token[ 0 ] == ch[ i ] )
+ {
+ *breakchar = ch [ i ];
+ done = true;
+ break;
+ }
+ }
+ }
+ } while ( !done );
+
+ return input;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *input -
+// *pairing -
+// Output : char
+//-----------------------------------------------------------------------------
+char *CC_DiscardUntilMatchingCharIncludingNesting( char *input, const char *pairing )
+{
+ int nestcount = 1;
+
+ do
+ {
+ input = CC_ParseToken( input );
+ if ( strlen( com_token ) <= 0 )
+ break;
+
+ if ( strlen( com_token ) == 1 )
+ {
+ if ( com_token[ 0 ] == pairing[ 0 ] )
+ {
+ nestcount++;
+ }
+ else if ( com_token[ 0 ] == pairing[ 1 ] )
+ {
+ nestcount--;
+ }
+ }
+ } while ( nestcount != 0 );
+
+ return input;
+} \ No newline at end of file
diff --git a/utils/mdlcheck/mdlcheck_util.h b/utils/mdlcheck/mdlcheck_util.h
new file mode 100644
index 0000000..da2f0d8
--- /dev/null
+++ b/utils/mdlcheck/mdlcheck_util.h
@@ -0,0 +1,31 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#ifndef CLASSCHECK_UTIL_H
+#define CLASSCHECK_UTIL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+void vprint( int depth, const char *fmt, ... );
+void vprint_queued( int depth, const char *fmt, ... );
+void dump_print_queue( void );
+void CC_UngetToken( void );
+char *CC_ParseToken(char *data);
+char *CC_DiscardUntilMatchingCharIncludingNesting( char *input, const char *pairing );
+char *CC_RawParseChar( char *input, const char *ch, char *breakchar );
+char *CC_ParseUntilEndOfLine( char *input );
+
+extern char com_token[1024];
+extern bool com_ignoreinlinecomment;
+extern int linesprocessed;
+
+unsigned char *COM_LoadFile( const char *name, int *len);
+void COM_FreeFile( unsigned char *buffer );
+bool COM_DirectoryExists( const char *dir );
+
+#endif // CLASSCHECK_UTIL_H
diff --git a/utils/mdlcheck/stdafx.cpp b/utils/mdlcheck/stdafx.cpp
new file mode 100644
index 0000000..1fdf9da
--- /dev/null
+++ b/utils/mdlcheck/stdafx.cpp
@@ -0,0 +1,15 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// stdafx.cpp : source file that includes just the standard includes
+// classcheck.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/utils/mdlcheck/stdafx.h b/utils/mdlcheck/stdafx.h
new file mode 100644
index 0000000..e0ae61a
--- /dev/null
+++ b/utils/mdlcheck/stdafx.h
@@ -0,0 +1,26 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#if !defined(AFX_STDAFX_H__50E4147E_A508_4D85_BF0A_BA26676063F0__INCLUDED_)
+#define AFX_STDAFX_H__50E4147E_A508_4D85_BF0A_BA26676063F0__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+
+// TODO: reference additional headers your program requires here
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__50E4147E_A508_4D85_BF0A_BA26676063F0__INCLUDED_)