1 // =================================================================================================
2 // ADOBE SYSTEMS INCORPORATED
3 // Copyright 2010 Adobe Systems Incorporated
4 // All Rights Reserved
5 //
6 // NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
7 // of the Adobe license agreement accompanying it.
8 // =================================================================================================
9
10 #include "public/include/XMP_Environment.h" // ! XMP_Environment.h must be the first included header.
11 #include "public/include/XMP_Const.h"
12 #include "source/XIO.hpp"
13
14 #include "XMPFiles/source/FormatSupport/WAVE/WAVEBehavior.h"
15 #include "XMPFiles/source/FormatSupport/IFF/Chunk.h"
16
17 #include <algorithm>
18
19 using namespace IFF_RIFF;
20
21 //
22 // Static init
23 //
24 const LittleEndian& WAVEBehavior::mEndian = LittleEndian::getInstance();
25
26
27 //-----------------------------------------------------------------------------
28 //
29 // WAVEBehavior::getRealSize(...)
30 //
31 // Purpose: Validate the passed in size value, identify the valid size if the
32 // passed in isn't valid and return the valid size.
33 // Throw an exception if the passed in size isn't valid and there's
34 // no way to identify a valid size.
35 //
36 //-----------------------------------------------------------------------------
37
getRealSize(const XMP_Uns64 size,const ChunkIdentifier & id,IChunkContainer & tree,XMP_IO * stream)38 XMP_Uns64 WAVEBehavior::getRealSize( const XMP_Uns64 size, const ChunkIdentifier& id, IChunkContainer& tree, XMP_IO* stream )
39 {
40 XMP_Uns64 realSize = size;
41
42 if( size >= kNormalRF64ChunkSize ) // 4GB
43 {
44 if( this->isRF64( tree ) )
45 {
46 //
47 // RF64 supports sizes beyond 4GB
48 //
49 DS64* rf64 = this->getDS64( tree, stream );
50
51 if( rf64 != NULL )
52 {
53 //
54 // get 64bit size from RF64 structure
55 //
56 switch( id.id )
57 {
58 case kChunk_RF64: realSize = rf64->riffSize; break;
59 case kChunk_data: realSize = rf64->dataSize; break;
60
61 default:
62 {
63 bool found = false;
64
65 //
66 // try to find size value for passed chunk id in the ds64 table
67 //
68 if( rf64->tableLength > 0 )
69 {
70 for( std::vector<ChunkSize64>::iterator iter=rf64->table.begin(); iter!=rf64->table.end(); iter++ )
71 {
72 if( iter->id == id.id )
73 {
74 realSize = iter->size;
75 found = true;
76 break;
77 }
78 }
79 }
80
81 if( !found )
82 {
83 //
84 // no size for passed id available
85 //
86 XMP_Throw( "Unknown size value", kXMPErr_BadFileFormat );
87 }
88 }
89 }
90 }
91 else
92 {
93 //
94 // no RF64 size info available
95 //
96 XMP_Throw( "Unknown size value", kXMPErr_BadFileFormat );
97 }
98 }
99 else
100 {
101 //
102 // WAVE doesn't support that size
103 //
104 XMP_Throw( "Unknown size value", kXMPErr_BadFileFormat );
105 }
106 }
107
108 return realSize;
109 }
110
111 //-----------------------------------------------------------------------------
112 //
113 // WAVEBehavior::getMaxChunkSize(...)
114 //
115 // Purpose: Return the maximum size of a single chunk, i.e. the maximum size
116 // of a top-level chunk.
117 //
118 //-----------------------------------------------------------------------------
119
getMaxChunkSize() const120 XMP_Uns64 WAVEBehavior::getMaxChunkSize() const
121 {
122 // simple WAVE 4GByte
123 XMP_Uns64 ret = 0x00000000FFFFFFFFLL;
124
125 if( mIsRF64 )
126 {
127 // RF64: full possible 64bit size
128 ret = 0xFFFFFFFFFFFFFFFFLL;
129 }
130
131 return ret;
132 }
133
134 //-----------------------------------------------------------------------------
135 //
136 // WAVEBehavior::isValidTopLevelChunk(...)
137 //
138 // Purpose: Return true if the passed identifier is valid for top-level chunks
139 // of a certain format.
140 //
141 //-----------------------------------------------------------------------------
142
isValidTopLevelChunk(const ChunkIdentifier & id,XMP_Uns32 chunkNo)143 bool WAVEBehavior::isValidTopLevelChunk( const ChunkIdentifier& id, XMP_Uns32 chunkNo )
144 {
145 return ( chunkNo == 0 ) &&
146 ( ( ( id.id == kChunk_RIFF ) && ( id.type == kType_WAVE ) ) ||
147 ( ( id.id == kChunk_RF64 ) && ( id.type == kType_WAVE ) ) );
148 }
149
150 //-----------------------------------------------------------------------------
151 //
152 // WAVEBehavior::fixHierarchy(...)
153 //
154 // Purpose: Fix the hierarchy of chunks depending ones based on size changes of
155 // one or more chunks and second based on format specific rules.
156 // Throw an exception if the hierarchy can't be fixed.
157 //
158 //-----------------------------------------------------------------------------
159
fixHierarchy(IChunkContainer & tree)160 void WAVEBehavior::fixHierarchy( IChunkContainer& tree )
161 {
162 XMP_Validate( tree.numChildren() == 1, "WAVE files should only have one top level chunk (RIFF)", kXMPErr_BadFileFormat);
163
164 Chunk* riffChunk = tree.getChildAt(0);
165
166 XMP_Validate( (riffChunk->getType() == kType_WAVE || riffChunk->getType() == kChunk_RF64) , "Invalid type for WAVE/RF64 top level chunk (RIFF)", kXMPErr_BadFileFormat);
167
168 if( riffChunk->hasChanged() )
169 {
170 //
171 // move new added chunks to temporary container
172 //
173 Chunk* tmpContainer = Chunk::createChunk( mEndian );
174 this->moveChunks( *riffChunk, *tmpContainer, riffChunk->numChildren() - mChunksAdded );
175
176 //
177 // try to arrange chunks at their current position
178 //
179 this->arrangeChunksInPlace( *riffChunk, *tmpContainer );
180
181 //
182 // for all chunks that were moved to the end try to find a FREE chunk for them
183 //
184 this->arrangeChunksInTree( *tmpContainer, *riffChunk );
185
186 //
187 // append all remaining new added chunks to the end of the tree
188 //
189 this->moveChunks( *tmpContainer, *riffChunk, 0 );
190 delete tmpContainer;
191
192 //
193 // check for FREE chunks at the end
194 //
195 Chunk* endFREE = this->mergeFreeChunks( *riffChunk, riffChunk->numChildren() - 1 );
196
197 if( endFREE != NULL )
198 {
199 riffChunk->removeChildAt( riffChunk->numChildren() - 1 );
200 delete endFREE;
201 }
202
203 //
204 // Fix the offset values of all chunks. Throw an exception in the case that
205 // the offset of a non-modified chunk needs to be reset.
206 //
207 XMP_Validate( riffChunk->getOffset() == 0, "Invalid offset for RIFF top level chunk", kXMPErr_InternalFailure );
208
209 this->validateOffsets( tree );
210
211 //
212 // update the RF64 chunk (if this is RF64) based on the current chunk sizes
213 //
214 this->updateRF64( tree );
215 }
216 }
217
insertChunk(IChunkContainer & tree,Chunk & chunk)218 void WAVEBehavior::insertChunk( IChunkContainer& tree, Chunk& chunk )
219 {
220 XMP_Validate( tree.numChildren() == 1, "WAVE files should only have one top level chunk (RIFF)", kXMPErr_BadFileFormat);
221 Chunk* riffChunk = tree.getChildAt(0);
222
223 XMP_Validate( riffChunk->getType() == kType_WAVE , "Invalid type for WAVE top level chunk (RIFF)", kXMPErr_BadFileFormat);
224
225 //
226 // add new chunk to the end of the RIFF:WAVE
227 //
228 riffChunk->appendChild(&chunk);
229
230 mChunksAdded++;
231 }
232
removeChunk(IChunkContainer & tree,Chunk & chunk)233 bool WAVEBehavior::removeChunk( IChunkContainer& tree, Chunk& chunk )
234 {
235 //
236 // validate parameter
237 //
238 XMP_Validate( chunk.getID() != kChunk_RIFF, "Can't remove RIFF chunk!", kXMPErr_InternalFailure );
239 XMP_Validate( chunk.getChunkMode() != CHUNK_UNKNOWN, "Cant' remove UNKNOWN Chunk", kXMPErr_InternalFailure );
240 XMP_Validate( tree.numChildren() == 1, "WAVE files should only have one top level chunk (RIFF)", kXMPErr_BadFileFormat);
241
242 //
243 // get top-level chunk
244 //
245 Chunk* riffChunk = tree.getChildAt(0);
246
247 //
248 // validate top-level chunk
249 //
250 XMP_Validate( (riffChunk->getType() == kType_WAVE || riffChunk->getType() == kChunk_RF64) , "Invalid type for WAVE/RF64 top level chunk (RIFF)", kXMPErr_BadFileFormat);
251
252 //
253 // calculate index of chunk to remove
254 //
255 XMP_Uns32 i = std::find( riffChunk->firstChild(), riffChunk->lastChild(), &chunk ) - riffChunk->firstChild();
256
257 //
258 // validate index
259 //
260 XMP_Validate( i < riffChunk->numChildren(), "Invalid chunk in tree", kXMPErr_InternalFailure );
261
262 //
263 // adjust new chunks counter
264 //
265 if( i > riffChunk->numChildren() - mChunksAdded - 1 )
266 {
267 mChunksAdded--;
268 }
269
270 if( i < riffChunk->numChildren()-1 )
271 {
272 //
273 // fill gap with free chunk
274 //
275 Chunk* free = this->createFREE( chunk.getPadSize( true ) );
276 riffChunk->replaceChildAt( i, free );
277 free->setAsNew();
278
279 //
280 // merge JUNK chunks
281 //
282 this->mergeFreeChunks( *riffChunk, i );
283 }
284 else
285 {
286 //
287 // remove chunk from tree
288 //
289 riffChunk->removeChildAt( i );
290 }
291
292 //
293 // if there is an entry in the ds64 table for the removed chunk
294 // then update the ds64 table entry
295 //
296 if( mDS64Data != NULL && mDS64Data->tableLength > 0 )
297 {
298 for( std::vector<ChunkSize64>::iterator iter=mDS64Data->table.begin(); iter!=mDS64Data->table.end(); iter++ )
299 {
300 if( iter->id == chunk.getID() )
301 {
302 //
303 // don't remove entry but set its size to zero
304 //
305 iter->size = 0LL;
306 break;
307 }
308 }
309 }
310
311 return true;
312 }
313
createFREE(XMP_Uns64 chunkSize)314 Chunk* WAVEBehavior::createFREE( XMP_Uns64 chunkSize )
315 {
316 XMP_Int64 alloc = chunkSize - Chunk::HEADER_SIZE;
317
318 Chunk* chunk = NULL;
319
320 //
321 // create a 'JUNK' chunk
322 //
323 if( alloc > 0 )
324 {
325 XMP_Uns8* data = new XMP_Uns8[static_cast<size_t>( alloc )];
326 memset( data, 0, static_cast<size_t>( alloc ) );
327
328 chunk = Chunk::createUnknownChunk( mEndian, kChunk_JUNK, kType_NONE, alloc );
329
330 chunk->setData( data, alloc );
331
332 delete[] data;
333 }
334 else
335 {
336 chunk = Chunk::createHeaderChunk( mEndian, kChunk_JUNK );
337 }
338
339 // force set dirty flag
340 chunk->setChanged();
341
342 return chunk;
343 }
344
isFREEChunk(const Chunk & chunk) const345 XMP_Bool WAVEBehavior::isFREEChunk( const Chunk& chunk ) const
346 {
347 // Check for sigature JUNK and JUNQ
348 return ( chunk.getID() == kChunk_JUNK || chunk.getID() == kChunk_JUNQ );
349 }
350
351
getMinFREESize() const352 XMP_Uns64 WAVEBehavior::getMinFREESize() const
353 {
354 // avoid creation of chunks with size==0
355 return static_cast<XMP_Uns64>( Chunk::HEADER_SIZE ) + 2;
356 }
357
358 //-----------------------------------------------------------------------------
359 //
360 // WAVEBehavior::isRF64(...)
361 //
362 // Purpose: Is the current file a RF64 file
363 //
364 //-----------------------------------------------------------------------------
365
isRF64(const IChunkContainer & tree)366 bool WAVEBehavior::isRF64( const IChunkContainer& tree )
367 {
368 // The file format will not change at runtime
369 // So if the flag is not already set, have a look at the tree
370 if( ! mIsRF64 && tree.numChildren() != 0 )
371 {
372 Chunk *chunk = tree.getChildAt(0);
373 // Only the TopLevel chunk is interesting
374 mIsRF64 = chunk->getID() == kChunk_RF64 &&
375 chunk->getType() == kType_WAVE;
376 }
377
378 return mIsRF64;
379 }
380
381 //-----------------------------------------------------------------------------
382 //
383 // WAVEBehavior::getDS64(...)
384 //
385 // Purpose: Return RF64 structure.
386 //
387 //-----------------------------------------------------------------------------
388
getDS64(IChunkContainer & tree,XMP_IO * stream)389 WAVEBehavior::DS64* WAVEBehavior::getDS64( IChunkContainer& tree, XMP_IO* stream )
390 {
391 DS64* ret = mDS64Data;
392
393 if( ret == NULL )
394 {
395 //
396 // try to find 'ds64' chunk in the tree
397 //
398 Chunk* ds64 = NULL;
399 Chunk* rf64 = NULL;
400
401 if( tree.numChildren() > 0 )
402 {
403 rf64 = tree.getChildAt(0);
404
405 if( rf64 != NULL && rf64->getID() == kChunk_RF64 && rf64->numChildren() > 0 )
406 {
407 //
408 // 'ds64' chunk needs to be the very first child of the 'RF64' chunk
409 //
410 ds64 = rf64->getChildAt(0);
411 }
412
413 //
414 // Try to create 'ds64' chunk by parsing the stream
415 //
416 if( ds64 == NULL && stream != NULL )
417 {
418 //
419 // remember file position before start reading from the stream
420 //
421 XMP_Uns64 filePos = stream->Offset();
422
423 try
424 {
425 ds64 = Chunk::createChunk( mEndian );
426 ds64->readChunk( stream );
427 }
428 catch( ... )
429 {
430 delete ds64;
431 ds64 = NULL;
432 }
433
434 if( rf64 != NULL && ds64 != NULL && ds64->getID() == kChunk_ds64 )
435 {
436 //
437 // Successfully read 'ds64' chunk.
438 // Now read its data area as well and
439 // add chunk to the 'RF64' chunk
440 //
441 ds64->cacheChunkData( stream );
442 rf64->appendChild( ds64, false );
443 }
444 else
445 {
446 //
447 // Either the reading failed or the 'ds64' chunk
448 // doesn't exists at the expected position.
449 // Now clean up and reject the stream position.
450 //
451 delete ds64;
452 ds64 = NULL;
453
454 stream->Seek( filePos, kXMP_SeekFromStart );
455 }
456 }
457 else if( ds64 != NULL && ds64->getID() != kChunk_ds64 )
458 {
459 //
460 // first child of 'RF64' chunk is NOT 'ds64'!
461 //
462 ds64 = NULL;
463 }
464 }
465
466 //
467 // parse 'ds64' chunk, store the RF64 struct and return it
468 //
469 if( ds64 != NULL )
470 {
471 DS64* ds64data = new DS64();
472
473 if( this->parseDS64Chunk( *ds64, *ds64data ) )
474 {
475 mDS64Data = ds64data;
476 ret = mDS64Data;
477 }
478 else
479 {
480 delete ds64data;
481 }
482 }
483 }
484
485 return ret;
486 }
487
488 //-----------------------------------------------------------------------------
489 //
490 // WAVEBehavior::updateRF64(...)
491 //
492 // Purpose: update the RF64 chunk (if this is RF64) based on the current chunk sizes
493 //
494 //-----------------------------------------------------------------------------
495
updateRF64(IChunkContainer & tree)496 void WAVEBehavior::updateRF64( IChunkContainer& tree )
497 {
498 if( this->isRF64( tree ) )
499 {
500 XMP_Validate( mDS64Data != NULL, "Missing DS64 structure", kXMPErr_InternalFailure );
501 XMP_Validate( tree.numChildren() == 1, "Invalid RF64 tree", kXMPErr_InternalFailure );
502
503 //
504 // Check all chunks that sizes have changed and update their related value in the DS64 chunk
505 //
506 Chunk* rf64 = tree.getChildAt(0);
507 XMP_Validate( rf64 != NULL && rf64->getID() == kChunk_RF64 && rf64->numChildren() > 0, "Invalid RF64 chunk", kXMPErr_InternalFailure );
508
509 this->doUpdateRF64( *rf64 );
510
511 //
512 // try to find 'ds64' chunk in the tree
513 // (needs to be the very first child of the 'RF64' chunk)
514 //
515 Chunk* ds64 = rf64->getChildAt(0);
516 XMP_Validate( ds64 != NULL && ds64->getID() == kChunk_ds64, "Missing 'ds64' chunk", kXMPErr_InternalFailure );
517
518 //
519 // serialize DS64 structure and write into ds64 chunk
520 //
521 this->serializeDS64Chunk( *mDS64Data, *ds64 );
522 }
523 }
524
doUpdateRF64(Chunk & chunk)525 void WAVEBehavior::doUpdateRF64( Chunk& chunk )
526 {
527 //
528 // update ds64 entry for chunk if its size has changed
529 //
530 if( chunk.hasChanged() && chunk.getOriginalSize() > kNormalRF64ChunkSize )
531 {
532 switch( chunk.getID() )
533 {
534 case kChunk_RF64: mDS64Data->riffSize = chunk.getSize(); break;
535 case kChunk_data:
536 if( chunk.getSize() != chunk.getOriginalSize() )
537 {
538 XMP_Throw( "Data chunk must not change", kXMPErr_InternalFailure );
539 }
540 break;
541 default:
542 {
543 bool requireEntry = ( chunk.getSize() > kNormalRF64ChunkSize );
544 bool found = false;
545
546 //
547 // try to find entry for passed chunk id in the ds64 table
548 //
549 if( mDS64Data->tableLength > 0 )
550 {
551 for( std::vector<ChunkSize64>::iterator iter=mDS64Data->table.begin(); iter!=mDS64Data->table.end(); iter++ )
552 {
553 if( iter->id == chunk.getID() )
554 {
555 // always set new size even if it's less than 4GB
556 iter->size = chunk.getSize();
557 found = true;
558 break;
559 }
560 }
561 }
562
563 //
564 // We can't add new entries to the table. So if we found no entry within 'ds64'
565 // for the passed chunk ID and the size of the chunk is larger than 4GB then
566 // we have to throw an exception
567 //
568 XMP_Validate( found || ( ! found && ! requireEntry ), "Can't update 'ds64' chunk", kXMPErr_Unimplemented );
569 }
570 }
571 }
572
573 //
574 // go through all children to update ds64 data
575 //
576 for( XMP_Uns32 i=0; i<chunk.numChildren(); i++ )
577 {
578 Chunk* child = chunk.getChildAt(i);
579
580 this->doUpdateRF64( *child );
581 }
582 }
583
584 //-----------------------------------------------------------------------------
585 //
586 // WAVEBehavior::parseRF64Chunk(...)
587 //
588 // Purpose: Parses the data block of the given RF64 chunk into the internal data structures
589 //
590 //-----------------------------------------------------------------------------
591
parseDS64Chunk(const Chunk & ds64Chunk,WAVEBehavior::DS64 & ds64)592 bool WAVEBehavior::parseDS64Chunk( const Chunk& ds64Chunk, WAVEBehavior::DS64& ds64 )
593 {
594 bool ret = false;
595
596 // It is a valid ds64 chunk
597 if( ds64Chunk.getID() == kChunk_ds64 && ds64Chunk.getSize() >= kMinimumDS64ChunkSize )
598 {
599 const XMP_Uns8* data;
600 XMP_Uns64 size = ds64Chunk.getData(&data);
601
602 memset( &ds64, 0, kMinimumDS64ChunkSize);
603
604 //
605 // copy fix input data into RF64 block (except chunk size table)
606 // Safe as fixed size matches size of struct that is #pragma packed(1)
607 //
608 memcpy( &ds64, data, kMinimumDS64ChunkSize );
609
610 // If there is more data but the table length is <= 0 then this is not a valid ds64 chunk
611 if( size > kMinimumDS64ChunkSize && ds64.tableLength > 0 )
612 {
613 // copy chunk sizes table
614 //
615 XMP_Assert( size - kMinimumDS64ChunkSize >= ds64.tableLength * sizeof(ChunkSize64));
616
617 XMP_Uns32 offset = kMinimumDS64ChunkSize;
618 ChunkSize64 chunkSize;
619
620 for( XMP_Uns32 i = 0 ; i < ds64.tableLength ; i++, offset += sizeof(ChunkSize64) )
621 {
622 chunkSize.id = mEndian.getUns32( data + offset );
623 chunkSize.size = mEndian.getUns64( data + offset + 4 );
624
625 ds64.table.push_back( chunkSize );
626 }
627 }
628
629 // remember any existing table buffer
630 ds64.trailingBytes = static_cast<XMP_Uns32>(size - kMinimumDS64ChunkSize - ds64.tableLength * sizeof(ChunkSize64));
631
632 // Either a table has been correctly parsed or there was no table
633 ret = (size - kMinimumDS64ChunkSize) >= (ds64.tableLength * sizeof(ChunkSize64));
634 }
635
636 return ret;
637 }
638
639 //-----------------------------------------------------------------------------
640 //
641 // WAVEBehavior::serializeRF64Chunk(...)
642 //
643 // Purpose: Serializes the internal RF64 data structures into the data part of the given chunk
644 //
645 //-----------------------------------------------------------------------------
serializeDS64Chunk(const WAVEBehavior::DS64 & ds64,Chunk & ds64Chunk)646 bool WAVEBehavior::serializeDS64Chunk( const WAVEBehavior::DS64& ds64, Chunk& ds64Chunk )
647 {
648 if( ds64Chunk.getID() != kChunk_ds64 )
649 {
650 return false; // not a valid ds64 chunk
651 }
652
653 // Calculate needed size
654 XMP_Uns32 size = kMinimumDS64ChunkSize + ds64.tableLength * sizeof(ChunkSize64) + ds64.trailingBytes;
655 // Create tmp buffer
656 XMP_Uns8* data = new XMP_Uns8[size];
657 memset( data, 0, size );
658
659 // copy fix input data into buffer (except chunk sizes table)
660 // Safe as fixed size matches size of struct that is #pragma packed(1)
661 memcpy( data, &ds64, kMinimumDS64ChunkSize );
662
663 // copy chunk sizes table
664 if( ds64.tableLength > 0 )
665 {
666 XMP_Uns32 offset = kMinimumDS64ChunkSize;
667
668 for( XMP_Uns32 i = 0 ; i < ds64.tableLength ; i++, offset += sizeof(ChunkSize64) )
669 {
670 mEndian.putUns32( ds64.table.at(i).id, data + offset );
671 mEndian.putUns64( ds64.table.at(i).size, data + offset + 4 );
672 }
673 }
674
675 ds64Chunk.setData( data, size );
676
677 // free tmp buffer
678 delete []data;
679
680 return true;
681 }
682