//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// // Avoid these warnings: #pragma warning(disable : 4512) // warning C4512: 'InFileRIFF' : assignment operator could not be generated #pragma warning(disable : 4514) // warning C4514: 'RIFFName' : unreferenced inline function has been removed #include "riff.h" #include #include #include "tier0/dbg.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #if 0 //----------------------------------------------------------------------------- // Purpose: Test code that implements the interface on stdio //----------------------------------------------------------------------------- class StdIOReadBinary : public IFileReadBinary { public: int open( const char *pFileName ) { return (int)fopen( pFileName, "rb" ); } int read( void *pOutput, int size, int file ) { FILE *fp = (FILE *)file; return fread( pOutput, size, 1, fp ); } void seek( int file, int pos ) { fseek( (FILE *)file, pos, SEEK_SET ); } unsigned int tell( int file ) { return ftell( (FILE *)file ); } unsigned int size( int file ) { FILE *fp = (FILE *)file; if ( !fp ) return 0; unsigned int pos = ftell( fp ); fseek( fp, 0, SEEK_END ); unsigned int size = ftell( fp ); fseek( fp, pos, SEEK_SET ); return size; } void close( int file ) { FILE *fp = (FILE *)file; fclose( fp ); } }; #endif #define RIFF_ID MAKEID('R','I','F','F') //----------------------------------------------------------------------------- // Purpose: Opens a RIFF file using the given I/O mechanism // Input : *pFileName // &io - I/O interface //----------------------------------------------------------------------------- InFileRIFF::InFileRIFF( const char *pFileName, IFileReadBinary &io ) : m_io(io) { m_file = m_io.open( pFileName ); int riff = 0; if ( !m_file ) { m_riffSize = 0; m_riffName = 0; return; } riff = ReadInt(); if ( riff != RIFF_ID ) { printf( "Not a RIFF File [%s]\n", pFileName ); m_riffSize = 0; } else { // we store size as size of all chunks // subtract off the RIFF form type (e.g. 'WAVE', 4 bytes) m_riffSize = ReadInt() - 4; m_riffName = ReadInt(); // HACKHACK: LWV files don't obey the RIFF format!!! // Do this or miss the linguistic chunks at the end. Lame! // subtract off 12 bytes for (RIFF, size, WAVE) m_riffSize = m_io.size( m_file ) - 12; } } //----------------------------------------------------------------------------- // Purpose: Close the file //----------------------------------------------------------------------------- InFileRIFF::~InFileRIFF( void ) { m_io.close( m_file ); } //----------------------------------------------------------------------------- // Purpose: read a 4-byte int out of the stream // Output : int = read value, default is zero //----------------------------------------------------------------------------- int InFileRIFF::ReadInt( void ) { int tmp = 0; m_io.read( &tmp, sizeof(int), m_file ); tmp = LittleLong( tmp ); return tmp; } //----------------------------------------------------------------------------- // Purpose: Read a block of binary data // Input : *pOutput - pointer to destination memory // dataSize - size of block to read // Output : int - number of bytes read //----------------------------------------------------------------------------- int InFileRIFF::ReadData( void *pOutput, int dataSize ) { int count = m_io.read( pOutput, dataSize, m_file ); return count; } //----------------------------------------------------------------------------- // Purpose: Gets the file position // Output : int (bytes from start of file) //----------------------------------------------------------------------------- int InFileRIFF::PositionGet( void ) { return m_io.tell( m_file ); } //----------------------------------------------------------------------------- // Purpose: Seek to file position // Input : position - bytes from start of file //----------------------------------------------------------------------------- void InFileRIFF::PositionSet( int position ) { m_io.seek( m_file, position ); } //----------------------------------------------------------------------------- // Purpose: Used to write a RIFF format file //----------------------------------------------------------------------------- OutFileRIFF::OutFileRIFF( const char *pFileName, IFileWriteBinary &io ) : m_io( io ) { m_file = m_io.create( pFileName ); if ( !m_file ) return; int riff = RIFF_ID; m_io.write( &riff, 4, m_file ); m_riffSize = 0; m_nNamePos = m_io.tell( m_file ); // Save room for the size and name now WriteInt( 0 ); // Write out the name WriteInt( RIFF_WAVE ); m_bUseIncorrectLISETLength = false; m_nLISETSize = 0; } OutFileRIFF::~OutFileRIFF( void ) { if ( !IsValid() ) return; unsigned int size = m_io.tell( m_file ) -8; m_io.seek( m_file, m_nNamePos ); if ( m_bUseIncorrectLISETLength ) { size = m_nLISETSize - 8; } WriteInt( size ); m_io.close( m_file ); } void OutFileRIFF::HasLISETData( int position ) { m_bUseIncorrectLISETLength = true; m_nLISETSize = position; } bool OutFileRIFF::WriteInt( int number ) { if ( !IsValid() ) return false; m_io.write( &number, sizeof( int ), m_file ); return true; } bool OutFileRIFF::WriteData( void *pOutput, int dataSize ) { if ( !IsValid() ) return false; m_io.write( pOutput, dataSize, m_file ); return true; } int OutFileRIFF::PositionGet( void ) { if ( !IsValid() ) return 0; return m_io.tell( m_file ); } void OutFileRIFF::PositionSet( int position ) { if ( !IsValid() ) return; m_io.seek( m_file, position ); } //----------------------------------------------------------------------------- // Purpose: Create an iterator for the given file // Input : &riff - riff file // size - size of file or sub-chunk //----------------------------------------------------------------------------- IterateRIFF::IterateRIFF( InFileRIFF &riff, int size ) : m_riff(riff), m_size(size) { if ( !m_riff.RIFFSize() ) { // bad file, just be an empty iterator ChunkClear(); return; } // get the position and parse a chunk m_start = riff.PositionGet(); ChunkSetup(); } //----------------------------------------------------------------------------- // Purpose: Set up a sub-chunk iterator // Input : &parent - parent iterator //----------------------------------------------------------------------------- IterateRIFF::IterateRIFF( IterateRIFF &parent ) : m_riff(parent.m_riff), m_size(parent.ChunkSize()) { m_start = parent.ChunkFilePosition(); ChunkSetup(); } //----------------------------------------------------------------------------- // Purpose: Parse the chunk at the current file position // This object will iterate over the sub-chunks of this chunk. // This makes for easy hierarchical parsing //----------------------------------------------------------------------------- void IterateRIFF::ChunkSetup( void ) { m_chunkPosition = m_riff.PositionGet(); m_chunkName = m_riff.ReadInt(); m_chunkSize = m_riff.ReadInt(); } //----------------------------------------------------------------------------- // Purpose: clear chunk setup, ChunkAvailable will return false //----------------------------------------------------------------------------- void IterateRIFF::ChunkClear( void ) { m_chunkSize = -1; } //----------------------------------------------------------------------------- // Purpose: If there are chunks left to read beyond this one, return true //----------------------------------------------------------------------------- bool IterateRIFF::ChunkAvailable( void ) { if ( m_chunkSize != -1 && m_chunkSize < 0x10000000 ) return true; return false; } //----------------------------------------------------------------------------- // Purpose: Go to the next chunk in the file, return true if there is one. //----------------------------------------------------------------------------- bool IterateRIFF::ChunkNext( void ) { if ( !ChunkAvailable() ) return false; int nextPos = m_chunkPosition + 8 + m_chunkSize; // chunks are aligned nextPos += m_chunkSize & 1; if ( nextPos >= (m_start + m_size) ) { ChunkClear(); return false; } m_riff.PositionSet( nextPos ); ChunkSetup(); return ChunkAvailable(); } //----------------------------------------------------------------------------- // Purpose: get the chunk FOURCC as an int // Output : unsigned int //----------------------------------------------------------------------------- unsigned int IterateRIFF::ChunkName( void ) { return m_chunkName; } //----------------------------------------------------------------------------- // Purpose: get the size of this chunk // Output : unsigned int //----------------------------------------------------------------------------- unsigned int IterateRIFF::ChunkSize( void ) { return m_chunkSize; } //----------------------------------------------------------------------------- // Purpose: Read the entire chunk into a buffer // Input : *pOutput - dest buffer // Output : int bytes read //----------------------------------------------------------------------------- int IterateRIFF::ChunkRead( void *pOutput ) { return m_riff.ReadData( pOutput, ChunkSize() ); } //----------------------------------------------------------------------------- // Purpose: Read a partial chunk (updates file position for subsequent partial reads). // Input : *pOutput - dest buffer // dataSize - partial size // Output : int - bytes read //----------------------------------------------------------------------------- int IterateRIFF::ChunkReadPartial( void *pOutput, int dataSize ) { return m_riff.ReadData( pOutput, dataSize ); } //----------------------------------------------------------------------------- // Purpose: Read a 4-byte int // Output : int - read int //----------------------------------------------------------------------------- int IterateRIFF::ChunkReadInt( void ) { return m_riff.ReadInt(); } //----------------------------------------------------------------------------- // Purpose: Used to iterate over an InFileRIFF //----------------------------------------------------------------------------- IterateOutputRIFF::IterateOutputRIFF( OutFileRIFF &riff ) : m_riff( riff ) { if ( !m_riff.IsValid() ) return; m_start = m_riff.PositionGet(); m_chunkPosition = m_start; m_chunkStart = -1; } IterateOutputRIFF::IterateOutputRIFF( IterateOutputRIFF &parent ) : m_riff(parent.m_riff) { m_start = parent.ChunkFilePosition(); m_chunkPosition = m_start; m_chunkStart = -1; } void IterateOutputRIFF::ChunkWrite( unsigned int chunkname, void *pOutput, int size ) { m_chunkPosition = m_riff.PositionGet(); m_chunkName = chunkname; m_chunkSize = size; m_riff.WriteInt( chunkname ); m_riff.WriteInt( size ); m_riff.WriteData( pOutput, size ); m_chunkPosition = m_riff.PositionGet(); m_chunkPosition += m_chunkPosition & 1; m_riff.PositionSet( m_chunkPosition ); m_chunkStart = -1; } void IterateOutputRIFF::ChunkWriteInt( int number ) { m_riff.WriteInt( number ); } void IterateOutputRIFF::ChunkWriteData( void *pOutput, int size ) { m_riff.WriteData( pOutput, size ); } void IterateOutputRIFF::ChunkFinish( void ) { Assert( m_chunkStart != -1 ); m_chunkPosition = m_riff.PositionGet(); int size = m_chunkPosition - m_chunkStart - 8; m_chunkPosition += m_chunkPosition & 1; m_riff.PositionSet( m_chunkStart + sizeof( int ) ); m_riff.WriteInt( size ); m_riff.PositionSet( m_chunkPosition ); m_chunkStart = -1; } void IterateOutputRIFF::ChunkStart( unsigned int chunkname ) { Assert( m_chunkStart == -1 ); m_chunkStart = m_riff.PositionGet(); m_riff.WriteInt( chunkname ); m_riff.WriteInt( 0 ); } void IterateOutputRIFF::ChunkSetPosition( int position ) { m_riff.PositionSet( position ); } unsigned int IterateOutputRIFF::ChunkGetPosition( void ) { return m_riff.PositionGet(); } void IterateOutputRIFF::CopyChunkData( IterateRIFF& input ) { if ( input.ChunkSize() > 0 ) { char *buffer = new char[ input.ChunkSize() ]; Assert( buffer ); input.ChunkRead( buffer ); // Don't copy/write the name or size, just the data itself ChunkWriteData( buffer, input.ChunkSize() ); delete[] buffer; } } void IterateOutputRIFF::SetLISETData( int position ) { m_riff.HasLISETData( position ); }