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.

588 lines
14 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 <stdio.h>
  7. #include <windows.h>
  8. #include "tier0/dbg.h"
  9. #include "tier1/utldict.h"
  10. #include "filesystem.h"
  11. #include "cmdlib.h"
  12. #include "scriplib.h"
  13. #include "vstdlib/random.h"
  14. #include "tier1/UtlBuffer.h"
  15. #include "pacifier.h"
  16. #include "appframework/tier3app.h"
  17. #include "tier0/icommandline.h"
  18. #include "vgui/IVGui.h"
  19. #include "vgui_controls/controls.h"
  20. #include "vgui/ILocalize.h"
  21. #include "tier1/checksum_crc.h"
  22. #include "tier1/UtlSortVector.h"
  23. #include "tier1/utlmap.h"
  24. #include "captioncompiler.h"
  25. #include "tier0/fasttimer.h"
  26. using namespace vgui;
  27. // #define TESTING 1
  28. bool uselogfile = false;
  29. bool bX360 = false;
  30. struct AnalysisData
  31. {
  32. CUtlSymbolTable symbols;
  33. };
  34. static AnalysisData g_Analysis;
  35. IBaseFileSystem *filesystem = NULL;
  36. static bool spewed = false;
  37. SpewRetval_t SpewFunc( SpewType_t type, char const *pMsg )
  38. {
  39. spewed = true;
  40. printf( "%s", pMsg );
  41. OutputDebugString( pMsg );
  42. if ( type == SPEW_ERROR )
  43. {
  44. printf( "\n" );
  45. OutputDebugString( "\n" );
  46. }
  47. return SPEW_CONTINUE;
  48. }
  49. //-----------------------------------------------------------------------------
  50. // Purpose:
  51. // Input : depth -
  52. // *fmt -
  53. // ... -
  54. //-----------------------------------------------------------------------------
  55. void vprint( int depth, const char *fmt, ... )
  56. {
  57. char string[ 8192 ];
  58. va_list va;
  59. va_start( va, fmt );
  60. vsprintf( string, fmt, va );
  61. va_end( va );
  62. FILE *fp = NULL;
  63. if ( uselogfile )
  64. {
  65. fp = fopen( "log.txt", "ab" );
  66. }
  67. while ( depth-- > 0 )
  68. {
  69. printf( " " );
  70. OutputDebugString( " " );
  71. if ( fp )
  72. {
  73. fprintf( fp, " " );
  74. }
  75. }
  76. ::printf( "%s", string );
  77. OutputDebugString( string );
  78. if ( fp )
  79. {
  80. char *p = string;
  81. while ( *p )
  82. {
  83. if ( *p == '\n' )
  84. {
  85. fputc( '\r', fp );
  86. }
  87. fputc( *p, fp );
  88. p++;
  89. }
  90. fclose( fp );
  91. }
  92. }
  93. void logprint( char const *logfile, const char *fmt, ... )
  94. {
  95. char string[ 8192 ];
  96. va_list va;
  97. va_start( va, fmt );
  98. vsprintf( string, fmt, va );
  99. va_end( va );
  100. FILE *fp = NULL;
  101. static bool first = true;
  102. if ( first )
  103. {
  104. first = false;
  105. fp = fopen( logfile, "wb" );
  106. }
  107. else
  108. {
  109. fp = fopen( logfile, "ab" );
  110. }
  111. if ( fp )
  112. {
  113. char *p = string;
  114. while ( *p )
  115. {
  116. if ( *p == '\n' )
  117. {
  118. fputc( '\r', fp );
  119. }
  120. fputc( *p, fp );
  121. p++;
  122. }
  123. fclose( fp );
  124. }
  125. }
  126. void Con_Printf( const char *fmt, ... )
  127. {
  128. va_list args;
  129. static char output[1024];
  130. va_start( args, fmt );
  131. vprintf( fmt, args );
  132. vsprintf( output, fmt, args );
  133. vprint( 0, output );
  134. }
  135. //-----------------------------------------------------------------------------
  136. // Purpose:
  137. //-----------------------------------------------------------------------------
  138. void printusage( void )
  139. {
  140. vprint( 0, "usage: captioncompiler closecaptionfile.txt\n\
  141. \t-v = verbose output\n\
  142. \t-l = log to file log.txt\n\
  143. \ne.g.: kvc -l u:/xbox/game/hl2x/resource/closecaption_english.txt" );
  144. // Exit app
  145. exit( 1 );
  146. }
  147. //-----------------------------------------------------------------------------
  148. // Purpose:
  149. //-----------------------------------------------------------------------------
  150. void CheckLogFile( void )
  151. {
  152. if ( uselogfile )
  153. {
  154. _unlink( "log.txt" );
  155. vprint( 0, " Outputting to log.txt\n" );
  156. }
  157. }
  158. void PrintHeader()
  159. {
  160. vprint( 0, "Valve Software - captioncompiler.exe (%s)\n", __DATE__ );
  161. vprint( 0, "--- Close Caption File compiler ---\n" );
  162. }
  163. //-----------------------------------------------------------------------------
  164. // The application object
  165. //-----------------------------------------------------------------------------
  166. class CCompileCaptionsApp : public CTier3SteamApp
  167. {
  168. typedef CTier3SteamApp BaseClass;
  169. public:
  170. // Methods of IApplication
  171. virtual bool Create();
  172. virtual bool PreInit();
  173. virtual int Main();
  174. virtual void PostShutdown();
  175. virtual void Destroy();
  176. private:
  177. // Sets up the search paths
  178. bool SetupSearchPaths();
  179. void CompileCaptionFile( char const *infile, char const *outfile );
  180. void DescribeCaptions( char const *file );
  181. };
  182. bool CCompileCaptionsApp::Create()
  183. {
  184. SpewOutputFunc( SpewFunc );
  185. SpewActivate( "kvc", 2 );
  186. AppSystemInfo_t appSystems[] =
  187. {
  188. { "vgui2.dll", VGUI_IVGUI_INTERFACE_VERSION },
  189. { "", "" } // Required to terminate the list
  190. };
  191. return AddSystems( appSystems );
  192. }
  193. void CCompileCaptionsApp::Destroy()
  194. {
  195. }
  196. //-----------------------------------------------------------------------------
  197. // Sets up the game path
  198. //-----------------------------------------------------------------------------
  199. bool CCompileCaptionsApp::SetupSearchPaths()
  200. {
  201. if ( !BaseClass::SetupSearchPaths( NULL, false, true ) )
  202. return false;
  203. // Set gamedir.
  204. Q_MakeAbsolutePath( gamedir, sizeof( gamedir ), GetGameInfoPath() );
  205. Q_AppendSlash( gamedir, sizeof( gamedir ) );
  206. return true;
  207. }
  208. //-----------------------------------------------------------------------------
  209. // Init, shutdown
  210. //-----------------------------------------------------------------------------
  211. bool CCompileCaptionsApp::PreInit( )
  212. {
  213. if ( !BaseClass::PreInit() )
  214. return false;
  215. g_pFileSystem = g_pFullFileSystem;
  216. if ( !g_pFileSystem || !g_pVGui || !g_pVGuiLocalize )
  217. {
  218. Error( "Unable to load required library interface!\n" );
  219. return false;
  220. }
  221. // MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
  222. g_pFullFileSystem->SetWarningFunc( Warning );
  223. // Add paths...
  224. if ( !SetupSearchPaths() )
  225. return false;
  226. return true;
  227. }
  228. void CCompileCaptionsApp::PostShutdown()
  229. {
  230. g_pFileSystem = NULL;
  231. BaseClass::PostShutdown();
  232. }
  233. void CCompileCaptionsApp::CompileCaptionFile( char const *infile, char const *outfile )
  234. {
  235. StringIndex_t maxindex = (StringIndex_t)-1;
  236. int maxunicodesize = 0;
  237. int totalsize = 0;
  238. int c = 0;
  239. int curblock = 0;
  240. int usedBytes = 0;
  241. int blockSize = MAX_BLOCK_SIZE;
  242. int freeSpace = 0;
  243. CUtlVector< CaptionLookup_t > directory;
  244. CUtlBuffer data;
  245. CUtlRBTree< unsigned int > hashcollision( 0, 0, DefLessFunc( unsigned int ) );
  246. for ( StringIndex_t i = g_pVGuiLocalize->GetFirstStringIndex(); i != INVALID_LOCALIZE_STRING_INDEX; i = g_pVGuiLocalize->GetNextStringIndex( i ), ++c )
  247. {
  248. char const *entryName = g_pVGuiLocalize->GetNameByIndex( i );
  249. CaptionLookup_t entry;
  250. entry.SetHash( entryName );
  251. // vprint( 0, "%d / %d: %s == %u\n", c, i, g_pVGuiLocalize->GetNameByIndex( i ), entry.hash );
  252. if ( hashcollision.Find( entry.hash ) != hashcollision.InvalidIndex() )
  253. {
  254. Error( "Hash name collision on %s!!!\n", g_pVGuiLocalize->GetNameByIndex( i ) );
  255. }
  256. hashcollision.Insert( entry.hash );
  257. const wchar_t *text = g_pVGuiLocalize->GetValueByIndex( i );
  258. if ( verbose )
  259. {
  260. vprint( 0, "Processing: '%30.30s' = '%S'\n", entryName, text );
  261. }
  262. int len = text ? ( wcslen( text ) + 1 ) * sizeof( short ) : 0;
  263. if ( len > maxunicodesize )
  264. {
  265. maxindex = i;
  266. maxunicodesize = len;
  267. }
  268. if ( len > blockSize )
  269. {
  270. Error( "Caption text file '%s' contains a single caption '%s' of %d bytes (%d is max), change MAX_BLOCK_SIZE in captioncompiler.h to fix!!!\n", g_pVGuiLocalize->GetNameByIndex( i ),
  271. entryName, len, blockSize );
  272. }
  273. totalsize += len;
  274. if ( usedBytes + len >= blockSize )
  275. {
  276. ++curblock;
  277. int leftover = ( blockSize - usedBytes );
  278. totalsize += leftover;
  279. freeSpace += leftover;
  280. while ( --leftover >= 0 )
  281. {
  282. data.PutChar( 0 );
  283. }
  284. usedBytes = len;
  285. entry.offset = 0;
  286. data.Put( (const void *)text, len );
  287. }
  288. else
  289. {
  290. entry.offset = usedBytes;
  291. usedBytes += len;
  292. data.Put( (const void *)text, len );
  293. }
  294. entry.length = len;
  295. entry.blockNum = curblock;
  296. directory.AddToTail( entry );
  297. }
  298. int leftover = ( blockSize - usedBytes );
  299. totalsize += leftover;
  300. freeSpace += leftover;
  301. while ( --leftover >= 0 )
  302. {
  303. data.PutChar( 0 );
  304. }
  305. vprint( 0, "Found %i strings in '%s'\n", c, infile );
  306. if ( maxindex != INVALID_LOCALIZE_STRING_INDEX )
  307. {
  308. vprint( 0, "Longest string '%s' = (%i) bytes average(%.3f)\n%",
  309. g_pVGuiLocalize->GetNameByIndex( maxindex ), maxunicodesize, (float)totalsize/(float)c );
  310. }
  311. vprint( 0, "%d blocks (%d bytes each), %d bytes wasted (%.3f per block average), total bytes %d\n",
  312. curblock + 1, blockSize, freeSpace, (float)freeSpace/(float)( curblock + 1 ), totalsize );
  313. vprint( 0, "directory size %d entries, %d bytes, data size %d bytes\n",
  314. directory.Count(), directory.Count() * sizeof( CaptionLookup_t ), data.TellPut() );
  315. CompiledCaptionHeader_t header;
  316. header.magic = COMPILED_CAPTION_FILEID;
  317. header.version = COMPILED_CAPTION_VERSION;
  318. header.numblocks = curblock + 1;
  319. header.blocksize = blockSize;
  320. header.directorysize = directory.Count();
  321. header.dataoffset = 0;
  322. // Now write the outfile
  323. CUtlBuffer out;
  324. out.Put( &header, sizeof( header ) );
  325. out.Put( directory.Base(), directory.Count() * sizeof( CaptionLookup_t ) );
  326. int curOffset = out.TellPut();
  327. // Round it up to the next 512 byte boundary
  328. int nBytesDestBuffer = AlignValue( curOffset, 512 ); // align to HD sector
  329. int nPadding = nBytesDestBuffer - curOffset;
  330. while ( --nPadding >= 0 )
  331. {
  332. out.PutChar( 0 );
  333. }
  334. out.Put( data.Base(), data.TellPut() );
  335. // Write out a corrected header
  336. header.dataoffset = nBytesDestBuffer;
  337. int savePos = out.TellPut();
  338. out.SeekPut( CUtlBuffer::SEEK_HEAD, 0 );
  339. out.Put( &header, sizeof( header ) );
  340. out.SeekPut( CUtlBuffer::SEEK_HEAD, savePos );
  341. g_pFullFileSystem->WriteFile( outfile, NULL, out );
  342. // Jeep: this function no longer exisits
  343. /*if ( bX360 )
  344. {
  345. UpdateOrCreateCaptionFile_X360( g_pFullFileSystem, outfile, NULL, true );
  346. }*/
  347. }
  348. void CCompileCaptionsApp::DescribeCaptions( char const *file )
  349. {
  350. CUtlBuffer buf;
  351. if ( !g_pFullFileSystem->ReadFile( file, NULL, buf ) )
  352. {
  353. Error( "Unable to read '%s' into buffer\n", file );
  354. }
  355. CompiledCaptionHeader_t header;
  356. buf.Get( &header, sizeof( header ) );
  357. if ( header.magic != COMPILED_CAPTION_FILEID )
  358. Error( "Invalid file id for %s\n", file );
  359. if ( header.version != COMPILED_CAPTION_VERSION )
  360. Error( "Invalid file version for %s\n", file );
  361. // Read the directory
  362. CUtlSortVector< CaptionLookup_t, CCaptionLookupLess > directory;
  363. directory.EnsureCapacity( header.directorysize );
  364. directory.CopyArray( (const CaptionLookup_t *)buf.PeekGet(), header.directorysize );
  365. directory.RedoSort( true );
  366. buf.SeekGet( CUtlBuffer::SEEK_HEAD, header.dataoffset );
  367. int i;
  368. CUtlVector< CaptionBlock_t > blocks;
  369. for ( i = 0; i < header.numblocks; ++i )
  370. {
  371. CaptionBlock_t& newBlock = blocks[ blocks.AddToTail() ];
  372. Q_memset( newBlock.data, 0, sizeof( newBlock.data ) );
  373. buf.Get( newBlock.data, header.blocksize );
  374. }
  375. CUtlMap< unsigned int, StringIndex_t > inverseMap( 0, 0, DefLessFunc( unsigned int ) );
  376. for ( StringIndex_t idx = g_pVGuiLocalize->GetFirstStringIndex(); idx != INVALID_LOCALIZE_STRING_INDEX; idx = g_pVGuiLocalize->GetNextStringIndex( idx ) )
  377. {
  378. const char *name = g_pVGuiLocalize->GetNameByIndex( idx );
  379. CaptionLookup_t dummy;
  380. dummy.SetHash( name );
  381. inverseMap.Insert( dummy.hash, idx );
  382. }
  383. // Now print everything out...
  384. for ( i = 0; i < header.directorysize; ++i )
  385. {
  386. const CaptionLookup_t& entry = directory[ i ];
  387. char const *name = g_pVGuiLocalize->GetNameByIndex( inverseMap.Element( inverseMap.Find( entry.hash ) ) );
  388. const CaptionBlock_t& block = blocks[ entry.blockNum ];
  389. const wchar_t *data = (const wchar_t *)&block.data[ entry.offset ];
  390. wchar_t *temp = ( wchar_t * )_alloca( entry.length * sizeof( short ) );
  391. wcsncpy( temp, data, ( entry.length / sizeof( short ) ) - 1 );
  392. vprint( 0, "%3.3d: (%40.40s) hash(%15.15u), block(%4.4d), offset(%4.4d), len(%4.4d) %S\n",
  393. i, name, entry.hash, entry.blockNum, entry.offset, entry.length, temp );
  394. }
  395. }
  396. //-----------------------------------------------------------------------------
  397. // main application
  398. //-----------------------------------------------------------------------------
  399. int CCompileCaptionsApp::Main()
  400. {
  401. CUtlVector< CUtlSymbol > worklist;
  402. int i = 1;
  403. for ( i ; i<CommandLine()->ParmCount() ; i++)
  404. {
  405. if ( CommandLine()->GetParm( i )[ 0 ] == '-' )
  406. {
  407. switch( CommandLine()->GetParm( i )[ 1 ] )
  408. {
  409. case 'l':
  410. uselogfile = true;
  411. break;
  412. case 'v':
  413. verbose = true;
  414. break;
  415. case 'x':
  416. bX360 = true;
  417. break;
  418. case 'g': // -game
  419. ++i;
  420. break;
  421. default:
  422. printusage();
  423. break;
  424. }
  425. }
  426. else if ( i != 0 )
  427. {
  428. char fn[ 512 ];
  429. Q_strncpy( fn, CommandLine()->GetParm( i ), sizeof( fn ) );
  430. Q_FixSlashes( fn );
  431. Q_strlower( fn );
  432. CUtlSymbol sym;
  433. sym = fn;
  434. worklist.AddToTail( sym );
  435. }
  436. }
  437. if ( CommandLine()->ParmCount() < 2 || ( i != CommandLine()->ParmCount() ) || worklist.Count() != 1 )
  438. {
  439. PrintHeader();
  440. printusage();
  441. }
  442. CheckLogFile();
  443. PrintHeader();
  444. char binaries[MAX_PATH];
  445. Q_strncpy( binaries, gamedir, MAX_PATH );
  446. Q_StripTrailingSlash( binaries );
  447. Q_strncat( binaries, "/../bin", MAX_PATH, MAX_PATH );
  448. char outfile[ 512 ];
  449. if ( Q_stristr( worklist[ worklist.Count() - 1 ].String(), gamedir ) )
  450. {
  451. Q_strncpy( outfile, &worklist[ worklist.Count() - 1 ].String()[ Q_strlen( gamedir ) ] , sizeof( outfile ) );
  452. }
  453. else
  454. {
  455. Q_snprintf( outfile, sizeof( outfile ), "resource\\%s", worklist[ worklist.Count() - 1 ].String() );
  456. }
  457. char infile[ 512 ];
  458. Q_strncpy( infile, outfile, sizeof( infile ) );
  459. Q_SetExtension( outfile, ".dat", sizeof( outfile ) );
  460. vprint( 0, "gamedir[ %s ]\n", gamedir );
  461. vprint( 0, "infile[ %s ]\n", infile );
  462. vprint( 0, "outfile[ %s ]\n", outfile );
  463. g_pFullFileSystem->AddSearchPath( binaries, "EXECUTABLE_PATH" );
  464. if ( !g_pVGuiLocalize->AddFile( infile, "MOD", false ) )
  465. {
  466. Error( "Unable to add localization file '%s'\n", infile );
  467. }
  468. vprint( 0, " Compiling Captions for '%s'...\n", infile );
  469. CompileCaptionFile( infile, outfile );
  470. if ( verbose )
  471. {
  472. DescribeCaptions( outfile );
  473. }
  474. g_pVGuiLocalize->RemoveAll();
  475. return 0;
  476. }
  477. //-----------------------------------------------------------------------------
  478. // Purpose: Main entry point
  479. //-----------------------------------------------------------------------------
  480. DEFINE_CONSOLE_STEAM_APPLICATION_OBJECT( CCompileCaptionsApp )