|
|
//========= 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 "choreoscene.h"
#include "choreoevent.h"
#include "iscenetokenprocessor.h"
#include "tier1/utlbuffer.h"
#include "tier1/checksum_crc.h"
#include "pacifier.h"
#include "SceneCache.h"
#include "SoundEmitterSystem/isoundemittersystembase.h"
#include "ModelSoundsCache.h"
#include "Datacache/imdlcache.h"
#include "datacache/idatacache.h"
#include "studio.h"
#include "appframework/appframework.h"
#include "tier0/icommandline.h"
#include "istudiorender.h"
#include "materialsystem/imaterialsystem.h"
#include "vphysics_interface.h"
#include "icvar.h"
#include "vstdlib/cvar.h"
#include "eventlist.h"
// #define TESTING 1
bool uselogfile = false; bool modelsoundscache = false; bool scenecache = false; bool virtualmodel = false; bool buildxcds = false;
struct AnalysisData { CUtlSymbolTable symbols; };
static AnalysisData g_Analysis;
static char g_szCurrentGameDir[ 512 ];
IFileSystem *filesystem = NULL; IMDLCache *g_pMDLCache = NULL; ISoundEmitterSystemBase *soundemitterbase = NULL;
static CUniformRandomStream g_Random; IUniformRandomStream *random = &g_Random;
static bool spewed = false;
static CUtlCachedFileData< CSceneCache > g_SceneCache( "scene.cache", SCENECACHE_VERSION, 0, UTL_CACHED_FILE_USE_FILESIZE ); static CUtlCachedFileData< CModelSoundsCache > g_ModelSoundsCache( "modelsounds.cache", MODELSOUNDSCACHE_VERSION, 0, UTL_CACHED_FILE_USE_FILESIZE );
//-----------------------------------------------------------------------------
// FIXME: This trashy glue code is really not acceptable. Figure out a way of making it unnecessary.
//-----------------------------------------------------------------------------
const studiohdr_t *studiohdr_t::FindModel( void **cache, char const *pModelName ) const { MDLHandle_t handle = g_pMDLCache->FindMDL( pModelName ); *cache = (void*)handle; return g_pMDLCache->GetStudioHdr( handle ); }
virtualmodel_t *studiohdr_t::GetVirtualModel( void ) const { return g_pMDLCache->GetVirtualModel( (MDLHandle_t)virtualModel ); }
byte *studiohdr_t::GetAnimBlock( int i ) const { return g_pMDLCache->GetAnimBlock( (MDLHandle_t)virtualModel, i ); }
int studiohdr_t::GetAutoplayList( unsigned short **pOut ) const { return g_pMDLCache->GetAutoplayList( (MDLHandle_t)virtualModel, pOut ); }
const studiohdr_t *virtualgroup_t::GetStudioHdr( void ) const { return g_pMDLCache->GetStudioHdr( (MDLHandle_t)cache ); }
//-----------------------------------------------------------------------------
// Purpose: Helper for parsing scene data file
//-----------------------------------------------------------------------------
class CSceneTokenProcessor : public ISceneTokenProcessor { public: const char *CurrentToken( void ); bool GetToken( bool crossline ); bool TokenAvailable( void ); void Error( const char *fmt, ... ); };
//-----------------------------------------------------------------------------
// Purpose:
// Output : const char
//-----------------------------------------------------------------------------
const char *CSceneTokenProcessor::CurrentToken( void ) { return token; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : crossline -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CSceneTokenProcessor::GetToken( bool crossline ) { return ::GetToken( crossline ) ? true : false; }
//-----------------------------------------------------------------------------
// Purpose:
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CSceneTokenProcessor::TokenAvailable( void ) { return ::TokenAvailable() ? true : false; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *fmt -
// ... -
//-----------------------------------------------------------------------------
void CSceneTokenProcessor::Error( const char *fmt, ... ) { char string[ 2048 ]; va_list argptr; va_start( argptr, fmt ); Q_vsnprintf( string, sizeof(string), fmt, argptr ); va_end( argptr );
Warning( "%s", string ); Assert(0); }
static CSceneTokenProcessor g_TokenProcessor;
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: makexvcd [options] -game gamedir\n\
\t-v = verbose output\n\ \t-m = rebuild modelsounds.cache\n\ \t-x = rebuild .xcd files\n\ \t-s = rebuild scene.cache\n\ \t-z = rebuild virtualmodel.cache (xbox only)\n\ \t-l = log to file log.txt\n\ \ne.g.: makexvcd -s -m -game episodic\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;
Q_snprintf( directory, sizeof( 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
Q_snprintf( filename, sizeof( 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( char const *basedir, CUtlVector< CUtlSymbol >& files, char const *rootdir, char const *extension ) { files.RemoveAll(); char root[ 512 ]; Q_snprintf( root, sizeof( root ), "%s\\%s", basedir, rootdir ); BuildFileList_R( files, root, extension ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CheckLogFile( void ) { if ( uselogfile ) { _unlink( "log.txt" ); vprint( 0, " Outputting to log.txt\n" ); } }
void PrintHeader() { vprint( 0, "Valve Software - CompileVCD.exe (%s)\n", __DATE__ ); vprint( 0, "--- Binary .vcd compiler ---\n" ); }
//-----------------------------------------------------------------------------
// Purpose: For each .wav file in the list, see if any vcd file in the list references it
// First build an index of .wav to .vcd mappings, then search wav list and print results
// Input : vcdfiles -
// wavfiles -
//-----------------------------------------------------------------------------
struct VCDList { VCDList() { }
VCDList( const VCDList& src ) { int c = src.vcds.Count(); for ( int i = 0 ; i < c; i++ ) { vcds.AddToTail( src.vcds[ i ] ); } }
VCDList& operator =( const VCDList& src ) { if ( this == &src ) return *this;
int c = src.vcds.Count(); for ( int i = 0 ; i < c; i++ ) { vcds.AddToTail( src.vcds[ i ] ); }
return *this; }
CUtlVector< CUtlSymbol > vcds; };
void AppendDisposition( CUtlVector< CUtlSymbol >& disposition, char const *fmt, ... ) { char string[ 2048 ]; va_list argptr; va_start( argptr, fmt ); _vsnprintf( string, sizeof( string ), fmt, argptr ); va_end( argptr );
CUtlSymbol sym; sym = string; disposition.AddToTail( sym ); }
CChoreoScene *BlockingLoadScene( char const *vcdname ) { // Load the .vcd
char scenefile[ MAX_PATH ]; Q_snprintf( scenefile, sizeof( scenefile ), "%s\\%s", g_szCurrentGameDir, vcdname );
LoadScriptFile( scenefile ); CChoreoScene *scene = ChoreoLoadScene( scenefile, NULL, &g_TokenProcessor, Con_Printf ); return scene; }
void ProcessVCD( char const *vcdname, CUtlVector< CUtlSymbol >& disposition ) { if ( verbose ) { vprint( 0, "Processing '%s'\n", vcdname ); }
bool rebuild = false;
FileHandle_t fh = filesystem->Open( vcdname, "rb", "GAME" ); if ( fh == FILESYSTEM_INVALID_HANDLE ) { Error( "Couldn't open '%s' for reading.", vcdname ); return; }
size_t bufSize = filesystem->Size( fh ); char *buffer = new char[ bufSize + 1 ]; filesystem->Read( buffer, bufSize, fh ); filesystem->Close( fh ); buffer[ bufSize ] = 0;
CRC32_t crc; CRC32_Init( &crc ); CRC32_ProcessBuffer( &crc, buffer, bufSize ); CRC32_Final( &crc );
delete[] buffer;
// Now load the file as a binary if it exists...
char binfile[ 512 ]; Q_strncpy( binfile, vcdname, sizeof( binfile ) ); Q_SetExtension( binfile, ".xcd", sizeof( binfile ) );
if ( buildxcds ) { fh = filesystem->Open( binfile, "rb", "GAME" ); if ( fh == FILESYSTEM_INVALID_HANDLE ) { AppendDisposition( disposition, "Built '%s'\n", binfile ); rebuild = true; } else { // Read the first bit of data and check
char crcdata[ 12 ]; filesystem->Read( crcdata, sizeof( crcdata ), fh ); filesystem->Close( fh );
CUtlBuffer buf; buf.Put( crcdata, sizeof( crcdata ) );
CRC32_t fileCRC = 0; if ( !CChoreoScene::GetCRCFromBuffer( buf, (unsigned int &)fileCRC ) ) { AppendDisposition( disposition, "Rebuilt '%s' due to version change\n", binfile ); rebuild = true; } else { if ( fileCRC != crc ) { AppendDisposition( disposition, "Rebuilt '%s' due to crc change\n", binfile ); rebuild = true; } } } }
// Validate the scene cache
g_SceneCache.RebuildItem( vcdname + strlen( g_szCurrentGameDir ) + 1 );
if ( !rebuild ) return;
// Remove current binary
if ( filesystem->FileExists( binfile, "GAME" ) ) { _unlink( binfile ); }
// Load the .vcd
LoadScriptFile( (char *)vcdname ); CChoreoScene *scene = ChoreoLoadScene( vcdname, NULL, &g_TokenProcessor, Con_Printf ); if ( scene ) { scene->SaveBinary( binfile, NULL, crc ); delete scene; } }
void CompileVCDs( CUtlVector< CUtlSymbol >& vcds ) { CUtlVector< CUtlSymbol > disposition;
StartPacifier( "CompileVCDs" ); int i; int c = vcds.Count(); for ( i = 0 ; i < c; ++i ) { UpdatePacifier( (float)i / (float)c ); ProcessVCD( g_Analysis.symbols.String( vcds[ i ] ), disposition ); } EndPacifier();
if ( verbose ) { c = disposition.Count(); for ( i = 0; i < c; ++i ) { Warning( "%s", disposition[ i ].String() ); } } }
static CUtlMap< CStudioHdr *, MDLHandle_t > g_ModelMap( 0, 0, DefLessFunc( CStudioHdr * ) );
CStudioHdr *ModelSoundsCache_LoadModel( char const *filename ) { MDLHandle_t handle = g_pMDLCache->FindMDL( filename );
CStudioHdr *studioHdr = new CStudioHdr( g_pMDLCache->GetStudioHdr( handle ), g_pMDLCache );
g_ModelMap.Insert( studioHdr, handle );
if ( studioHdr->IsValid() ) return studioHdr; return NULL; }
void ModelSoundsCache_FinishModel( CStudioHdr *hdr ) { int idx = g_ModelMap.Find( hdr ); if ( idx != g_ModelMap.InvalidIndex() ) { g_pMDLCache->Release( g_ModelMap[ idx ] ); g_ModelMap.RemoveAt( idx ); } delete hdr; }
void ModelSoundsCache_PrecacheScriptSound( const char *soundname ) { }
void ProcessMDL( char const *mdlname, CUtlVector< CUtlSymbol >& disposition ) { if ( verbose ) { vprint( 0, "Processing '%s'\n", mdlname ); }
if ( Q_stristr( mdlname, "ghostanim" ) ) { int n =3 ; } // Validate the model sounds cache
g_ModelSoundsCache.RebuildItem( mdlname + strlen( g_szCurrentGameDir ) + 1 ); }
void CompileMDLs( CUtlVector< CUtlSymbol >& mdls ) { CUtlVector< CUtlSymbol > disposition;
StartPacifier( "CompileMDLs" ); int i; int c = mdls.Count(); for ( i = 0 ; i < c; ++i ) { UpdatePacifier( (float)i / (float)c ); ProcessMDL( g_Analysis.symbols.String( mdls[ i ] ), disposition ); } EndPacifier();
c = disposition.Count(); for ( i = 0; i < c; ++i ) { Warning( "%s", disposition[ i ].String() ); } }
//-----------------------------------------------------------------------------
// The application object
//-----------------------------------------------------------------------------
class CMakeCachesApp : public CSteamAppSystemGroup { 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 CMakeCachesApp::Create() { SpewOutputFunc( SpewFunc ); SpewActivate( "makexvcd", 2 );
// Add in the cvar factory
AppModule_t cvarModule = LoadModule( VStdLib_GetICVarFactory() ); AddSystem( cvarModule, VENGINE_CVAR_INTERFACE_VERSION );
AppSystemInfo_t appSystems[] = { { "materialsystem.dll", MATERIAL_SYSTEM_INTERFACE_VERSION }, { "studiorender.dll", STUDIO_RENDER_INTERFACE_VERSION }, { "vphysics.dll", VPHYSICS_INTERFACE_VERSION }, { "datacache.dll", DATACACHE_INTERFACE_VERSION }, { "datacache.dll", MDLCACHE_INTERFACE_VERSION }, { "datacache.dll", STUDIO_DATA_CACHE_INTERFACE_VERSION }, { "soundemittersystem.dll", SOUNDEMITTERSYSTEM_INTERFACE_VERSION },
{ "", "" } // Required to terminate the list
};
if ( !AddSystems( appSystems ) ) return false;
g_pFileSystem = filesystem = (IFileSystem*)FindSystem( FILESYSTEM_INTERFACE_VERSION ); g_pMDLCache = (IMDLCache*)FindSystem( MDLCACHE_INTERFACE_VERSION ); soundemitterbase = (ISoundEmitterSystemBase*)FindSystem(SOUNDEMITTERSYSTEM_INTERFACE_VERSION); g_pMaterialSystem = (IMaterialSystem*)FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION );
if ( !soundemitterbase || !g_pMDLCache || !filesystem || !g_pMaterialSystem ) { Error("Unable to load required library interface!\n"); }
g_pMaterialSystem->SetShaderAPI( "shaderapiempty.dll" );
return true; }
void CMakeCachesApp::Destroy() { g_pFileSystem = filesystem = NULL; soundemitterbase = NULL; g_pMDLCache = NULL; }
//-----------------------------------------------------------------------------
// Sets up the game path
//-----------------------------------------------------------------------------
bool CMakeCachesApp::SetupSearchPaths() { CFSSteamSetupInfo steamInfo; steamInfo.m_pDirectoryName = NULL; steamInfo.m_bOnlyUseDirectoryName = false; steamInfo.m_bToolsMode = true; steamInfo.m_bSetSteamDLLPath = true; steamInfo.m_bSteam = filesystem->IsSteam(); if ( FileSystem_SetupSteamEnvironment( steamInfo ) != FS_OK ) return false;
CFSMountContentInfo fsInfo; fsInfo.m_pFileSystem = filesystem; fsInfo.m_bToolsMode = true; fsInfo.m_pDirectoryName = steamInfo.m_GameInfoPath;
if ( FileSystem_MountContent( fsInfo ) != FS_OK ) return false;
// Finally, load the search paths for the "GAME" path.
CFSSearchPathsInit searchPathsInit; searchPathsInit.m_pDirectoryName = steamInfo.m_GameInfoPath; searchPathsInit.m_pFileSystem = fsInfo.m_pFileSystem; if ( FileSystem_LoadSearchPaths( searchPathsInit ) != FS_OK ) return false;
char platform[MAX_PATH]; Q_strncpy( platform, steamInfo.m_GameInfoPath, MAX_PATH ); Q_StripTrailingSlash( platform ); Q_strncat( platform, "/../platform", MAX_PATH, MAX_PATH );
fsInfo.m_pFileSystem->AddSearchPath( platform, "PLATFORM" );
// Set gamedir.
Q_MakeAbsolutePath( gamedir, sizeof( gamedir ), steamInfo.m_GameInfoPath ); Q_AppendSlash( gamedir, sizeof( gamedir ) );
return true; }
//-----------------------------------------------------------------------------
// Init, shutdown
//-----------------------------------------------------------------------------
bool CMakeCachesApp::PreInit( ) { MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false ); filesystem->SetWarningFunc( Warning );
// Add paths...
if ( !SetupSearchPaths() ) return false;
return true; }
void CMakeCachesApp::PostShutdown() { }
//-----------------------------------------------------------------------------
// main application
//-----------------------------------------------------------------------------
int CMakeCachesApp::Main() { for ( int 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 'm': modelsoundscache = true; break; case 's': scenecache = true; break; case 'x': buildxcds = true; break; case 'z': virtualmodel = true; break; default: printusage(); break; } } }
if ( CommandLine()->ParmCount() < 2 || ( i != CommandLine()->ParmCount() ) ) { PrintHeader(); printusage(); }
CheckLogFile();
PrintHeader();
vprint( 0, " Compiling binary .vcd files to .xvcd ...\n" );
char vcddir[ 256 ]; char modelsdir[ 256 ]; Q_snprintf( vcddir, sizeof( vcddir ), "scenes", CommandLine()->GetParm( i - 1 ) ); Q_snprintf( modelsdir, sizeof( modelsdir ), "models", CommandLine()->GetParm( i - 1 ) ); if ( !strstr( vcddir, "scenes" ) ) { vprint( 0, ".vcd dir %s looks invalid (format: u:/game/hl2/scenes)\n", vcddir ); return 0; } if ( !strstr( modelsdir, "models" ) ) { vprint( 0, ".mdl dir %s looks invalid (format: u:/game/hl2/models)\n", modelsdir ); return 0; }
char binaries[MAX_PATH]; Q_strncpy( binaries, gamedir, MAX_PATH ); Q_StripTrailingSlash( binaries ); Q_strncat( binaries, "/../bin", MAX_PATH, MAX_PATH );
filesystem->AddSearchPath( binaries, "EXECUTABLE_PATH");
soundemitterbase->ModInit();
// Delete the scene cache file
if ( scenecache ) filesystem->RemoveFile( "scene.cache", "MOD" ); if ( modelsoundscache ) filesystem->RemoveFile( "modelsounds.cache", "MOD" ); if ( virtualmodel ) filesystem->RemoveFile( "virtualmodel.cache", "MOD" );
CUtlSymbolTable pathStrings; CUtlVector< CUtlSymbol > searchList; char searchPaths[ 512 ]; filesystem->GetSearchPath( "GAME", true, searchPaths, sizeof( searchPaths ) );
// We want to walk them in reverse order so newer files are "overrides" for older ones, so we add them to a list in reverse order
for ( char *path = strtok( searchPaths, ";" ); path; path = strtok( NULL, ";" ) ) { char dir[ 512 ]; Q_strncpy( dir, path, sizeof( dir ) ); Q_FixSlashes( dir ); Q_strlower( dir ); Q_StripTrailingSlash( dir );
CUtlSymbol sym = pathStrings.AddString( dir ); // Push them on head so we can walk them in reverse order
searchList.AddToHead( sym ); }
if ( scenecache ) { g_SceneCache.Init(); } if ( modelsoundscache ) { g_ModelSoundsCache.Init();
g_pMDLCache->InitPreloadData( true ); }
EventList_RegisterSharedEvents();
for ( int sp = 0; sp < searchList.Count(); ++sp ) { char const *basedir = pathStrings.String( searchList[ sp ] ); Q_strncpy( g_szCurrentGameDir, basedir, sizeof( g_szCurrentGameDir ) );
vprint( 0, "Processing gamedir %s\n", basedir );
if ( scenecache ) { vprint( 1, "Building list of .vcd files\n" ); CUtlVector< CUtlSymbol > vcdfiles; BuildFileList( basedir, vcdfiles, vcddir, ".vcd" ); vprint( 1, "found %i .vcd files\n", vcdfiles.Count() ); CompileVCDs( vcdfiles ); }
if ( modelsoundscache ) { vprint( 1, "Building list of .mdl files\n" ); CUtlVector< CUtlSymbol > mdlfiles; BuildFileList( basedir, mdlfiles, modelsdir, ".mdl" ); vprint( 1, "found %i .mdl files\n", mdlfiles.Count() ); CompileMDLs( mdlfiles ); } }
if ( scenecache ) { if ( g_SceneCache.IsDirty() ) { g_SceneCache.Save(); } g_SceneCache.Shutdown(); }
if ( modelsoundscache ) { if ( g_ModelSoundsCache.IsDirty() ) { g_ModelSoundsCache.Save(); } g_ModelSoundsCache.Shutdown();
g_pMDLCache->ShutdownPreloadData(); }
soundemitterbase->ModShutdown();
return 0; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : argc -
// argv[] -
// Output : int
//-----------------------------------------------------------------------------
int main( int argc, char* argv[] ) { CommandLine()->CreateCmdLine( argc, argv );
CMakeCachesApp sceneManagerApp; CSteamApplication steamApplication( &sceneManagerApp ); int nRetVal = steamApplication.Run();
return nRetVal; }
|