00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #ifdef HAVE_CONFIG_H
00022 #include <config.h>
00023 #endif
00024
00025
00026
00027 #include <string>
00028
00029 #include <iostream>
00030 #include <iomanip>
00031 #include <byteswap.h>
00032
00033 using std::cout;
00034 using std::cerr;
00035 using std::hex;
00036 using std::dec;
00037 using std::setw;
00038 using std::setfill;
00039 using std::endl;
00040
00041
00042
00043 #include <fcntl.h>
00044 #include <unistd.h>
00045 #include <assert.h>
00046
00047
00048
00049 #include "error.h"
00050 #include "riff.h"
00051
00052
00063 FOURCC make_fourcc( char *s )
00064 {
00065 if ( s[ 0 ] == 0 )
00066 return 0;
00067 else
00068 return *( ( FOURCC* ) s );
00069 }
00070
00071
00072 RIFFDirEntry::RIFFDirEntry()
00073 {}
00074
00075
00076 RIFFDirEntry::RIFFDirEntry ( FOURCC t, FOURCC n, int l, int o, int p ) : type( t ), name( n ), length( l ), offset( o ), parent( p ), written( 0 )
00077 {}
00078
00079
00084 RIFFFile::RIFFFile() : fd( -1 )
00085 {
00086 pthread_mutex_init( &file_mutex, NULL );
00087 }
00088
00089
00090
00091
00092
00093
00094
00095 RIFFFile::RIFFFile( const RIFFFile& riff ) : fd( -1 )
00096 {
00097 if ( riff.fd != -1 )
00098 {
00099 fd = dup( riff.fd );
00100 }
00101 directory = riff.directory;
00102 }
00103
00104
00109 RIFFFile::~RIFFFile()
00110 {
00111 Close();
00112 pthread_mutex_destroy( &file_mutex );
00113 }
00114
00115
00116 RIFFFile& RIFFFile::operator=( const RIFFFile& riff )
00117 {
00118 if ( fd != riff.fd )
00119 {
00120 Close();
00121 if ( riff.fd != -1 )
00122 {
00123 fd = dup( riff.fd );
00124 }
00125 directory = riff.directory;
00126 }
00127 return *this;
00128 }
00129
00130
00136 bool RIFFFile::Create( const char *s )
00137 {
00138 fd = open( s, O_RDWR | O_NONBLOCK | O_CREAT | O_TRUNC, 00644 );
00139
00140 if ( fd == -1 )
00141 return false;
00142 else
00143 return true;
00144 }
00145
00146
00152 bool RIFFFile::Open( const char *s )
00153 {
00154 fd = open( s, O_RDONLY | O_NONBLOCK );
00155
00156 if ( fd == -1 )
00157 return false;
00158 else
00159 return true;
00160 }
00161
00162
00167 void RIFFFile::Close()
00168 {
00169 if ( fd != -1 )
00170 {
00171 close( fd );
00172 fd = -1;
00173 }
00174 }
00175
00176
00188 int RIFFFile::AddDirectoryEntry( FOURCC type, FOURCC name, off_t length, int list )
00189 {
00190
00191
00192
00193 RIFFDirEntry entry( type, name, length, 0 , list );
00194
00195
00196
00197
00198
00199
00200 if ( list != RIFF_NO_PARENT )
00201 {
00202 RIFFDirEntry parent = GetDirectoryEntry( list );
00203 entry.offset = parent.offset + parent.length + RIFF_HEADERSIZE;
00204 }
00205
00206
00207
00208
00209
00210
00211 while ( list != RIFF_NO_PARENT )
00212 {
00213 RIFFDirEntry parent = GetDirectoryEntry( list );
00214 parent.length += RIFF_HEADERSIZE + length;
00215 SetDirectoryEntry( list, parent );
00216 list = parent.parent;
00217 }
00218
00219 directory.insert( directory.end(), entry );
00220
00221 return directory.size() - 1;
00222 }
00223
00224
00235 void RIFFFile::SetDirectoryEntry( int i, FOURCC type, FOURCC name, off_t length, off_t offset, int list )
00236 {
00237 RIFFDirEntry entry( type, name, length, offset, list );
00238
00239 assert( i >= 0 && i < ( int ) directory.size() );
00240
00241 directory[ i ] = entry;
00242 }
00243
00244
00254 void RIFFFile::SetDirectoryEntry( int i, RIFFDirEntry &entry )
00255 {
00256 assert( i >= 0 && i < ( int ) directory.size() );
00257
00258 entry.written = false;
00259 directory[ i ] = entry;
00260 }
00261
00262
00274 void RIFFFile::GetDirectoryEntry( int i, FOURCC &type, FOURCC &name, off_t &length, off_t &offset, int &list ) const
00275 {
00276 RIFFDirEntry entry;
00277
00278 assert( i >= 0 && i < ( int ) directory.size() );
00279
00280 entry = directory[ i ];
00281 type = entry.type;
00282 name = entry.name;
00283 length = entry.length;
00284 offset = entry.offset;
00285 list = entry.parent;
00286 }
00287
00288
00296 RIFFDirEntry RIFFFile::GetDirectoryEntry( int i ) const
00297 {
00298 assert( i >= 0 && i < ( int ) directory.size() );
00299
00300 return directory[ i ];
00301 }
00302
00303
00309 off_t RIFFFile::GetFileSize( void ) const
00310 {
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322 if ( directory.size() > 0 )
00323 return directory[ 0 ].length;
00324 else
00325 return 0;
00326 }
00327
00328
00334 void RIFFFile::PrintDirectoryEntry ( int i ) const
00335 {
00336 RIFFDirEntry entry;
00337 RIFFDirEntry parent;
00338 FOURCC entry_name;
00339 FOURCC list_name;
00340
00341
00342
00343
00344
00345
00346 entry = GetDirectoryEntry( i );
00347 if ( entry.parent != RIFF_NO_PARENT )
00348 {
00349 parent = GetDirectoryEntry( entry.parent );
00350 list_name = parent.name;
00351 }
00352 else
00353 {
00354 list_name = make_fourcc( " " );
00355 }
00356 if ( entry.name != 0 )
00357 {
00358 entry_name = entry.name;
00359 }
00360 else
00361 {
00362 entry_name = make_fourcc( " " );
00363 }
00364
00365
00366
00367
00368 cout << hex << setfill( '0' ) << "type: "
00369 << ((char *)&entry.type)[0]
00370 << ((char *)&entry.type)[1]
00371 << ((char *)&entry.type)[2]
00372 << ((char *)&entry.type)[3]
00373 << " name: "
00374 << ((char *)&entry_name)[0]
00375 << ((char *)&entry_name)[1]
00376 << ((char *)&entry_name)[2]
00377 << ((char *)&entry_name)[3]
00378 << " length: 0x" << setw( 12 ) << entry.length
00379 << " offset: 0x" << setw( 12 ) << entry.offset
00380 << " list: "
00381 << ((char *)&list_name)[0]
00382 << ((char *)&list_name)[1]
00383 << ((char *)&list_name)[2]
00384 << ((char *)&list_name)[3] << dec << endl;
00385
00386
00387
00388 PrintDirectoryEntryData( entry );
00389 }
00390
00391
00399 void RIFFFile::PrintDirectoryEntryData( const RIFFDirEntry &entry ) const
00400 {}
00401
00402
00410 void RIFFFile::PrintDirectory() const
00411 {
00412 int i;
00413 int count = directory.size();
00414
00415 for ( i = 0; i < count; ++i )
00416 PrintDirectoryEntry( i );
00417 }
00418
00419
00429 int RIFFFile::FindDirectoryEntry ( FOURCC type, int n ) const
00430 {
00431 int i, j = 0;
00432 int count = directory.size();
00433
00434 for ( i = 0; i < count; ++i )
00435 if ( directory[ i ].type == type )
00436 {
00437 if ( j == n )
00438 return i;
00439 j++;
00440 }
00441
00442 return -1;
00443 }
00444
00445
00455 void RIFFFile::ParseChunk( int parent )
00456 {
00457 FOURCC type;
00458 DWORD length;
00459 int typesize;
00460
00461
00462
00463 read( fd, &type, sizeof( type ) );
00464 if ( type == make_fourcc( "LIST" ) )
00465 {
00466 typesize = -sizeof( type );
00467 fail_if( lseek( fd, typesize, SEEK_CUR ) == ( off_t ) - 1 );
00468 ParseList( parent );
00469 }
00470
00471
00472
00473 else
00474 {
00475 fail_neg( read( fd, &length, sizeof( length ) ) );
00476 fail_if( length == 0 );
00477 if ( length & 1 )
00478 length++;
00479 AddDirectoryEntry( type, 0, length, parent );
00480 fail_if( lseek( fd, length, SEEK_CUR ) == ( off_t ) - 1 );
00481 }
00482 }
00483
00484
00491 void RIFFFile::ParseList( int parent )
00492 {
00493 FOURCC type;
00494 FOURCC name;
00495 int list;
00496 DWORD length;
00497 off_t pos;
00498 off_t listEnd;
00499
00500
00501 fail_neg( read( fd, &type, sizeof( type ) ) );
00502 fail_neg( read( fd, &length, sizeof( length ) ) );
00503
00504 if ( length & 1 )
00505 length++;
00506
00507
00508
00509
00510 pos = lseek( fd, 0, SEEK_CUR );
00511 fail_if( pos == ( off_t ) - 1 );
00512 fail_neg( read( fd, &name, sizeof( name ) ) );
00513
00514
00515
00516 list = AddDirectoryEntry( type, name, sizeof( name ), parent );
00517
00518
00519
00520
00521 listEnd = pos + length;
00522 while ( pos < listEnd )
00523 {
00524 ParseChunk( list );
00525 pos = lseek( fd, 0, SEEK_CUR );
00526 fail_if( pos == ( off_t ) - 1 );
00527 }
00528 }
00529
00530
00535 void RIFFFile::ParseRIFF( void )
00536 {
00537 FOURCC type;
00538 DWORD length;
00539 off_t filesize;
00540 off_t pos;
00541 int container = AddDirectoryEntry( make_fourcc( "FILE" ), make_fourcc( "FILE" ), 0, RIFF_NO_PARENT );
00542
00543 pos = lseek( fd, 0, SEEK_SET );
00544
00545
00546
00547 while ( ( read( fd, &type, sizeof( type ) ) > 0 ) &&
00548 ( read( fd, &length, sizeof( length ) ) > 0 ) &&
00549 ( type == make_fourcc( "RIFF" ) ) )
00550 {
00551
00552 filesize += length + RIFF_HEADERSIZE;
00553
00554 fail_if( lseek( fd, pos, SEEK_SET ) == ( off_t ) - 1 );
00555 ParseList( container );
00556 pos = lseek( fd, 0, SEEK_CUR );
00557 fail_if( pos == ( off_t ) - 1 );
00558 }
00559 }
00560
00561
00569 bool RIFFFile::ReadChunk( int chunk_index, void *data, off_t data_len )
00570 {
00571 RIFFDirEntry entry;
00572
00573 entry = GetDirectoryEntry( chunk_index );
00574 #ifdef DEBUG
00575 if ( entry.length > data_len ) {
00576 FOURCC entry_name = entry.name ? entry.name : make_fourcc( " " );
00577
00578 cerr << dec << "Warning: insufficient storage in RIFFFile::ReadChunk(): data_len " << data_len << " entry.length " << entry.length << endl;
00579 cerr << hex << setfill( '0' ) << " type: "
00580 << ((char *)&entry.type)[0]
00581 << ((char *)&entry.type)[1]
00582 << ((char *)&entry.type)[2]
00583 << ((char *)&entry.type)[3]
00584 << " name: "
00585 << ((char *)&entry_name)[0]
00586 << ((char *)&entry_name)[1]
00587 << ((char *)&entry_name)[2]
00588 << ((char *)&entry_name)[3]
00589 << " length: 0x" << setw( 12 ) << entry.length
00590 << " offset: 0x" << setw( 12 ) << entry.offset
00591 << endl;
00592 }
00593 #endif
00594 pthread_mutex_lock( &file_mutex );
00595 fail_if( lseek( fd, entry.offset, SEEK_SET ) == ( off_t ) - 1 );
00596 fail_neg( read( fd, data, entry.length > data_len ? data_len : entry.length ) );
00597 pthread_mutex_unlock( &file_mutex );
00598
00599 return ( entry.length <= data_len );
00600 }
00601
00602
00610 void RIFFFile::WriteChunk( int chunk_index, const void *data )
00611 {
00612 RIFFDirEntry entry;
00613
00614 entry = GetDirectoryEntry( chunk_index );
00615 pthread_mutex_lock( &file_mutex );
00616 fail_if( lseek( fd, entry.offset - RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 );
00617 fail_neg( write( fd, &entry.type, sizeof( entry.type ) ) );
00618 DWORD length = entry.length;
00619 fail_neg( write( fd, &length, sizeof( length ) ) );
00620 fail_neg( write( fd, data, entry.length ) );
00621 pthread_mutex_unlock( &file_mutex );
00622
00623
00624
00625 directory[ chunk_index ].written = true;
00626 }
00627
00628
00638 void RIFFFile::WriteRIFF( void )
00639 {
00640 int i;
00641 RIFFDirEntry entry;
00642 int count = directory.size();
00643
00644
00645
00646
00647
00648 for ( i = 1; i < count; ++i )
00649 {
00650
00651
00652
00653 entry = GetDirectoryEntry( i );
00654 if ( entry.written == false )
00655 {
00656
00657
00658
00659
00660
00661
00662
00663 fail_if( lseek( fd, entry.offset - RIFF_HEADERSIZE, SEEK_SET ) == ( off_t ) - 1 ) ;
00664 fail_neg( write( fd, &entry.type, sizeof( entry.type ) ) );
00665 DWORD length = entry.length;
00666 fail_neg( write( fd, &length, sizeof( length ) ) );
00667
00668
00669
00670
00671 if ( entry.name != 0 )
00672 {
00673 fail_neg( write( fd, &entry.name, sizeof( entry.name ) ) );
00674 }
00675
00676
00677
00678 directory[ i ].written = true;
00679 }
00680 }
00681 }