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
588 lines
14 KiB
//========= 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 "cmdlib.h" |
|
#include "scriplib.h" |
|
#include "vstdlib/random.h" |
|
#include "tier1/UtlBuffer.h" |
|
#include "pacifier.h" |
|
#include "appframework/tier3app.h" |
|
#include "tier0/icommandline.h" |
|
#include "vgui/IVGui.h" |
|
#include "vgui_controls/controls.h" |
|
#include "vgui/ILocalize.h" |
|
#include "tier1/checksum_crc.h" |
|
#include "tier1/UtlSortVector.h" |
|
#include "tier1/utlmap.h" |
|
#include "captioncompiler.h" |
|
|
|
#include "tier0/fasttimer.h" |
|
|
|
using namespace vgui; |
|
|
|
// #define TESTING 1 |
|
|
|
|
|
bool uselogfile = false; |
|
bool bX360 = false; |
|
|
|
struct AnalysisData |
|
{ |
|
CUtlSymbolTable symbols; |
|
}; |
|
|
|
static AnalysisData g_Analysis; |
|
|
|
IBaseFileSystem *filesystem = 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: captioncompiler closecaptionfile.txt\n\ |
|
\t-v = verbose output\n\ |
|
\t-l = log to file log.txt\n\ |
|
\ne.g.: kvc -l u:/xbox/game/hl2x/resource/closecaption_english.txt" ); |
|
|
|
// Exit app |
|
exit( 1 ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CheckLogFile( void ) |
|
{ |
|
if ( uselogfile ) |
|
{ |
|
_unlink( "log.txt" ); |
|
vprint( 0, " Outputting to log.txt\n" ); |
|
} |
|
} |
|
|
|
void PrintHeader() |
|
{ |
|
vprint( 0, "Valve Software - captioncompiler.exe (%s)\n", __DATE__ ); |
|
vprint( 0, "--- Close Caption File compiler ---\n" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// The application object |
|
//----------------------------------------------------------------------------- |
|
class CCompileCaptionsApp : public CTier3SteamApp |
|
{ |
|
typedef CTier3SteamApp 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(); |
|
|
|
void CompileCaptionFile( char const *infile, char const *outfile ); |
|
void DescribeCaptions( char const *file ); |
|
}; |
|
|
|
|
|
bool CCompileCaptionsApp::Create() |
|
{ |
|
SpewOutputFunc( SpewFunc ); |
|
SpewActivate( "kvc", 2 ); |
|
|
|
AppSystemInfo_t appSystems[] = |
|
{ |
|
{ "vgui2.dll", VGUI_IVGUI_INTERFACE_VERSION }, |
|
{ "", "" } // Required to terminate the list |
|
}; |
|
|
|
return AddSystems( appSystems ); |
|
} |
|
|
|
void CCompileCaptionsApp::Destroy() |
|
{ |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets up the game path |
|
//----------------------------------------------------------------------------- |
|
bool CCompileCaptionsApp::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 CCompileCaptionsApp::PreInit( ) |
|
{ |
|
if ( !BaseClass::PreInit() ) |
|
return false; |
|
|
|
g_pFileSystem = g_pFullFileSystem; |
|
if ( !g_pFileSystem || !g_pVGui || !g_pVGuiLocalize ) |
|
{ |
|
Error( "Unable to load required library interface!\n" ); |
|
return false; |
|
} |
|
|
|
// 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 CCompileCaptionsApp::PostShutdown() |
|
{ |
|
g_pFileSystem = NULL; |
|
BaseClass::PostShutdown(); |
|
} |
|
|
|
void CCompileCaptionsApp::CompileCaptionFile( char const *infile, char const *outfile ) |
|
{ |
|
StringIndex_t maxindex = (StringIndex_t)-1; |
|
int maxunicodesize = 0; |
|
int totalsize = 0; |
|
|
|
int c = 0; |
|
|
|
int curblock = 0; |
|
int usedBytes = 0; |
|
int blockSize = MAX_BLOCK_SIZE; |
|
|
|
int freeSpace = 0; |
|
|
|
CUtlVector< CaptionLookup_t > directory; |
|
CUtlBuffer data; |
|
|
|
CUtlRBTree< unsigned int > hashcollision( 0, 0, DefLessFunc( unsigned int ) ); |
|
|
|
for ( StringIndex_t i = g_pVGuiLocalize->GetFirstStringIndex(); i != INVALID_LOCALIZE_STRING_INDEX; i = g_pVGuiLocalize->GetNextStringIndex( i ), ++c ) |
|
{ |
|
char const *entryName = g_pVGuiLocalize->GetNameByIndex( i ); |
|
CaptionLookup_t entry; |
|
entry.SetHash( entryName ); |
|
|
|
// vprint( 0, "%d / %d: %s == %u\n", c, i, g_pVGuiLocalize->GetNameByIndex( i ), entry.hash ); |
|
|
|
if ( hashcollision.Find( entry.hash ) != hashcollision.InvalidIndex() ) |
|
{ |
|
Error( "Hash name collision on %s!!!\n", g_pVGuiLocalize->GetNameByIndex( i ) ); |
|
} |
|
|
|
hashcollision.Insert( entry.hash ); |
|
|
|
const wchar_t *text = g_pVGuiLocalize->GetValueByIndex( i ); |
|
if ( verbose ) |
|
{ |
|
vprint( 0, "Processing: '%30.30s' = '%S'\n", entryName, text ); |
|
} |
|
int len = text ? ( wcslen( text ) + 1 ) * sizeof( short ) : 0; |
|
if ( len > maxunicodesize ) |
|
{ |
|
maxindex = i; |
|
maxunicodesize = len; |
|
} |
|
|
|
if ( len > blockSize ) |
|
{ |
|
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 ), |
|
entryName, len, blockSize ); |
|
} |
|
totalsize += len; |
|
|
|
if ( usedBytes + len >= blockSize ) |
|
{ |
|
++curblock; |
|
|
|
int leftover = ( blockSize - usedBytes ); |
|
|
|
totalsize += leftover; |
|
|
|
freeSpace += leftover; |
|
|
|
while ( --leftover >= 0 ) |
|
{ |
|
data.PutChar( 0 ); |
|
} |
|
|
|
usedBytes = len; |
|
entry.offset = 0; |
|
|
|
data.Put( (const void *)text, len ); |
|
} |
|
else |
|
{ |
|
entry.offset = usedBytes; |
|
usedBytes += len; |
|
data.Put( (const void *)text, len ); |
|
} |
|
|
|
entry.length = len; |
|
entry.blockNum = curblock; |
|
|
|
directory.AddToTail( entry ); |
|
} |
|
|
|
int leftover = ( blockSize - usedBytes ); |
|
totalsize += leftover; |
|
freeSpace += leftover; |
|
while ( --leftover >= 0 ) |
|
{ |
|
data.PutChar( 0 ); |
|
} |
|
|
|
vprint( 0, "Found %i strings in '%s'\n", c, infile ); |
|
|
|
if ( maxindex != INVALID_LOCALIZE_STRING_INDEX ) |
|
{ |
|
vprint( 0, "Longest string '%s' = (%i) bytes average(%.3f)\n%", |
|
g_pVGuiLocalize->GetNameByIndex( maxindex ), maxunicodesize, (float)totalsize/(float)c ); |
|
} |
|
|
|
vprint( 0, "%d blocks (%d bytes each), %d bytes wasted (%.3f per block average), total bytes %d\n", |
|
curblock + 1, blockSize, freeSpace, (float)freeSpace/(float)( curblock + 1 ), totalsize ); |
|
|
|
vprint( 0, "directory size %d entries, %d bytes, data size %d bytes\n", |
|
directory.Count(), directory.Count() * sizeof( CaptionLookup_t ), data.TellPut() ); |
|
|
|
CompiledCaptionHeader_t header; |
|
header.magic = COMPILED_CAPTION_FILEID; |
|
header.version = COMPILED_CAPTION_VERSION; |
|
header.numblocks = curblock + 1; |
|
header.blocksize = blockSize; |
|
header.directorysize = directory.Count(); |
|
header.dataoffset = 0; |
|
|
|
// Now write the outfile |
|
CUtlBuffer out; |
|
out.Put( &header, sizeof( header ) ); |
|
out.Put( directory.Base(), directory.Count() * sizeof( CaptionLookup_t ) ); |
|
int curOffset = out.TellPut(); |
|
// Round it up to the next 512 byte boundary |
|
int nBytesDestBuffer = AlignValue( curOffset, 512 ); // align to HD sector |
|
int nPadding = nBytesDestBuffer - curOffset; |
|
while ( --nPadding >= 0 ) |
|
{ |
|
out.PutChar( 0 ); |
|
} |
|
out.Put( data.Base(), data.TellPut() ); |
|
|
|
// Write out a corrected header |
|
header.dataoffset = nBytesDestBuffer; |
|
int savePos = out.TellPut(); |
|
out.SeekPut( CUtlBuffer::SEEK_HEAD, 0 ); |
|
out.Put( &header, sizeof( header ) ); |
|
out.SeekPut( CUtlBuffer::SEEK_HEAD, savePos ); |
|
|
|
g_pFullFileSystem->WriteFile( outfile, NULL, out ); |
|
|
|
// Jeep: this function no longer exisits |
|
/*if ( bX360 ) |
|
{ |
|
UpdateOrCreateCaptionFile_X360( g_pFullFileSystem, outfile, NULL, true ); |
|
}*/ |
|
} |
|
|
|
void CCompileCaptionsApp::DescribeCaptions( char const *file ) |
|
{ |
|
CUtlBuffer buf; |
|
if ( !g_pFullFileSystem->ReadFile( file, NULL, buf ) ) |
|
{ |
|
Error( "Unable to read '%s' into buffer\n", file ); |
|
} |
|
|
|
CompiledCaptionHeader_t header; |
|
buf.Get( &header, sizeof( header ) ); |
|
if ( header.magic != COMPILED_CAPTION_FILEID ) |
|
Error( "Invalid file id for %s\n", file ); |
|
if ( header.version != COMPILED_CAPTION_VERSION ) |
|
Error( "Invalid file version for %s\n", file ); |
|
|
|
// Read the directory |
|
CUtlSortVector< CaptionLookup_t, CCaptionLookupLess > directory; |
|
directory.EnsureCapacity( header.directorysize ); |
|
directory.CopyArray( (const CaptionLookup_t *)buf.PeekGet(), header.directorysize ); |
|
directory.RedoSort( true ); |
|
buf.SeekGet( CUtlBuffer::SEEK_HEAD, header.dataoffset ); |
|
|
|
int i; |
|
CUtlVector< CaptionBlock_t > blocks; |
|
for ( i = 0; i < header.numblocks; ++i ) |
|
{ |
|
CaptionBlock_t& newBlock = blocks[ blocks.AddToTail() ]; |
|
Q_memset( newBlock.data, 0, sizeof( newBlock.data ) ); |
|
buf.Get( newBlock.data, header.blocksize ); |
|
} |
|
|
|
CUtlMap< unsigned int, StringIndex_t > inverseMap( 0, 0, DefLessFunc( unsigned int ) ); |
|
for ( StringIndex_t idx = g_pVGuiLocalize->GetFirstStringIndex(); idx != INVALID_LOCALIZE_STRING_INDEX; idx = g_pVGuiLocalize->GetNextStringIndex( idx ) ) |
|
{ |
|
const char *name = g_pVGuiLocalize->GetNameByIndex( idx ); |
|
CaptionLookup_t dummy; |
|
dummy.SetHash( name ); |
|
|
|
inverseMap.Insert( dummy.hash, idx ); |
|
} |
|
|
|
// Now print everything out... |
|
for ( i = 0; i < header.directorysize; ++i ) |
|
{ |
|
const CaptionLookup_t& entry = directory[ i ]; |
|
char const *name = g_pVGuiLocalize->GetNameByIndex( inverseMap.Element( inverseMap.Find( entry.hash ) ) ); |
|
const CaptionBlock_t& block = blocks[ entry.blockNum ]; |
|
const wchar_t *data = (const wchar_t *)&block.data[ entry.offset ]; |
|
wchar_t *temp = ( wchar_t * )_alloca( entry.length * sizeof( short ) ); |
|
wcsncpy( temp, data, ( entry.length / sizeof( short ) ) - 1 ); |
|
|
|
vprint( 0, "%3.3d: (%40.40s) hash(%15.15u), block(%4.4d), offset(%4.4d), len(%4.4d) %S\n", |
|
i, name, entry.hash, entry.blockNum, entry.offset, entry.length, temp ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// main application |
|
//----------------------------------------------------------------------------- |
|
int CCompileCaptionsApp::Main() |
|
{ |
|
CUtlVector< CUtlSymbol > worklist; |
|
|
|
int i = 1; |
|
for ( i ; 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 'x': |
|
bX360 = true; |
|
break; |
|
case 'g': // -game |
|
++i; |
|
break; |
|
default: |
|
printusage(); |
|
break; |
|
} |
|
} |
|
else if ( i != 0 ) |
|
{ |
|
char fn[ 512 ]; |
|
Q_strncpy( fn, CommandLine()->GetParm( i ), sizeof( fn ) ); |
|
Q_FixSlashes( fn ); |
|
Q_strlower( fn ); |
|
|
|
CUtlSymbol sym; |
|
sym = fn; |
|
worklist.AddToTail( sym ); |
|
} |
|
} |
|
|
|
if ( CommandLine()->ParmCount() < 2 || ( i != CommandLine()->ParmCount() ) || worklist.Count() != 1 ) |
|
{ |
|
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 ]; |
|
if ( Q_stristr( worklist[ worklist.Count() - 1 ].String(), gamedir ) ) |
|
{ |
|
Q_strncpy( outfile, &worklist[ worklist.Count() - 1 ].String()[ Q_strlen( gamedir ) ] , sizeof( outfile ) ); |
|
} |
|
else |
|
{ |
|
Q_snprintf( outfile, sizeof( outfile ), "resource\\%s", worklist[ worklist.Count() - 1 ].String() ); |
|
} |
|
|
|
char infile[ 512 ]; |
|
Q_strncpy( infile, outfile, sizeof( infile ) ); |
|
|
|
Q_SetExtension( outfile, ".dat", sizeof( outfile ) ); |
|
|
|
vprint( 0, "gamedir[ %s ]\n", gamedir ); |
|
vprint( 0, "infile[ %s ]\n", infile ); |
|
vprint( 0, "outfile[ %s ]\n", outfile ); |
|
|
|
g_pFullFileSystem->AddSearchPath( binaries, "EXECUTABLE_PATH" ); |
|
|
|
if ( !g_pVGuiLocalize->AddFile( infile, "MOD", false ) ) |
|
{ |
|
Error( "Unable to add localization file '%s'\n", infile ); |
|
} |
|
|
|
vprint( 0, " Compiling Captions for '%s'...\n", infile ); |
|
|
|
CompileCaptionFile( infile, outfile ); |
|
|
|
if ( verbose ) |
|
{ |
|
DescribeCaptions( outfile ); |
|
} |
|
|
|
g_pVGuiLocalize->RemoveAll(); |
|
|
|
return 0; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Main entry point |
|
//----------------------------------------------------------------------------- |
|
DEFINE_CONSOLE_STEAM_APPLICATION_OBJECT( CCompileCaptionsApp )
|
|
|