Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

800 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: vcd_sound_check.cpp : Defines the entry point for the console application.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include <stdio.h>
  8. #include <windows.h>
  9. #include "tier0/dbg.h"
  10. #include "utldict.h"
  11. #include "tier1/UtlLinkedList.h"
  12. #include "filesystem.h"
  13. #include "FileSystem_Tools.h"
  14. #include "KeyValues.h"
  15. #include "cmdlib.h"
  16. #include "scriplib.h"
  17. #include "vstdlib/random.h"
  18. #include "SoundEmitterSystem/isoundemittersystembase.h"
  19. #include "choreoscene.h"
  20. #include "choreoevent.h"
  21. #include "choreoactor.h"
  22. #include "choreochannel.h"
  23. #include "iscenetokenprocessor.h"
  24. bool uselogfile = false;
  25. struct AnalysisData
  26. {
  27. CUtlSymbolTable symbols;
  28. };
  29. static AnalysisData g_Analysis;
  30. IFileSystem *filesystem = NULL;
  31. static CUniformRandomStream g_Random;
  32. IUniformRandomStream *random = &g_Random;
  33. ISoundEmitterSystemBase *soundemitter = NULL;
  34. static bool spewed = false;
  35. static bool spewmoveto = false;
  36. static bool vcdonly= false;
  37. //-----------------------------------------------------------------------------
  38. // Purpose: Helper for parsing scene data file
  39. //-----------------------------------------------------------------------------
  40. class CSceneTokenProcessor : public ISceneTokenProcessor
  41. {
  42. public:
  43. const char *CurrentToken( void );
  44. bool GetToken( bool crossline );
  45. bool TokenAvailable( void );
  46. void Error( const char *fmt, ... );
  47. };
  48. //-----------------------------------------------------------------------------
  49. // Purpose:
  50. // Output : const char
  51. //-----------------------------------------------------------------------------
  52. const char *CSceneTokenProcessor::CurrentToken( void )
  53. {
  54. return token;
  55. }
  56. //-----------------------------------------------------------------------------
  57. // Purpose:
  58. // Input : crossline -
  59. // Output : Returns true on success, false on failure.
  60. //-----------------------------------------------------------------------------
  61. bool CSceneTokenProcessor::GetToken( bool crossline )
  62. {
  63. return ::GetToken( crossline ) ? true : false;
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Purpose:
  67. // Output : Returns true on success, false on failure.
  68. //-----------------------------------------------------------------------------
  69. bool CSceneTokenProcessor::TokenAvailable( void )
  70. {
  71. return ::TokenAvailable() ? true : false;
  72. }
  73. //-----------------------------------------------------------------------------
  74. // Purpose:
  75. // Input : *fmt -
  76. // ... -
  77. //-----------------------------------------------------------------------------
  78. void CSceneTokenProcessor::Error( const char *fmt, ... )
  79. {
  80. char string[ 2048 ];
  81. va_list argptr;
  82. va_start( argptr, fmt );
  83. Q_vsnprintf( string, sizeof(string), fmt, argptr );
  84. va_end( argptr );
  85. Warning( "%s", string );
  86. Assert(0);
  87. }
  88. static CSceneTokenProcessor g_TokenProcessor;
  89. SpewRetval_t SpewFunc( SpewType_t type, char const *pMsg )
  90. {
  91. spewed = true;
  92. printf( "%s", pMsg );
  93. OutputDebugString( pMsg );
  94. if ( type == SPEW_ERROR )
  95. {
  96. printf( "\n" );
  97. OutputDebugString( "\n" );
  98. }
  99. return SPEW_CONTINUE;
  100. }
  101. //-----------------------------------------------------------------------------
  102. // Purpose:
  103. // Input : depth -
  104. // *fmt -
  105. // ... -
  106. //-----------------------------------------------------------------------------
  107. void vprint( int depth, const char *fmt, ... )
  108. {
  109. char string[ 8192 ];
  110. va_list va;
  111. va_start( va, fmt );
  112. vsprintf( string, fmt, va );
  113. va_end( va );
  114. FILE *fp = NULL;
  115. if ( uselogfile )
  116. {
  117. fp = fopen( "log.txt", "ab" );
  118. }
  119. while ( depth-- > 0 )
  120. {
  121. printf( " " );
  122. OutputDebugString( " " );
  123. if ( fp )
  124. {
  125. fprintf( fp, " " );
  126. }
  127. }
  128. ::printf( "%s", string );
  129. OutputDebugString( string );
  130. if ( fp )
  131. {
  132. char *p = string;
  133. while ( *p )
  134. {
  135. if ( *p == '\n' )
  136. {
  137. fputc( '\r', fp );
  138. }
  139. fputc( *p, fp );
  140. p++;
  141. }
  142. fclose( fp );
  143. }
  144. }
  145. void logprint( char const *logfile, const char *fmt, ... )
  146. {
  147. char string[ 8192 ];
  148. va_list va;
  149. va_start( va, fmt );
  150. vsprintf( string, fmt, va );
  151. va_end( va );
  152. FILE *fp = NULL;
  153. static bool first = true;
  154. if ( first )
  155. {
  156. first = false;
  157. fp = fopen( logfile, "wb" );
  158. }
  159. else
  160. {
  161. fp = fopen( logfile, "ab" );
  162. }
  163. if ( fp )
  164. {
  165. char *p = string;
  166. while ( *p )
  167. {
  168. if ( *p == '\n' )
  169. {
  170. fputc( '\r', fp );
  171. }
  172. fputc( *p, fp );
  173. p++;
  174. }
  175. fclose( fp );
  176. }
  177. }
  178. void Con_Printf( const char *fmt, ... )
  179. {
  180. va_list args;
  181. static char output[1024];
  182. va_start( args, fmt );
  183. vprintf( fmt, args );
  184. vsprintf( output, fmt, args );
  185. vprint( 0, output );
  186. }
  187. //-----------------------------------------------------------------------------
  188. // Purpose:
  189. //-----------------------------------------------------------------------------
  190. void printusage( void )
  191. {
  192. vprint( 0, "usage: vcd_sound_check <.wav root directory> <scenes root directory>\n\
  193. \t-v = verbose output\n\
  194. \t-m = spew moveto info\n\
  195. \t-o = spew vcd overlap info only\n\
  196. \t-l = log to file log.txt\n\
  197. \ne.g.: vcd_sound_check -l u:/hl2/hl2/sound/vo u:/hl2/hl2/scenes\n" );
  198. // Exit app
  199. exit( 1 );
  200. }
  201. void BuildFileList_R( CUtlVector< CUtlSymbol >& files, char const *dir, char const *extension )
  202. {
  203. WIN32_FIND_DATA wfd;
  204. char directory[ 256 ];
  205. char filename[ 256 ];
  206. HANDLE ff;
  207. sprintf( directory, "%s\\*.*", dir );
  208. if ( ( ff = FindFirstFile( directory, &wfd ) ) == INVALID_HANDLE_VALUE )
  209. return;
  210. int extlen = strlen( extension );
  211. do
  212. {
  213. if ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
  214. {
  215. if ( wfd.cFileName[ 0 ] == '.' )
  216. continue;
  217. // Recurse down directory
  218. sprintf( filename, "%s\\%s", dir, wfd.cFileName );
  219. BuildFileList_R( files, filename, extension );
  220. }
  221. else
  222. {
  223. int len = strlen( wfd.cFileName );
  224. if ( len > extlen )
  225. {
  226. if ( !stricmp( &wfd.cFileName[ len - extlen ], extension ) )
  227. {
  228. char filename[ MAX_PATH ];
  229. Q_snprintf( filename, sizeof( filename ), "%s\\%s", dir, wfd.cFileName );
  230. _strlwr( filename );
  231. Q_FixSlashes( filename );
  232. CUtlSymbol sym = g_Analysis.symbols.AddString( filename );
  233. files.AddToTail( sym );
  234. if ( !( files.Count() % 3000 ) )
  235. {
  236. vprint( 0, "...found %i .%s files\n", files.Count(), extension );
  237. }
  238. }
  239. }
  240. }
  241. } while ( FindNextFile( ff, &wfd ) );
  242. }
  243. void BuildFileList( CUtlVector< CUtlSymbol >& files, char const *rootdir, char const *extension )
  244. {
  245. files.RemoveAll();
  246. BuildFileList_R( files, rootdir, extension );
  247. }
  248. //-----------------------------------------------------------------------------
  249. // Purpose:
  250. //-----------------------------------------------------------------------------
  251. void CheckLogFile( void )
  252. {
  253. if ( uselogfile )
  254. {
  255. _unlink( "log.txt" );
  256. vprint( 0, " Outputting to log.txt\n" );
  257. }
  258. }
  259. void PrintHeader()
  260. {
  261. vprint( 0, "Valve Software - vcd_sound_check.exe (%s)\n", __DATE__ );
  262. vprint( 0, "--- Voice Wav File .vcd Checker ---\n" );
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose: For each .wav file in the list, see if any vcd file in the list references it
  266. // First build an index of .wav to .vcd mappings, then search wav list and print results
  267. // Input : vcdfiles -
  268. // wavfiles -
  269. //-----------------------------------------------------------------------------
  270. struct VCDList
  271. {
  272. VCDList()
  273. {
  274. }
  275. VCDList( const VCDList& src )
  276. {
  277. int c = src.vcds.Count();
  278. for ( int i = 0 ; i < c; i++ )
  279. {
  280. vcds.AddToTail( src.vcds[ i ] );
  281. }
  282. }
  283. VCDList& operator =( const VCDList& src )
  284. {
  285. if ( this == &src )
  286. return *this;
  287. int c = src.vcds.Count();
  288. for ( int i = 0 ; i < c; i++ )
  289. {
  290. vcds.AddToTail( src.vcds[ i ] );
  291. }
  292. return *this;
  293. }
  294. CUtlVector< CUtlSymbol > vcds;
  295. };
  296. static int ecounter = 0;
  297. void SpewMoveto( bool first, char const *vcdname, CChoreoEvent *e )
  298. {
  299. if ( !spewmoveto )
  300. return;
  301. //
  302. if ( first )
  303. {
  304. ecounter = 0;
  305. }
  306. logprint( "moveto.txt", "\"%s\",%i,\"%s\",%.3f,\"%s\",%s\n",
  307. vcdname,
  308. ++ecounter,
  309. e->GetName(),
  310. e->GetStartTime(),
  311. e->GetParameters(),
  312. e->IsResumeCondition() ? "YES" : "no" );
  313. }
  314. static bool ChoreEventStartTimeLessFunc( CChoreoEvent * const &p1, CChoreoEvent * const &p2 )
  315. {
  316. CChoreoEvent *e1;
  317. CChoreoEvent *e2;
  318. e1 = const_cast< CChoreoEvent * >( p1 );
  319. e2 = const_cast< CChoreoEvent * >( p2 );
  320. return e1->GetStartTime() < e2->GetStartTime();
  321. }
  322. static bool IsFlexTrackBeingUsed( CChoreoEvent *event, char const *trackName )
  323. {
  324. int tc = event->GetNumFlexAnimationTracks();
  325. for ( int track = 0; track < tc; ++track )
  326. {
  327. CFlexAnimationTrack *t = event->GetFlexAnimationTrack( track );
  328. if ( !t->IsTrackActive() )
  329. {
  330. continue;
  331. }
  332. int sampleCountNormal = t->GetNumSamples( 0 );
  333. int sampleCountBalance = 0;
  334. if ( t->IsComboType() )
  335. {
  336. sampleCountBalance = t->GetNumSamples( 1 );
  337. }
  338. if ( !sampleCountNormal && !sampleCountBalance )
  339. continue;
  340. // Otherwise, see if the test track has this as an active track
  341. if ( !Q_stricmp( t->GetFlexControllerName(), trackName ) )
  342. {
  343. return true;
  344. }
  345. }
  346. return false;
  347. }
  348. static bool EventCollidesWithRows( CUtlLinkedList< CChoreoEvent*, int >& list, CChoreoEvent *event, char *trackName, size_t trackNameLength )
  349. {
  350. float st = event->GetStartTime();
  351. float ed = event->GetEndTime();
  352. for ( int i = list.Head(); i != list.InvalidIndex(); i = list.Next( i ) )
  353. {
  354. CChoreoEvent *test = list[ i ];
  355. float teststart = test->GetStartTime();
  356. float testend = test->GetEndTime();
  357. // See if spans overlap
  358. if ( teststart >= ed )
  359. continue;
  360. if ( testend <= st )
  361. continue;
  362. // Now see if they deal with the same flex controller
  363. int tc = event->GetNumFlexAnimationTracks();
  364. for ( int track = 0; track < tc; ++track )
  365. {
  366. CFlexAnimationTrack *t = event->GetFlexAnimationTrack( track );
  367. if ( !t->IsTrackActive() )
  368. {
  369. continue;
  370. }
  371. int sampleCountNormal = t->GetNumSamples( 0 );
  372. int sampleCountBalance = 0;
  373. if ( t->IsComboType() )
  374. {
  375. sampleCountBalance = t->GetNumSamples( 1 );
  376. }
  377. if ( !sampleCountNormal && !sampleCountBalance )
  378. continue;
  379. // Otherwise, see if the test track has this as an active track
  380. if ( IsFlexTrackBeingUsed( test, t->GetFlexControllerName() ) )
  381. {
  382. Q_strncpy( trackName, t->GetFlexControllerName(), trackNameLength );
  383. return true;
  384. }
  385. }
  386. return false;
  387. }
  388. return false;
  389. }
  390. void CheckForOverlappingFlexTracks( CChoreoScene *scene )
  391. {
  392. for ( int a = 0; a < scene->GetNumActors(); ++a )
  393. {
  394. CChoreoActor *actor = scene->GetActor( a );
  395. CUtlRBTree< CChoreoEvent * > actorFlexEvents( 0, 0, ChoreEventStartTimeLessFunc );
  396. for ( int c = 0; c < actor->GetNumChannels(); ++c )
  397. {
  398. CChoreoChannel *channel = actor->GetChannel( c );
  399. for ( int e = 0 ; e < channel->GetNumEvents(); ++e )
  400. {
  401. CChoreoEvent *event = channel->GetEvent( e );
  402. if ( event->GetType() != CChoreoEvent::FLEXANIMATION )
  403. continue;
  404. actorFlexEvents.Insert( event );
  405. }
  406. }
  407. CUtlVector< CUtlLinkedList< CChoreoEvent*, int > > rows;
  408. bool done = false;
  409. int i;
  410. // Now check for overlaps
  411. for ( i = actorFlexEvents.FirstInorder(); i != actorFlexEvents.InvalidIndex() && !done; i = actorFlexEvents.NextInorder( i ) )
  412. {
  413. CChoreoEvent *e = actorFlexEvents[ i ];
  414. if ( !rows.Count() )
  415. {
  416. rows.AddToTail();
  417. CUtlLinkedList< CChoreoEvent*, int >& list = rows[ 0 ];
  418. list.AddToHead( e );
  419. continue;
  420. }
  421. // Does it come totally after what's in rows[0]?
  422. int rowCount = rows.Count();
  423. bool addrow = true;
  424. for ( int j = 0; j < rowCount; j++ )
  425. {
  426. CUtlLinkedList< CChoreoEvent*, int >& list = rows[ j ];
  427. char offender[ 256 ];
  428. if ( !EventCollidesWithRows( list, e, offender, sizeof( offender ) ) )
  429. {
  430. // Update row event list
  431. list.AddToHead( e );
  432. addrow = false;
  433. break;
  434. }
  435. else
  436. {
  437. Msg( "[%s] has overlapping events for actor [%s] [%s] [flex: %s]\n",
  438. scene->GetFilename(), actor->GetName(), e->GetName(), offender );
  439. done = true;
  440. }
  441. }
  442. if ( addrow )
  443. {
  444. // Add a new row
  445. int idx = rows.AddToTail();
  446. CUtlLinkedList< CChoreoEvent *, int >& list = rows[ idx ];
  447. list.AddToHead( e );
  448. }
  449. }
  450. // Assert( rows.Count() <= 1 );
  451. }
  452. }
  453. void ProcessVCD( CUtlDict< VCDList, int >& database, CUtlSymbol& vcdname )
  454. {
  455. // vprint( 0, "Processing '%s'\n", g_Analysis.symbols.String( vcdname ) );
  456. // Load the .vcd
  457. char fullname[ 512 ];
  458. Q_snprintf( fullname, sizeof( fullname ), "%s", g_Analysis.symbols.String( vcdname ) );
  459. LoadScriptFile( fullname );
  460. CChoreoScene *scene = ChoreoLoadScene( fullname, NULL, &g_TokenProcessor, Con_Printf );
  461. if ( scene )
  462. {
  463. bool first = true;
  464. // Now iterate the events looking for speak events
  465. int c = scene->GetNumEvents();
  466. for ( int i = 0; i < c; i++ )
  467. {
  468. CChoreoEvent *e = scene->GetEvent( i );
  469. if ( e->GetType() == CChoreoEvent::MOVETO )
  470. {
  471. SpewMoveto( first, fullname, e );
  472. first = false;
  473. }
  474. if ( e->GetType() != CChoreoEvent::SPEAK )
  475. continue;
  476. // Look up sound in sound emitter system
  477. char const *wavename = soundemitter->GetWavFileForSound( e->GetParameters(), NULL );
  478. if ( !wavename || !wavename[ 0 ] )
  479. {
  480. continue;
  481. }
  482. char fullwavename[ 512 ];
  483. Q_snprintf( fullwavename, sizeof( fullwavename ), "%ssound\\%s",
  484. gamedir, wavename );
  485. Q_FixSlashes( fullwavename );
  486. // Now add to proper slot
  487. VCDList *entry = NULL;
  488. // Add vcd to database
  489. int slot = database.Find( fullwavename );
  490. if ( slot == database.InvalidIndex() )
  491. {
  492. VCDList nullEntry;
  493. slot = database.Insert( fullwavename, nullEntry );
  494. }
  495. entry = &database[ slot ];
  496. if ( entry->vcds.Find( vcdname ) == entry->vcds.InvalidIndex() )
  497. {
  498. entry->vcds.AddToTail( vcdname );
  499. }
  500. }
  501. if ( vcdonly )
  502. {
  503. CheckForOverlappingFlexTracks( scene );
  504. }
  505. }
  506. delete scene;
  507. }
  508. void CorrelateWavsAndVCDs( CUtlVector< CUtlSymbol >& vcdfiles, CUtlVector< CUtlSymbol >& wavfiles )
  509. {
  510. CUtlDict< VCDList, int > database;
  511. int i;
  512. int c = vcdfiles.Count();
  513. for ( i = 0; i < c; i++ )
  514. {
  515. CUtlSymbol& vcdname = vcdfiles[ i ];
  516. // Load the .vcd and update the database
  517. ProcessVCD( database, vcdname );
  518. }
  519. if ( vcdonly )
  520. return;
  521. vprint( 0, "Found %i wav files in %i vcds\n",
  522. database.Count(), vcdfiles.Count() );
  523. // Now look for any wavfiles that weren't in the database
  524. int ecount = 0;
  525. c = wavfiles.Count();
  526. for ( i = 0; i < c; i++ )
  527. {
  528. CUtlSymbol& wavename = wavfiles[ i ];
  529. int idx = database.Find( g_Analysis.symbols.String( wavename ) );
  530. if ( idx != database.InvalidIndex() )
  531. {
  532. VCDList *listentry = &database[ idx ];
  533. int vcdcount = listentry->vcds.Count();
  534. if ( vcdcount >= 2 && verbose )
  535. {
  536. vprint( 0, " wave '%s' used by multiple .vcds:\n", g_Analysis.symbols.String( wavename ) );
  537. int j;
  538. for ( j = 0; j < vcdcount; j++ )
  539. {
  540. vprint( 1, "%i -- '%s'\n", j+1, g_Analysis.symbols.String( listentry->vcds[ j ] ) );
  541. }
  542. }
  543. continue;
  544. }
  545. vprint( 0, "%i -- '%s' not referenced by .vcd\n",
  546. ++ecount, g_Analysis.symbols.String( wavename ) );
  547. }
  548. vprint( 0, "\nSummary: found %i/%i (%.2f percent) .wav errors\n", ecount, c, 100.0 * ecount / max( c, 1 ) );
  549. }
  550. //-----------------------------------------------------------------------------
  551. // Purpose:
  552. // Input : argc -
  553. // argv[] -
  554. // Output : int
  555. //-----------------------------------------------------------------------------
  556. int main( int argc, char* argv[] )
  557. {
  558. SpewOutputFunc( SpewFunc );
  559. SpewActivate( "vcd_sound_check", 2 );
  560. int i=1;
  561. for ( i ; i<argc ; i++)
  562. {
  563. if ( argv[ i ][ 0 ] == '-' )
  564. {
  565. switch( argv[ i ][ 1 ] )
  566. {
  567. case 'l':
  568. uselogfile = true;
  569. break;
  570. case 'v':
  571. verbose = true;
  572. break;
  573. case 'm':
  574. spewmoveto = true;
  575. break;
  576. case 'o':
  577. vcdonly = true;
  578. break;
  579. default:
  580. printusage();
  581. break;
  582. }
  583. }
  584. }
  585. if ( argc < 3 || ( i != argc ) )
  586. {
  587. PrintHeader();
  588. printusage();
  589. }
  590. CheckLogFile();
  591. PrintHeader();
  592. vprint( 0, " Looking for .wav files not referenced in .vcd files...\n" );
  593. char sounddir[ 256 ];
  594. char vcddir[ 256 ];
  595. strcpy( sounddir, argv[ i - 2 ] );
  596. strcpy( vcddir, argv[ i - 1 ] );
  597. if ( !strstr( sounddir, "sound" ) )
  598. {
  599. vprint( 0, "Sound dir %s looks invalid (format: u:/tf2/hl2/sound/vo)\n", sounddir );
  600. return 0;
  601. }
  602. if ( !strstr( vcddir, "scenes" ) )
  603. {
  604. vprint( 0, ".vcd dir %s looks invalid (format: u:/tf2/hl2/scenes)\n", vcddir );
  605. return 0;
  606. }
  607. char workingdir[ 256 ];
  608. workingdir[0] = 0;
  609. Q_getwd( workingdir, sizeof( workingdir ) );
  610. // If they didn't specify -game on the command line, use VPROJECT.
  611. CmdLib_InitFileSystem( workingdir );
  612. CSysModule *pSoundEmitterModule = g_pFullFileSystem->LoadModule( "soundemittersystem.dll" );
  613. if ( !pSoundEmitterModule )
  614. {
  615. vprint( 0, "Sys_LoadModule( soundemittersystem.dll ) failed!\n" );
  616. return 0;
  617. }
  618. CreateInterfaceFn hSoundEmitterFactory = Sys_GetFactory( pSoundEmitterModule );
  619. if ( !hSoundEmitterFactory )
  620. {
  621. vprint( 0, "Sys_GetFactory on soundemittersystem.dll failed!\n" );
  622. return 0;
  623. }
  624. soundemitter = ( ISoundEmitterSystemBase * )hSoundEmitterFactory( SOUNDEMITTERSYSTEM_INTERFACE_VERSION, NULL );
  625. if ( !soundemitter )
  626. {
  627. vprint( 0, "Couldn't get interface %s from soundemittersystem.dll!\n", SOUNDEMITTERSYSTEM_INTERFACE_VERSION );
  628. return 0;
  629. }
  630. filesystem = (IFileSystem *)(CmdLib_GetFileSystemFactory()( FILESYSTEM_INTERFACE_VERSION, NULL ));
  631. if ( !filesystem )
  632. {
  633. AssertMsg( 0, "Failed to create/get IFileSystem" );
  634. return 1;
  635. }
  636. Q_FixSlashes( gamedir );
  637. Q_strlower( gamedir );
  638. vprint( 0, "game dir %s\nsounds dir %s\nvcd dir %s\n\n",
  639. gamedir,
  640. sounddir,
  641. vcddir );
  642. Q_StripTrailingSlash( sounddir );
  643. Q_StripTrailingSlash( vcddir );
  644. filesystem->RemoveFile( "moveto.txt", "GAME" );
  645. //
  646. //ProcessMaterialsDirectory( vmtdir );
  647. vprint( 0, "Initializing sound emitter system\n" );
  648. soundemitter->Connect( FileSystem_GetFactory() );
  649. soundemitter->Init();
  650. vprint( 0, "Loaded %i sounds\n", soundemitter->GetSoundCount() );
  651. vprint( 0, "Building list of .vcd files\n" );
  652. CUtlVector< CUtlSymbol > vcdfiles;
  653. BuildFileList( vcdfiles, vcddir, ".vcd" );
  654. vprint( 0, "found %i .vcd files\n\n", vcdfiles.Count() );
  655. vprint( 0, "Building list of known .wav files\n" );
  656. CUtlVector< CUtlSymbol > wavfiles;
  657. BuildFileList( wavfiles, sounddir, ".wav" );
  658. vprint( 0, "found %i .wav files\n\n", wavfiles.Count() );
  659. CorrelateWavsAndVCDs( vcdfiles, wavfiles );
  660. soundemitter->Shutdown();
  661. soundemitter = 0;
  662. g_pFullFileSystem->UnloadModule( pSoundEmitterModule );
  663. FileSystem_Term();
  664. return 0;
  665. }