//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: .360.WAV Creation // //=====================================================================================// #include "MakeGameData.h" #ifndef NO_X360_XDK #include #endif #include "datamap.h" #include "sentence.h" #include "tier2/riff.h" #include "resample.h" #include "xwvfile.h" // all files are built for streaming compliance // allows for fastest runtime loading path // actual streaming or static state is determined by engine #define XBOX_DVD_SECTORSIZE 2048 #define XMA_BLOCK_SIZE 2048 // must be aligned to 1024 #define MAX_CHUNKS 256 // [0,100] #define XMA_HIGH_QUALITY 90 #define XMA_DEFAULT_QUALITY 75 #define XMA_MEDIUM_QUALITY 50 #define XMA_LOW_QUALITY 25 typedef struct { unsigned int id; int size; byte *pData; } chunk_t; struct conversion_t { const char *pSubDir; int quality; bool bForceTo22K; }; // default conversion rules conversion_t g_defaultConversionRules[] = { // subdir quality 22Khz { "", XMA_DEFAULT_QUALITY, false }, // default settings { "weapons", XMA_DEFAULT_QUALITY, false }, { "music", XMA_DEFAULT_QUALITY, false }, { "vo", XMA_MEDIUM_QUALITY, false }, { "npc", XMA_MEDIUM_QUALITY, false }, { "ambient", XMA_DEFAULT_QUALITY, false }, { "commentary", XMA_LOW_QUALITY, true }, { NULL }, }; // portal conversion rules conversion_t g_portalConversionRules[] = { // subdir quality 22Khz { "", XMA_DEFAULT_QUALITY, false }, // default settings { "commentary", XMA_LOW_QUALITY, true }, { NULL }, }; chunk_t g_chunks[MAX_CHUNKS]; int g_numChunks; extern IFileReadBinary *g_pSndIO; //----------------------------------------------------------------------------- // Purpose: chunk printer //----------------------------------------------------------------------------- void PrintChunk( unsigned int chunkName, int size ) { char c[4]; for ( int i=0; i<4; i++ ) { c[i] = ( chunkName >> i*8 ) & 0xFF; if ( !c[i] ) c[i] = ' '; } Msg( "%c%c%c%c: %d bytes\n", c[0], c[1], c[2], c[3], size ); } //----------------------------------------------------------------------------- // Purpose: which chunks are supported, false to ignore //----------------------------------------------------------------------------- bool IsValidChunk( unsigned int chunkName ) { switch ( chunkName ) { case WAVE_DATA: case WAVE_CUE: case WAVE_SAMPLER: case WAVE_VALVEDATA: case WAVE_FMT: return true; } return false; } //----------------------------------------------------------------------------- // Purpose: align buffer //----------------------------------------------------------------------------- int AlignToBoundary( CUtlBuffer &buf, int alignment ) { int curPosition; int newPosition; byte padByte = 0; buf.SeekPut( CUtlBuffer::SEEK_TAIL, 0 ); curPosition = buf.TellPut(); if ( alignment <= 1 ) return curPosition; // advance to aligned position newPosition = AlignValue( curPosition, alignment ); buf.EnsureCapacity( newPosition ); // write empty for ( int i=0; isize ); memcpy( buf.Base(), pChunk->pData, pChunk->size ); buf.SeekPut( CUtlBuffer::SEEK_HEAD, pChunk->size ); pSentence = new CSentence(); // Make binary version of VDAT // Throws all phonemes into one word, discards sentence memory, etc. pSentence->InitFromDataChunk( buf.Base(), buf.TellPut() ); pSentence->MakeRuntimeOnly(); CUtlBuffer binaryBuffer( 0, 0, 0 ); binaryBuffer.SetBigEndian( true ); pSentence->CacheSaveToBuffer( binaryBuffer, CACHED_SENTENCE_VERSION_ALIGNED ); delete pSentence; unsigned int compressedSize = 0; unsigned char *pCompressedOutput = LZMA_OpportunisticCompress( (unsigned char *)binaryBuffer.Base(), binaryBuffer.TellPut(), &compressedSize ); if ( pCompressedOutput ) { if ( !g_bQuiet ) { Msg( "CompressVDAT: Compressed %d to %d\n", binaryBuffer.TellPut(), compressedSize ); } free( pChunk->pData ); pChunk->size = compressedSize; pChunk->pData = pCompressedOutput; } else { // save binary VDAT as-is free( pChunk->pData ); pChunk->size = binaryBuffer.TellPut(); pChunk->pData = (byte *)malloc( pChunk->size ); memcpy( pChunk->pData, binaryBuffer.Base(), pChunk->size ); } // success return true; } //----------------------------------------------------------------------------- // Purpose: read chunks into provided array //----------------------------------------------------------------------------- bool ReadChunks( const char *pFileName, int &numChunks, chunk_t chunks[MAX_CHUNKS] ) { numChunks = 0; InFileRIFF riff( pFileName, *g_pSndIO ); if ( riff.RIFFName() != RIFF_WAVE ) { return false; } IterateRIFF walk( riff, riff.RIFFSize() ); while ( walk.ChunkAvailable() ) { chunks[numChunks].id = walk.ChunkName(); chunks[numChunks].size = walk.ChunkSize(); int size = chunks[numChunks].size; if ( walk.ChunkName() == WAVE_FMT && size < sizeof( WAVEFORMATEXTENSIBLE ) ) { // format chunks are variable and cast to different structures // ensure the data footprint is at least the structure we want to manipulate size = sizeof( WAVEFORMATEXTENSIBLE ); } chunks[numChunks].pData = (byte *)malloc( size ); memset( chunks[numChunks].pData, 0, size ); walk.ChunkRead( chunks[numChunks].pData ); numChunks++; if ( numChunks >= MAX_CHUNKS ) return false; walk.ChunkNext(); } // success return true; } //----------------------------------------------------------------------------- // Purpose: promote pcm 8 bit to 16 bit pcm //----------------------------------------------------------------------------- void ConvertPCMDataChunk8To16( chunk_t *pFormatChunk, chunk_t *pDataChunk ) { WAVEFORMATEX *pFormat = (WAVEFORMATEX*)pFormatChunk->pData; int sampleSize = ( pFormat->nChannels * pFormat->wBitsPerSample ) >> 3; int sampleCount = pDataChunk->size / sampleSize; int outputSize = sizeof( short ) * ( sampleCount * pFormat->nChannels ); short *pOut = (short *)malloc( outputSize ); // in-place convert data from 8-bits to 16-bits Convert8To16( pDataChunk->pData, pOut, sampleCount, pFormat->nChannels ); free( pDataChunk->pData ); pDataChunk->pData = (byte *)pOut; pDataChunk->size = outputSize; pFormat->wFormatTag = WAVE_FORMAT_PCM; pFormat->nBlockAlign = 2 * pFormat->nChannels; pFormat->wBitsPerSample = 16; pFormat->nAvgBytesPerSec = 2 * pFormat->nSamplesPerSec * pFormat->nChannels; } //----------------------------------------------------------------------------- // Purpose: convert adpcm to 16 bit pcm //----------------------------------------------------------------------------- void ConvertADPCMDataChunkTo16( chunk_t *pFormatChunk, chunk_t *pDataChunk ) { WAVEFORMATEX *pFormat = (WAVEFORMATEX*)pFormatChunk->pData; int sampleCount = ADPCMSampleCount( (byte *)pFormat, pDataChunk->pData, pDataChunk->size ); int outputSize = sizeof( short ) * sampleCount * pFormat->nChannels; short *pOut = (short *)malloc( outputSize ); // convert to PCM 16bit format DecompressADPCMSamples( (byte*)pFormat, (byte*)pDataChunk->pData, pDataChunk->size, pOut ); free( pDataChunk->pData ); pDataChunk->pData = (byte *)pOut; pDataChunk->size = outputSize; pFormat->wFormatTag = WAVE_FORMAT_PCM; pFormat->nBlockAlign = 2 * pFormat->nChannels; pFormat->wBitsPerSample = 16; pFormat->nAvgBytesPerSec = 2 * pFormat->nSamplesPerSec * pFormat->nChannels; pFormatChunk->size = 16; } //----------------------------------------------------------------------------- // Purpose: Decimate to 22K //----------------------------------------------------------------------------- void ConvertPCMDataChunk16To22K( chunk_t *pFormatChunk, chunk_t *pDataChunk ) { WAVEFORMATEX *pFormat = (WAVEFORMATEX*)pFormatChunk->pData; if ( pFormat->nSamplesPerSec != 44100 || pFormat->wBitsPerSample != 16 || pFormat->wFormatTag != WAVE_FORMAT_PCM ) { // not in expected format return; } int sampleSize = ( pFormat->nChannels * pFormat->wBitsPerSample ) >> 3; int sampleCount = pDataChunk->size / sampleSize; short *pOut = (short *)malloc( sizeof( short ) * ( sampleCount * pFormat->nChannels ) ); DecimateSampleRateBy2_16( (short *)pDataChunk->pData, pOut, sampleCount, pFormat->nChannels ); free( pDataChunk->pData ); pDataChunk->pData = (byte *)pOut; pDataChunk->size = sizeof( short ) * ( sampleCount/2 * pFormat->nChannels ); pFormat->nSamplesPerSec = 22050; pFormat->nBlockAlign = 2 * pFormat->nChannels; pFormat->nAvgBytesPerSec = 2 * pFormat->nSamplesPerSec * pFormat->nChannels; } //----------------------------------------------------------------------------- // Purpose: determine loop start //----------------------------------------------------------------------------- int FindLoopStart( int samplerChunk, int cueChunk ) { int loopStartFromCue = -1; int loopStartFromSampler = -1; if ( cueChunk != -1 ) { struct cuechunk_t { unsigned int dwName; unsigned int dwPosition; unsigned int fccChunk; unsigned int dwChunkStart; unsigned int dwBlockStart; unsigned int dwSampleOffset; }; struct cueRIFF_t { int cueCount; cuechunk_t cues[1]; }; cueRIFF_t *pCue = (cueRIFF_t *)g_chunks[cueChunk].pData; if ( pCue->cueCount > 0 ) { loopStartFromCue = pCue->cues[0].dwSampleOffset; } } if ( samplerChunk != -1 ) { struct SampleLoop { unsigned int dwIdentifier; unsigned int dwType; unsigned int dwStart; unsigned int dwEnd; unsigned int dwFraction; unsigned int dwPlayCount; }; struct samplerchunk_t { unsigned int dwManufacturer; unsigned int dwProduct; unsigned int dwSamplePeriod; unsigned int dwMIDIUnityNote; unsigned int dwMIDIPitchFraction; unsigned int dwSMPTEFormat; unsigned int dwSMPTEOffset; unsigned int cSampleLoops; unsigned int cbSamplerData; struct SampleLoop Loops[1]; }; // assume that the loop end is the sample end // assume that only the first loop is relevant samplerchunk_t *pSampler = (samplerchunk_t *)g_chunks[samplerChunk].pData; if ( pSampler->cSampleLoops > 0 ) { // only support normal forward loops if ( pSampler->Loops[0].dwType == 0 ) { loopStartFromSampler = pSampler->Loops[0].dwStart; } } } return ( max( loopStartFromCue, loopStartFromSampler ) ); } //----------------------------------------------------------------------------- // Purpose: returns chunk, -1 if not found //----------------------------------------------------------------------------- int FindChunk( unsigned int id ) { int i; for ( i=0; inAvgBytesPerSec = pFormat->nSamplesPerSec * pFormat->nChannels * 2; pFormat->nBlockAlign = 2 * pFormat->nChannels; pFormat->cbSize = 0; XMAENCODERSTREAM inputStream = { 0 }; WAVEFORMATEXTENSIBLE wfx; Assert( g_chunks[formatChunk].size <= sizeof( WAVEFORMATEXTENSIBLE ) ); memcpy( &wfx, g_chunks[formatChunk].pData, g_chunks[formatChunk].size ); if ( g_chunks[formatChunk].size < sizeof( WAVEFORMATEXTENSIBLE ) ) { memset( (unsigned char*)&wfx + g_chunks[formatChunk].size, 0, sizeof( WAVEFORMATEXTENSIBLE ) - g_chunks[formatChunk].size ); } memcpy( &inputStream.Format, &wfx, sizeof( WAVEFORMATEX ) ); inputStream.pBuffer = g_chunks[dataChunk].pData; inputStream.BufferSize = g_chunks[dataChunk].size; if ( loopStart != -1 ) { // can only support a single loop point until end of file inputStream.LoopStart = loopStart; inputStream.LoopLength = inputStream.BufferSize / ( pFormat->nChannels * sizeof( short ) ) - loopStart; } void *pXMAData = NULL; DWORD XMADataSize = 0; XMA2WAVEFORMAT *pXMA2Format = NULL; DWORD XMA2FormatSize = 0; DWORD *pXMASeekTable = NULL; DWORD XMASeekTableSize = 0; HRESULT hr = S_OK; DWORD xmaFlags = XMAENCODER_NOFILTER; if ( loopStart != -1 ) { xmaFlags |= XMAENCODER_LOOP; } int numAttempts = 1; while ( numAttempts < 10 ) { hr = XMA2InMemoryEncoder( 1, &inputStream, quality, xmaFlags, XMA_BLOCK_SIZE/1024, &pXMAData, &XMADataSize, &pXMA2Format, &XMA2FormatSize, &pXMASeekTable, &XMASeekTableSize ); if ( !FAILED( hr ) ) break; // make small jumps quality += 5; if ( quality > 100 ) quality = 100; if ( !g_bQuiet ) { Msg( "XMA Encoding Error on '%s', Attempting increasing quality to %d\n", pDebugName, quality ); } numAttempts++; pXMAData = NULL; XMADataSize = 0; pXMA2Format = NULL; XMA2FormatSize = 0; pXMASeekTable = NULL; XMASeekTableSize = 0; } if ( FAILED( hr ) ) { // unrecoverable return false; } else if ( numAttempts > 1 ) { if ( !g_bQuiet ) { Msg( "XMA Encoding Success on '%s' at quality %d\n", pDebugName, quality ); } } DWORD loopBlock = 0; DWORD numLeadingSamples = 0; DWORD numTrailingSamples = 0; if ( loopStart != -1 ) { // calculate start block/offset DWORD loopBlockStartIndex = 0; DWORD loopBlockStartOffset = 0; if ( !SampleToXMABlockOffset( BigLong( pXMA2Format->LoopBegin ), pXMASeekTable, XMASeekTableSize/sizeof( DWORD ), &loopBlockStartIndex, &loopBlockStartOffset ) ) { // could not determine loop point, out of range of encoded samples Msg( "XMA Loop Encoding Error on '%s', loop %d\n", pDebugName, loopStart ); return false; } loopBlock = loopBlockStartIndex; numLeadingSamples = loopBlockStartOffset; if ( BigLong( pXMA2Format->LoopEnd ) < BigLong( pXMA2Format->SamplesEncoded ) ) { // calculate end block/offset DWORD loopBlockEndIndex = 0; DWORD loopBlockEndOffset = 0; if ( !SampleToXMABlockOffset( BigLong( pXMA2Format->LoopEnd ), pXMASeekTable, XMASeekTableSize/sizeof( DWORD ), &loopBlockEndIndex, &loopBlockEndOffset ) ) { // could not determine loop point, out of range of encoded samples Msg( "XMA Loop Encoding Error on '%s', loop %d\n", pDebugName, loopStart ); return false; } if ( loopBlockEndIndex != BigLong( pXMA2Format->BlockCount ) - 1 ) { // end block MUST be last block Msg( "XMA Loop Encoding Error on '%s', block end is %d/%d\n", pDebugName, loopBlockEndOffset, BigLong( pXMA2Format->BlockCount ) ); return false; } numTrailingSamples = BigLong( pXMA2Format->SamplesEncoded ) - BigLong( pXMA2Format->LoopEnd ); } // check for proper encoding range if ( loopBlock > 32767 ) { Msg( "XMA Loop Encoding Error on '%s', loop block exceeds 16 bits %d\n", pDebugName, loopBlock ); return false; } if ( numLeadingSamples > 32767 ) { Msg( "XMA Loop Encoding Error on '%s', leading samples exceeds 16 bits %d\n", pDebugName, numLeadingSamples ); return false; } if ( numTrailingSamples > 32767 ) { Msg( "XMA Loop Encoding Error on '%s', trailing samples exceeds 16 bits %d\n", pDebugName, numTrailingSamples ); return false; } } xwvHeader_t header; memset( &header, 0, sizeof( xwvHeader_t ) ); int seekTableSize = 0; if ( vdatSize || bIsVoiceOver ) { // save the optional seek table only for vdat or vo // the seek table size is expected to be derived by this calculation seekTableSize = ( XMADataSize / XMA_BYTES_PER_PACKET ) * sizeof( int ); if ( seekTableSize != XMASeekTableSize ) { Msg( "XMA Error: Unexpected seek table calculation in '%s'!", pDebugName ); return false; } } if ( loopStart != -1 && ( vdatSize || bIsVoiceOver ) ) { Msg( "XMA Warning: Unexpected loop in vo data '%s'!", pDebugName ); // do not write the seek table for looping sounds seekTableSize = 0; } header.id = BigLong( XWV_ID ); header.version = BigLong( XWV_VERSION ); header.headerSize = BigLong( sizeof( xwvHeader_t ) ); header.staticDataSize = BigLong( seekTableSize + vdatSize ); header.dataOffset = BigLong( AlignValue( sizeof( xwvHeader_t) + seekTableSize + vdatSize, XBOX_DVD_SECTORSIZE ) ); header.dataSize = BigLong( XMADataSize ); // track the XMA number of samples that will get decoded // which is NOT the same as what the source actually encoded header.numDecodedSamples = pXMA2Format->SamplesEncoded; if ( loopStart != -1 ) { // the loop start is in source space (now meaningless), need the loop in XMA decoding sample space header.loopStart = pXMA2Format->LoopBegin; } else { header.loopStart = BigLong( -1 ); } header.loopBlock = BigShort( (unsigned short)loopBlock ); header.numLeadingSamples = BigShort( (unsigned short)numLeadingSamples ); header.numTrailingSamples = BigShort( (unsigned short)numTrailingSamples ); header.vdatSize = BigShort( (short)vdatSize ); header.format = XWV_FORMAT_XMA; header.bitsPerSample = 16; header.SetSampleRate( pFormat->nSamplesPerSec ); header.SetChannels( pFormat->nChannels ); header.quality = quality; header.bHasSeekTable = ( seekTableSize != 0 ); // output header targetBuff.Put( &header, sizeof( xwvHeader_t ) ); // output optional seek table if ( seekTableSize ) { // seek table is already in big-endian format targetBuff.Put( pXMASeekTable, seekTableSize ); } // output vdat if ( vdatSize ) { targetBuff.Put( g_chunks[vdatChunk].pData, g_chunks[vdatChunk].size ); } AlignToBoundary( targetBuff, XBOX_DVD_SECTORSIZE ); // write data targetBuff.Put( pXMAData, XMADataSize ); // pad to EOF AlignToBoundary( targetBuff, XBOX_DVD_SECTORSIZE ); free( pXMAData ); free( pXMA2Format ); free( pXMASeekTable ); // xma encoder leaves its temporary files, we'll delete scriptlib->DeleteTemporaryFiles( "LoopStrm*" ); scriptlib->DeleteTemporaryFiles( "EncStrm*" ); return true; #endif } bool EncodeAsPCM( const char *pTargetName, CUtlBuffer &targetBuff ) { int formatChunk = FindChunk( WAVE_FMT ); int dataChunk = FindChunk( WAVE_DATA ); if ( formatChunk == -1 || dataChunk == -1 ) { // huh? these should have been pre-validated return false; } WAVEFORMATEX *pFormat = (WAVEFORMATEX *)g_chunks[formatChunk].pData; if ( pFormat->wBitsPerSample != 16 ) { // huh? the input is expeted to be 16 bit PCM return false; } int vdatSize = 0; int vdatChunk = FindChunk( WAVE_VALVEDATA ); if ( vdatChunk != -1 ) { vdatSize = g_chunks[vdatChunk].size; } chunk_t *pDataChunk = &g_chunks[dataChunk]; xwvHeader_t header; memset( &header, 0, sizeof( xwvHeader_t ) ); int sampleSize = pFormat->nChannels * sizeof( short ); int sampleCount = pDataChunk->size / sampleSize; header.id = BigLong( XWV_ID ); header.version = BigLong( XWV_VERSION ); header.headerSize = BigLong( sizeof( xwvHeader_t ) ); header.staticDataSize = BigLong( vdatSize ); header.dataOffset = BigLong( AlignValue( sizeof( xwvHeader_t) + vdatSize, XBOX_DVD_SECTORSIZE ) ); header.dataSize = BigLong( pDataChunk->size ); header.numDecodedSamples = BigLong( sampleCount ); header.loopStart = BigLong( -1 ); header.loopBlock = 0; header.numLeadingSamples = 0; header.numTrailingSamples = 0; header.vdatSize = BigShort( (short)vdatSize ); header.format = XWV_FORMAT_PCM; header.bitsPerSample = 16; header.SetSampleRate( pFormat->nSamplesPerSec ); header.SetChannels( pFormat->nChannels ); header.quality = 100; // output header targetBuff.Put( &header, sizeof( xwvHeader_t ) ); // output vdat if ( vdatSize ) { targetBuff.Put( g_chunks[vdatChunk].pData, g_chunks[vdatChunk].size ); } AlignToBoundary( targetBuff, XBOX_DVD_SECTORSIZE ); for ( int i = 0; i < sampleCount * pFormat->nChannels; i++ ) { ((short *)pDataChunk->pData)[i] = BigShort( ((short *)pDataChunk->pData)[i] ); } // write data targetBuff.Put( pDataChunk->pData, pDataChunk->size ); // pad to EOF AlignToBoundary( targetBuff, XBOX_DVD_SECTORSIZE ); return true; } //----------------------------------------------------------------------------- // Purpose: read source, do work, and write to target //----------------------------------------------------------------------------- bool CreateTargetFile_WAV( const char *pSourceName, const char *pTargetName, bool bWriteToZip ) { g_numChunks = 0; // resolve relative source to absolute path char fullSourcePath[MAX_PATH]; if ( _fullpath( fullSourcePath, pSourceName, sizeof( fullSourcePath ) ) ) { pSourceName = fullSourcePath; } if ( !ReadChunks( pSourceName, g_numChunks, g_chunks ) ) { Msg( "No RIFF Chunks on '%s'\n", pSourceName ); return false; } int formatChunk = FindChunk( WAVE_FMT ); if ( formatChunk == -1 ) { Msg( "RIFF Format Chunk not found on '%s'\n", pSourceName ); return false; } int dataChunk = FindChunk( WAVE_DATA ); if ( dataChunk == -1 ) { Msg( "RIFF Data Chunk not found on '%s'\n", pSourceName ); return false; } // get the conversion rules conversion_t *pConversion = g_defaultConversionRules; if ( V_stristr( g_szModPath, "\\portal" ) ) { pConversion = g_portalConversionRules; } // conversion rules are based on matching subdir for ( int i=1; ;i++ ) { char subString[MAX_PATH]; if ( !pConversion[i].pSubDir ) { // end of list break; } sprintf( subString, "\\%s\\", pConversion[i].pSubDir ); if ( V_stristr( pSourceName, subString ) ) { // use matched conversion rules pConversion = &pConversion[i]; break; } } bool bForceTo22K = pConversion->bForceTo22K; int quality = pConversion->quality; // cannot trust the localization depots to have matched their sources // cannot allow 44K if ( IsLocalizedFile( pSourceName ) ) { bForceTo22K = true; } // classify strict vo from /sound/vo only bool bIsVoiceOver = V_stristr( pSourceName, "\\sound\\vo\\" ) != NULL; // can override default settings quality = CommandLine()->ParmValue( "-xmaquality", quality ); if ( quality < 0 ) quality = 0; else if ( quality > 100 ) quality = 100; if ( !g_bQuiet ) { Msg( "Encoding quality: %d on '%s'\n", quality, pSourceName ); } int vdatSize = 0; int vdatChunk = FindChunk( WAVE_VALVEDATA ); if ( vdatChunk != -1 ) { // compile to optimal block if ( !CompressVDAT( &g_chunks[vdatChunk] ) ) { Msg( "Compress VDAT Error on '%s'\n", pSourceName ); return false; } vdatSize = g_chunks[vdatChunk].size; } // for safety (not trusting their decoding) and simplicity convert all data to 16 bit PCM before encoding WAVEFORMATEX *pFormat = (WAVEFORMATEX *)g_chunks[formatChunk].pData; if ( ( pFormat->wFormatTag == WAVE_FORMAT_PCM ) ) { if ( pFormat->wBitsPerSample == 8 ) { ConvertPCMDataChunk8To16( &g_chunks[formatChunk], &g_chunks[dataChunk] ); } } else if ( pFormat->wFormatTag == WAVE_FORMAT_ADPCM ) { ConvertADPCMDataChunkTo16( &g_chunks[formatChunk], &g_chunks[dataChunk] ); } else { Msg( "Unknown RIFF Format on '%s'\n", pSourceName ); return false; } // optionally decimate to 22K if ( pFormat->nSamplesPerSec == 44100 && bForceTo22K ) { if ( !g_bQuiet ) { Msg( "Converting to 22K '%s'\n", pSourceName ); } ConvertPCMDataChunk16To22K( &g_chunks[formatChunk], &g_chunks[dataChunk] ); } CUtlBuffer targetBuff; bool bSuccess; bSuccess = EncodeAsXMA( pSourceName, targetBuff, quality, bIsVoiceOver ); if ( bSuccess ) { WriteBufferToFile( pTargetName, targetBuff, bWriteToZip, g_WriteModeForConversions ); } // release data for ( int i = 0; i < g_numChunks; i++ ) { free( g_chunks[i].pData ); } return bSuccess; } //----------------------------------------------------------------------------- // Purpose: MP3's are already pre-converted into .360.wav //----------------------------------------------------------------------------- bool CreateTargetFile_MP3( const char *pSourceName, const char *pTargetName, bool bWriteToZip ) { CUtlBuffer targetBuffer; // ignore the .mp3 source, the .360.wav target should have been pre-converted, checked in, and exist // use the expected target as the source if ( !scriptlib->ReadFileToBuffer( pTargetName, targetBuffer ) ) { // the .360.wav target does not exist // try again using a .wav version and convert from that char wavFilename[MAX_PATH]; V_StripExtension( pSourceName, wavFilename, sizeof( wavFilename ) ); V_SetExtension( wavFilename, ".wav", sizeof( wavFilename ) ); if ( scriptlib->DoesFileExist( wavFilename ) ) { if ( CreateTargetFile_WAV( wavFilename, pTargetName, bWriteToZip ) ) { return true; } } return false; } // no conversion to write, but possibly zipped bool bSuccess = WriteBufferToFile( pTargetName, targetBuffer, bWriteToZip, WRITE_TO_DISK_NEVER ); return bSuccess; } //----------------------------------------------------------------------------- // Get the preload data for a wav file //----------------------------------------------------------------------------- bool GetPreloadData_WAV( const char *pFilename, CUtlBuffer &fileBufferIn, CUtlBuffer &preloadBufferOut ) { xwvHeader_t *pHeader = ( xwvHeader_t * )fileBufferIn.Base(); if ( pHeader->id != ( unsigned int )BigLong( XWV_ID ) || pHeader->version != ( unsigned int )BigLong( XWV_VERSION ) || pHeader->headerSize != BigLong( sizeof( xwvHeader_t ) ) ) { // bad version Msg( "Can't preload: '%s', has bad version\n", pFilename ); return false; } // ensure caller's buffer is clean // caller determines preload size, via TellMaxPut() preloadBufferOut.Purge(); unsigned int preloadSize = BigLong( pHeader->headerSize ) + BigLong( pHeader->staticDataSize ); preloadBufferOut.Put( fileBufferIn.Base(), preloadSize ); return true; }