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
13 #include "XMPFiles/source/FormatSupport/IFF/Chunk.h"
14 #include "source/XMP_LibUtils.hpp"
15 #include "source/XIO.hpp"
16
17 #include <cstdio>
18 #include <cstdlib>
19 #include <typeinfo>
20
21 using namespace IFF_RIFF;
22
23 //-----------------------------------------------------------------------------
24 //
25 // Chunk::createChunk(...)
26 //
27 // Purpose: [static] Static factory to create an unknown chunk
28 //
29 //-----------------------------------------------------------------------------
30
createChunk(const IEndian & endian)31 Chunk* Chunk::createChunk( const IEndian& endian )
32 {
33 return new Chunk( endian );
34 }
35
36
37 //-----------------------------------------------------------------------------
38 //
39 // Chunk::createUnknownChunk(...)
40 //
41 // Purpose: [static] Static factory to create an unknown chunk with initial id,
42 // sizes and offsets.
43 //
44 //-----------------------------------------------------------------------------
45
createUnknownChunk(const IEndian & endian,const XMP_Uns32 id,const XMP_Uns32 type,const XMP_Uns64 size,const XMP_Uns64 originalOffset,const XMP_Uns64 offset)46 Chunk* Chunk::createUnknownChunk(
47 const IEndian& endian,
48 const XMP_Uns32 id,
49 const XMP_Uns32 type,
50 const XMP_Uns64 size,
51 const XMP_Uns64 originalOffset,
52 const XMP_Uns64 offset
53 )
54 {
55 Chunk *chunk = new Chunk( endian );
56 chunk->setID( id );
57 chunk->mOriginalOffset = originalOffset;
58 chunk->mOffset = offset;
59
60 if (type != 0)
61 {
62 chunk->setType(type);
63 }
64
65 // sizes have to be set after type, otherwise the setType sets the size to 4.
66 chunk->mSize = chunk->mOriginalSize = size;
67 chunk->mChunkMode = CHUNK_UNKNOWN;
68 chunk->mDirty = false;
69 return chunk;
70 }
71
72 //-----------------------------------------------------------------------------
73 //
74 // Chunk::createHeaderChunk(...)
75 //
76 // Purpose: [static] Static factory to create a leaf chunk with no data area or
77 // only the type in the data area
78 //
79 //-----------------------------------------------------------------------------
80
createHeaderChunk(const IEndian & endian,const XMP_Uns32 id,const XMP_Uns32 type)81 Chunk* Chunk::createHeaderChunk( const IEndian& endian, const XMP_Uns32 id, const XMP_Uns32 type /*= kType_NONE*/)
82 {
83 Chunk *chunk = new Chunk( endian );
84 chunk->setID( id );
85
86 XMP_Uns64 size = 0;
87
88 if( type != kType_NONE )
89 {
90 chunk->setType( type );
91 size += Chunk::TYPE_SIZE;
92 }
93
94 chunk->mSize = size;
95 chunk->mOriginalSize = size;
96 chunk->mChunkMode = CHUNK_LEAF;
97 chunk->mDirty = false;
98
99 return chunk;
100 }
101
102
103 //-----------------------------------------------------------------------------
104 //
105 // Chunk::Chunk(...)
106 //
107 // Purpose: ctor/dtor
108 //
109 //-----------------------------------------------------------------------------
110
Chunk(const IEndian & endian)111 Chunk::Chunk( const IEndian& endian )
112 : mEndian( endian )
113 {
114 // initialize private instance variables
115 mChunkId.id = kChunk_NONE;
116 mChunkId.type = kType_NONE;
117 mSize = 0;
118 mOriginalSize = 0;
119 mBufferSize = 0;
120 mData = NULL;
121 mParent = NULL;
122 mOriginalOffset = 0;
123 mOffset = 0;
124 mDirty = false;
125 mChunkMode = CHUNK_UNKNOWN;
126 }
127
128
~Chunk()129 Chunk::~Chunk()
130 {
131 for( ChunkIterator iter = mChildren.begin(); iter != mChildren.end(); iter++ )
132 {
133 delete *iter;
134 }
135
136 // Free allocated data buffer
137 if( mData != NULL )
138 {
139 delete [] mData;
140 }
141 }
142
143
144 /************************ IChunk interface implementation ************************/
145
146 //-----------------------------------------------------------------------------
147 //
148 // Chunk::getData(...)
149 //
150 // Purpose: access data area of Chunk
151 //
152 //-----------------------------------------------------------------------------
153
getData(const XMP_Uns8 ** data) const154 XMP_Uns64 Chunk::getData( const XMP_Uns8** data ) const
155 {
156 if( data == NULL )
157 {
158 XMP_Throw ( "Invalid data pointer.", kXMPErr_BadParam );
159 }
160
161 *data = mData;
162
163 return mBufferSize;
164 }
165
166
167 //-----------------------------------------------------------------------------
168 //
169 // Chunk::setData(...)
170 //
171 // Purpose: Set new data for the chunk.
172 // Will delete an existing internal buffer and recreate a new one
173 // and copy the given data into that new buffer.
174 //
175 //-----------------------------------------------------------------------------
176
setData(const XMP_Uns8 * const data,XMP_Uns64 size,XMP_Bool writeType)177 void Chunk::setData( const XMP_Uns8* const data, XMP_Uns64 size, XMP_Bool writeType /*=false*/ )
178 {
179 // chunk nodes cannot contain data
180 if ( mChunkMode == CHUNK_NODE )
181 {
182 XMP_Throw ( "A chunk node cannot contain data.", kXMPErr_BadParam );
183 }
184 else if ( data == NULL || size == 0 )
185 {
186 XMP_Throw ( "Invalid data pointer.", kXMPErr_BadParam );
187 }
188
189 if( mData != NULL )
190 {
191 delete [] mData;
192 }
193
194 if( writeType )
195 {
196 mBufferSize = size + TYPE_SIZE;
197 mData = new XMP_Uns8[static_cast<size_t>(mBufferSize)]; // Throws bad_alloc exception in case of being out of memory
198 setType( mChunkId.type );
199 memcpy( &mData[TYPE_SIZE], data, static_cast<size_t>(size) );
200 }
201 else
202 {
203 mBufferSize = size;
204 mData = new XMP_Uns8[static_cast<size_t>(mBufferSize)]; // Throws bad_alloc exception in case of being out of memory
205 // ! We assume that size IS the actual size of that input buffer, otherwise behavior is undefined
206 memcpy( mData, data, static_cast<size_t>(size) );
207
208 // set the type variable
209 if( mBufferSize >= TYPE_SIZE )
210 {
211 //Chunk type is always BE
212 //The first four bytes could be the type
213 mChunkId.type = BigEndian::getInstance().getUns32( mData );
214 }
215 }
216
217 mChunkMode = CHUNK_LEAF;
218 setChanged();
219 adjustSize();
220 }
221
222 //-----------------------------------------------------------------------------
223 //
224 // Chunk::getUns32(...)
225 //
226 // Purpose: The following methods are getter/setter for certain data types.
227 // They always take care of little-endian/big-endian issues.
228 // The offset starts at the data area of the Chunk.
229 //
230 //-----------------------------------------------------------------------------
231
getUns32(XMP_Uns64 offset) const232 XMP_Uns32 Chunk::getUns32( XMP_Uns64 offset ) const
233 {
234 if( offset + sizeof(XMP_Uns32) > mBufferSize )
235 {
236 XMP_Throw ( "Data access out of bounds", kXMPErr_BadIndex );
237 }
238 return mEndian.getUns32( &mData[offset] );
239 }
240
241
setUns32(XMP_Uns32 value,XMP_Uns64 offset)242 void Chunk::setUns32( XMP_Uns32 value, XMP_Uns64 offset )
243 {
244 // chunk nodes cannot contain data
245 if ( mChunkMode == CHUNK_NODE )
246 {
247 XMP_Throw ( "A chunk node cannot contain data.", kXMPErr_BadParam );
248 }
249
250 // If the new value exceeds the size of the buffer, recreate the buffer
251 adjustInternalBuffer( offset + sizeof(XMP_Uns32) );
252 // Write the new value
253 mEndian.putUns32( value, &mData[offset] );
254 // Chunk becomes leaf chunk when adding data
255 mChunkMode = CHUNK_LEAF;
256 // Flag the chunk as dirty
257 setChanged();
258 // If the buffer is bigger than the Chunk size, adjust the Chunk size
259 adjustSize();
260
261 }
262
263
getUns64(XMP_Uns64 offset) const264 XMP_Uns64 Chunk::getUns64( XMP_Uns64 offset ) const
265 {
266 if( offset + sizeof(XMP_Uns64) > mBufferSize )
267 {
268 XMP_Throw ( "Data access out of bounds", kXMPErr_BadIndex );
269 }
270 return mEndian.getUns64( &mData[offset] );
271 }
272
273
setUns64(XMP_Uns64 value,XMP_Uns64 offset)274 void Chunk::setUns64( XMP_Uns64 value, XMP_Uns64 offset )
275 {
276 // chunk nodes cannot contain data
277 if ( mChunkMode == CHUNK_NODE )
278 {
279 XMP_Throw ( "A chunk node cannot contain data.", kXMPErr_BadParam );
280 }
281
282 // If the new value exceeds the size of the buffer, recreate the buffer
283 adjustInternalBuffer( offset + sizeof(XMP_Uns64) );
284 // Write the new value
285 mEndian.putUns64( value, &mData[offset] );
286 // Chunk becomes leaf chunk when adding data
287 mChunkMode = CHUNK_LEAF;
288 // Flag the chunk as dirty
289 setChanged();
290 // If the buffer is bigger than the Chunk size, adjust the Chunk size
291 adjustSize();
292 }
293
294
getInt32(XMP_Uns64 offset) const295 XMP_Int32 Chunk::getInt32( XMP_Uns64 offset ) const
296 {
297 if( offset + sizeof(XMP_Int32) > mBufferSize )
298 {
299 XMP_Throw ( "Data access out of bounds", kXMPErr_BadIndex );
300 }
301 return mEndian.getUns32( &mData[offset] );
302 }
303
304
setInt32(XMP_Int32 value,XMP_Uns64 offset)305 void Chunk::setInt32( XMP_Int32 value, XMP_Uns64 offset )
306 {
307 // chunk nodes cannot contain data
308 if ( mChunkMode == CHUNK_NODE )
309 {
310 XMP_Throw ( "A chunk node cannot contain data.", kXMPErr_BadParam );
311 }
312
313 // If the new value exceeds the size of the buffer, recreate the buffer
314 adjustInternalBuffer( offset + sizeof(XMP_Int32) );
315 // Write the new value
316 mEndian.putUns32( value, &mData[offset] );
317 // Chunk becomes leaf chunk when adding data
318 mChunkMode = CHUNK_LEAF;
319 // Flag the chunk as dirty
320 setChanged();
321 // If the buffer is bigger than the Chunk size, adjust the Chunk size
322 adjustSize();
323 }
324
325
getInt64(XMP_Uns64 offset) const326 XMP_Int64 Chunk::getInt64( XMP_Uns64 offset ) const
327 {
328 if( offset + sizeof(XMP_Int64) > mBufferSize )
329 {
330 XMP_Throw ( "Data access out of bounds", kXMPErr_BadIndex );
331 }
332 return mEndian.getUns64( &mData[offset] );
333 }
334
335
setInt64(XMP_Int64 value,XMP_Uns64 offset)336 void Chunk::setInt64( XMP_Int64 value, XMP_Uns64 offset )
337 {
338 // chunk nodes cannot contain data
339 if ( mChunkMode == CHUNK_NODE )
340 {
341 XMP_Throw ( "A chunk node cannot contain data.", kXMPErr_BadParam );
342 }
343
344 // If the new value exceeds the size of the buffer, recreate the buffer
345 adjustInternalBuffer( offset + sizeof(XMP_Int64) );
346 // Write the new value
347 mEndian.putUns64( value, &mData[offset] );
348 // Chunk becomes leaf chunk when adding data
349 mChunkMode = CHUNK_LEAF;
350 // Flag the chunk as dirty
351 setChanged();
352 // If the buffer is bigger than the Chunk size, adjust the Chunk size
353 adjustSize();
354 }
355
356
getString(XMP_Uns64 size,XMP_Uns64 offset) const357 std::string Chunk::getString( XMP_Uns64 size /*=0*/, XMP_Uns64 offset /*=0*/ ) const
358 {
359 if( offset + size > mBufferSize )
360 {
361 XMP_Throw ( "Data access out of bounds", kXMPErr_BadIndex );
362 }
363
364 XMP_Uns64 requestedSize = size != 0 ? size : mBufferSize - offset;
365
366 std::string str((char *)&mData[offset],static_cast<size_t>(requestedSize));
367
368 return str;
369 }
370
371
setString(std::string value,XMP_Uns64 offset)372 void Chunk::setString( std::string value, XMP_Uns64 offset )
373 {
374 if ( mChunkMode == CHUNK_NODE )
375 {
376 XMP_Throw ( "A chunk node cannot contain data.", kXMPErr_BadParam );
377 }
378
379 // If the new value exceeds the size of the buffer, recreate the buffer
380 adjustInternalBuffer( offset + value.length() );
381 // Write the new value
382 memcpy( &mData[offset], value.data(), value.length() );
383 // Chunk becomes leaf chunk when adding data
384 mChunkMode = CHUNK_LEAF;
385 // Flag the chunk as dirty
386 setChanged();
387 // If the buffer is bigger than the Chunk size, adjust the Chunk size
388 adjustSize();
389 }
390
391
392 /************************ Chunk public methods ************************/
393
394 //-----------------------------------------------------------------------------
395 //
396 // Chunk::setID(...)
397 //
398 // Purpose: Sets the chunk id.
399 //
400 //-----------------------------------------------------------------------------
401
setID(XMP_Uns32 id)402 void Chunk::setID( XMP_Uns32 id )
403 {
404 mChunkId.id = id;
405 setChanged();
406 }
407
408
409 //-----------------------------------------------------------------------------
410 //
411 // Chunk::setType(...)
412 //
413 // Purpose: Sets the chunk type
414 //
415 //-----------------------------------------------------------------------------
416
setType(XMP_Uns32 type)417 void Chunk::setType( XMP_Uns32 type )
418 {
419 mChunkId.type = type;
420
421 // reserve space for type
422 // setChanged() and adjustSize() implicitly called
423 // make sure that no exception is thrown
424 ChunkMode existing = mChunkMode;
425 mChunkMode = CHUNK_UNKNOWN;
426 setUns32(0, 0);
427 mChunkMode = existing;
428
429 BigEndian::getInstance().putUns32( type, mData );
430 }
431
432 //-----------------------------------------------------------------------------
433 //
434 // Chunk::getPadSize(...)
435 //
436 // Purpose: Returns the original size of the Chunk including a pad byte if
437 // the size isn't a even number
438 //
439 //-----------------------------------------------------------------------------
440
getOriginalPadSize(bool includeHeader) const441 XMP_Uns64 Chunk::getOriginalPadSize( bool includeHeader /*= false*/ ) const
442 {
443 XMP_Uns64 ret = this->getOriginalSize( includeHeader );
444
445 if( ret & 1 )
446 {
447 ret++;
448 }
449
450 return ret;
451 }
452
453 //-----------------------------------------------------------------------------
454 //
455 // Chunk::getPadSize(...)
456 //
457 // Purpose: Returns the current size of the Chunk including a pad byte if the
458 // size isn't a even number
459 //
460 //-----------------------------------------------------------------------------
461
getPadSize(bool includeHeader) const462 XMP_Uns64 Chunk::getPadSize( bool includeHeader /*= false*/ ) const
463 {
464 XMP_Uns64 ret = this->getSize( includeHeader );
465
466 if( ret & 1 )
467 {
468 ret++;
469 }
470
471 return ret;
472 }
473
474 //-----------------------------------------------------------------------------
475 //
476 // Chunk::calculateSize(...)
477 //
478 // Purpose: Calculate the size of this chunks based on its children sizes.
479 // If this chunk has no children then no new size will be calculated.
480 //
481 //-----------------------------------------------------------------------------
482
calculateSize(bool setOriginal)483 XMP_Uns64 Chunk::calculateSize( bool setOriginal /*= false*/ )
484 {
485 XMP_Uns64 size = 0LL;
486
487 //
488 // calculate only foe nodes
489 //
490 if( this->getChunkMode() == CHUNK_NODE )
491 {
492 //
493 // calculate size of all children
494 //
495 for( ChunkIterator iter = mChildren.begin(); iter != mChildren.end(); iter++ )
496 {
497 XMP_Uns64 childSize = (*iter)->getSize(true);
498
499 size += childSize;
500
501 //
502 // take account of pad byte
503 //
504 if( childSize & 1 )
505 {
506 size++;
507 }
508 }
509
510 //
511 // assume that we have a type
512 //
513 size += Chunk::TYPE_SIZE;
514
515 //
516 // set dirty flag only if something has changed
517 //
518 if( size != mSize || ( setOriginal && size != mOriginalSize ) )
519 {
520 this->setChanged();
521 }
522
523 //
524 // set new size(s)
525 //
526 if( setOriginal )
527 {
528 mOriginalSize = size;
529 }
530
531 mSize = size;
532 }
533 else
534 size = mSize;
535
536 return size;
537 }
538
539
540 //-----------------------------------------------------------------------------
541 //
542 // Chunk::calculateWriteSize(...)
543 //
544 // Purpose: Calculate the size of the chunks that are dirty including the size
545 // of its children
546 //
547 //-----------------------------------------------------------------------------
548
calculateWriteSize() const549 XMP_Int64 Chunk::calculateWriteSize( ) const
550 {
551 XMP_Int64 size=0;
552 if (hasChanged())
553 {
554 size+=(sizeof(XMP_Uns32)*2);
555 if (mChunkMode == CHUNK_LEAF)
556 {
557 if ( mSize % 2 == 1 )
558 {
559 // for odd file sizes, a pad byte is written
560 size+=(mSize+1);
561 }
562 else
563 {
564 size+=mSize;
565 }
566 }
567 else // mChunkMode == CHUNK_NODE
568 {
569 // writes type if defined
570 if (mChunkId.type != kType_NONE)
571 {
572 size+=sizeof(XMP_Uns32);
573 }
574
575 // calls calculateWriteSize recursively on it's children
576 for( ConstChunkIterator iter = mChildren.begin(); iter != mChildren.end(); iter++ )
577 {
578 size+=(*iter)->calculateWriteSize( );
579 }
580 }
581 }
582
583 return size;
584 }
585
586 //-----------------------------------------------------------------------------
587 //
588 // Chunk::setOffset(...)
589 //
590 // Purpose: Adjust the offset that this chunk has within the file
591 //
592 //-----------------------------------------------------------------------------
593
setOffset(XMP_Uns64 newOffset)594 void Chunk::setOffset (XMP_Uns64 newOffset) // changes during rearranging
595 {
596 XMP_Uns64 oldOffset = mOffset;
597 mOffset = newOffset;
598
599 if( mOffset != oldOffset )
600 {
601 setChanged();
602 }
603 }
604
605
606 //-----------------------------------------------------------------------------
607 //
608 // Chunk::resetChanges(...)
609 //
610 // Purpose: Resets the dirty status for this chunk and its children to false
611 //
612 //-----------------------------------------------------------------------------
613
resetChanges()614 void Chunk::resetChanges()
615 {
616 mDirty = false;
617
618 for( ChunkIterator iter = mChildren.begin(); iter != mChildren.end(); iter++ )
619 {
620 (*iter)->resetChanges();
621 }
622 } //resetChanges
623
624
625 //-----------------------------------------------------------------------------
626 //
627 // Chunk::setAsNew(...)
628 //
629 // Purpose: Sets all necessary member variables to flag this chunk as a new one
630 // being inserted into the tree
631 //
632 //-----------------------------------------------------------------------------
633
setAsNew()634 void Chunk::setAsNew()
635 {
636 mOriginalSize = mSize;
637 mOriginalOffset = mOffset;
638 }
639
640 //-----------------------------------------------------------------------------
641 //
642 // Chunk::toString(...)
643 //
644 // Purpose: Creates a string representation of the chunk (debug method)
645 //
646 //-----------------------------------------------------------------------------
647
toString(std::string tabs,XMP_Bool showOriginal)648 std::string Chunk::toString( std::string tabs, XMP_Bool showOriginal )
649 {
650 const BigEndian &BE = BigEndian::getInstance();
651 char buffer[256];
652 XMP_Uns32 id = BE.getUns32(&this->mChunkId.id);
653 XMP_Uns32 type = BE.getUns32(&this->mChunkId.type);
654
655 XMP_Uns64 size, offset;
656
657 if ( showOriginal )
658 {
659 size = mEndian.getUns64(&this->mOriginalSize);
660 offset = mEndian.getUns64(&this->mOriginalOffset);
661 }
662 else
663 {
664 size = mEndian.getUns64(&this->mSize);
665 offset = mEndian.getUns64(&this->mOffset);
666 }
667
668 snprintf( buffer, 255, "%.4s -- "
669 "size: 0x%.8llX, "
670 "type: %.4s, "
671 "offset: 0x%.8llX",
672 (char*)(&id),
673 (long long unsigned)size,
674 (char*)(&type),
675 (long long unsigned)offset );
676 std::string str(buffer);
677
678 // Dump children
679 if ( mChildren.size() > 0)
680 {
681 tabs.append("\t");
682 }
683
684 for( ChunkIterator iter = mChildren.begin(); iter != mChildren.end(); iter++ )
685 {
686 str += "\n";
687 str += tabs;
688 str += (*iter)->toString(tabs , showOriginal);
689 }
690
691 return str;
692 }
693
694
695 /************************ file access ************************/
696
697 //-----------------------------------------------------------------------------
698 //
699 // Chunk::readChunk(...)
700 //
701 // Purpose: Read id, size and offset and create a chunk with mode CHUNK_UNKNOWN.
702 // The file is expected to be open and is not closed!
703 //
704 //-----------------------------------------------------------------------------
705
readChunk(XMP_IO * file)706 void Chunk::readChunk( XMP_IO* file )
707 {
708 if( file == NULL )
709 {
710 XMP_Throw( "Chunk::readChunk: Must pass a valid file pointer", kXMPErr_BadParam );
711 }
712
713 if( mChunkId.id != kChunk_NONE )
714 {
715 XMP_Throw ( "readChunk must not be called more than once", kXMPErr_InternalFailure );
716 }
717 // error handling is done in the controller
718 // determine offset in the file
719 mOriginalOffset = mOffset = file->Offset();
720 //ID is always BE
721 mChunkId.id = XIO::ReadUns32_BE( file );
722 // Size can be both
723 if (typeid(mEndian) == typeid(LittleEndian))
724 {
725 mOriginalSize = mSize = XIO::ReadUns32_LE( file );
726
727 }
728 else
729 {
730 mOriginalSize = mSize = XIO::ReadUns32_BE( file );
731 }
732
733 // For Type do not assume any format as it could be data, read it as bytes
734 if (mSize >= TYPE_SIZE)
735 {
736 mData = new XMP_Uns8[TYPE_SIZE];
737
738 for ( XMP_Uns32 i = 0; i < TYPE_SIZE ; i++ )
739 {
740 mData[i] = XIO::ReadUns8( file );
741 }
742 //Chunk type is always BE
743 //The first four bytes could be the type
744 mChunkId.type = BigEndian::getInstance().getUns32( mData );
745 }
746
747 mDirty = false;
748 }//readChunk
749
750
751 //-----------------------------------------------------------------------------
752 //
753 // Chunk::cacheChunkData(...)
754 //
755 // Purpose: Stores the data in the class (only called if required).
756 // The file is expected to be open and is not closed!
757 //
758 //-----------------------------------------------------------------------------
759
cacheChunkData(XMP_IO * file)760 void Chunk::cacheChunkData( XMP_IO* file )
761 {
762 XMP_Enforce( file != NULL );
763
764 if( mChunkMode != CHUNK_UNKNOWN )
765 {
766 XMP_Throw ( "chunk already has either data or children.", kXMPErr_BadParam );
767 }
768
769 // error handling is done in the controller
770
771 // continue only when the chunk contains data
772 if (mSize != 0)
773 {
774 mBufferSize = mSize;
775 XMP_Uns8* tmp = new XMP_Uns8[XMP_Uns32(mSize)];
776
777 // Do we have a type?
778 if (mSize >= TYPE_SIZE)
779 {
780 // add type in front of new buffer
781 for ( XMP_Uns32 i = 0; i < TYPE_SIZE ; i++ )
782 {
783 tmp[i] = mData[i];
784 }
785 // Read rest of data from file
786 if( mSize != TYPE_SIZE )
787 {
788 // Chunks that are cached are very probably not bigger than 2GB, so cast is safe
789 file->ReadAll ( &tmp[TYPE_SIZE], static_cast<XMP_Int32>(mSize - TYPE_SIZE) );
790 }
791 }
792 else
793 {
794 // Chunks that are cached are very probably not bigger than 2GB, so cast is safe
795 file->ReadAll ( tmp, static_cast<XMP_Int32>(mSize) );
796 }
797 // deletes the existing array
798 delete [] mData;
799 //assign the new buffer
800 mData = tmp;
801 }
802
803 // Remember that this method has been called
804 mDirty = false;
805 mChunkMode = CHUNK_LEAF;
806 }
807
808
809 //-----------------------------------------------------------------------------
810 //
811 // Chunk::writeChunk(...)
812 //
813 // Purpose: Write or updates chunk (new data, new size, new position).
814 // The file is expected to be open and is not closed!
815 //
816 //-----------------------------------------------------------------------------
817
writeChunk(XMP_IO * file)818 void Chunk::writeChunk( XMP_IO* file )
819 {
820 if( file == NULL )
821 {
822 XMP_Throw( "Chunk::writeChunk: Must pass a valid file pointer", kXMPErr_BadParam );
823 }
824
825 if (mChunkMode == CHUNK_UNKNOWN)
826 {
827 if (hasChanged())
828 {
829 XMP_Throw ( "A chunk with mode unknown must not be changed & written.", kXMPErr_BadParam );
830 }
831
832 // do nothing
833 }
834 else if (hasChanged())
835 {
836 // positions the file pointer
837 file->Seek ( mOffset, kXMP_SeekFromStart );
838
839
840 // ============ This part is identical for CHUNK_LEAF and CHUNK_TYPE ============
841
842 // writes ID (starting with offset)
843 XIO::WriteInt32_BE( file, mChunkId.id );
844
845 // writes size, which is always 32bit
846 // XXX for some reason this is unused -- Hub
847 /*XMP_Uns32 outSize = ( mSize >= 0x00000000FFFFFFFF ? 0xFFFFFFFF : static_cast<XMP_Uns32>( mSize & 0x00000000FFFFFFFF ) );*/
848
849 if (typeid(mEndian) == typeid(LittleEndian))
850 {
851 XIO::WriteUns32_LE( file, static_cast<XMP_Uns32>(mSize) );
852 }
853 else
854 {
855 XIO::WriteUns32_BE( file, static_cast<XMP_Uns32>(mSize) );
856 }
857
858
859 // ============ This part is different for CHUNK_LEAF and CHUNK_TYPE ============
860 if (mChunkMode == CHUNK_LEAF)
861 {
862 // writes buffer (including the optional type at the beginning)
863 // Cached chunks will very probably not be bigger than 2GB, so cast is safe
864 file->Write ( mData, static_cast<XMP_Int32>(mSize) );
865 if ( mSize % 2 == 1 )
866 {
867 // for odd file sizes, a pad byte is written
868 XIO::WriteUns8 ( file, 0 );
869 }
870 }
871 else // mChunkMode == CHUNK_NODE
872 {
873 // writes type if defined
874 if (mChunkId.type != kType_NONE)
875 {
876 XIO::WriteInt32_BE( file, mChunkId.type );
877 }
878
879 // calls writeChunk on it's children
880 for( ChunkIterator iter = mChildren.begin(); iter != mChildren.end(); iter++ )
881 {
882 (*iter)->writeChunk( file );
883 }
884 }
885 }
886
887 // set back dirty state
888 mDirty = false;
889 }
890
891
892 /************************ children access ************************/
893
894 //-----------------------------------------------------------------------------
895 //
896 // Chunk::numChildren(...)
897 //
898 // Purpose: Returns the number children chunks
899 //
900 //-----------------------------------------------------------------------------
901
numChildren() const902 XMP_Uns32 Chunk::numChildren() const
903 {
904 return static_cast<XMP_Uns32>( mChildren.size() );
905 }
906
907
908 //-----------------------------------------------------------------------------
909 //
910 // Chunk::getChildAt(...)
911 //
912 // Purpose: Returns a child node
913 //
914 //-----------------------------------------------------------------------------
915
getChildAt(XMP_Uns32 pos) const916 Chunk* Chunk::getChildAt( XMP_Uns32 pos ) const
917 {
918 try
919 {
920 return mChildren.at(pos);
921 }
922 catch( ... )
923 {
924 XMP_Throw ( "Non-existing child requested.", kXMPErr_BadIndex );
925 }
926 }
927
928
929 //-----------------------------------------------------------------------------
930 //
931 // Chunk::appendChild(...)
932 //
933 // Purpose: Appends a child node at the end of the children list
934 //
935 //-----------------------------------------------------------------------------
936
appendChild(Chunk * child,XMP_Bool adjustSizes)937 void Chunk::appendChild( Chunk* child, XMP_Bool adjustSizes )
938 {
939 if (mChunkMode == CHUNK_LEAF)
940 {
941 XMP_Throw ( "A chunk leaf cannot contain children.", kXMPErr_BadParam );
942 }
943
944 try
945 {
946 mChildren.push_back( child );
947 // make this the parent of the new node
948 child->mParent = this;
949 mChunkMode = CHUNK_NODE;
950
951 // set offset of new child
952 XMP_Uns64 childOffset = 0;
953
954 if( this->numChildren() == 1 )
955 {
956 // first added child
957 if( this->getID() != kChunk_NONE )
958 {
959 childOffset = this->getOffset() + Chunk::HEADER_SIZE + ( this->getType() == kType_NONE ? 0 : Chunk::TYPE_SIZE );
960 }
961 }
962 else
963 {
964 Chunk* predecessor = this->getChildAt( this->numChildren() - 2 );
965 childOffset = predecessor->getOffset() + predecessor->getPadSize( true );
966 }
967
968 child->setOffset( childOffset );
969
970 setChanged();
971
972 if ( adjustSizes )
973 {
974 // to fix the sizes of this node and parents
975 adjustSize( child->getSize(true) );
976 }
977 }
978 catch (...)
979 {
980 XMP_Throw ( "Vector error in appendChild", kXMPErr_InternalFailure );
981 }
982 }
983
984
985 //-----------------------------------------------------------------------------
986 //
987 // Chunk::insertChildAt(...)
988 //
989 // Purpose: Inserts a child node at a certain position
990 //
991 //-----------------------------------------------------------------------------
992
insertChildAt(XMP_Uns32 pos,Chunk * child)993 void Chunk::insertChildAt( XMP_Uns32 pos, Chunk* child )
994 {
995 if (mChunkMode == CHUNK_LEAF)
996 {
997 XMP_Throw ( "A chunk leaf cannot contain children.", kXMPErr_BadParam );
998 }
999
1000 try
1001 {
1002 if (pos <= mChildren.size())
1003 {
1004 mChildren.insert(mChildren.begin() + pos, child);
1005 // make this the parent of the new node
1006 child->mParent = this;
1007 mChunkMode = CHUNK_NODE;
1008
1009 // set offset of new child
1010 XMP_Uns64 childOffset = 0;
1011
1012 if( pos == 0 )
1013 {
1014 if( this->getID() != kChunk_NONE )
1015 {
1016 childOffset = this->getOffset() + Chunk::HEADER_SIZE + ( this->getType() == kType_NONE ? 0 : Chunk::TYPE_SIZE );
1017 }
1018 }
1019 else
1020 {
1021 Chunk* predecessor = this->getChildAt( pos-1 );
1022 childOffset = predecessor->getOffset() + predecessor->getPadSize( true );
1023 }
1024
1025 child->setOffset( childOffset );
1026
1027 setChanged();
1028
1029 // to fix the sizes of this node and parents
1030 adjustSize( child->getSize(true) );
1031 }
1032 else
1033 {
1034 XMP_Throw ( "Index not valid.", kXMPErr_BadIndex );
1035 }
1036 }
1037 catch (...)
1038 {
1039 XMP_Throw ( "Index not valid.", kXMPErr_BadIndex );
1040 }
1041 }
1042
1043
1044 //-----------------------------------------------------------------------------
1045 //
1046 // Chunk::removeChildAt(...)
1047 //
1048 // Purpose: Removes a child node at a given position
1049 //
1050 //-----------------------------------------------------------------------------
1051
removeChildAt(XMP_Uns32 pos)1052 Chunk* Chunk::removeChildAt( XMP_Uns32 pos )
1053 {
1054 Chunk* toDelete = NULL;
1055
1056 try
1057 {
1058 toDelete = mChildren.at(pos);
1059 // to fix the size of this node
1060 XMP_Int64 sizeDeleted = static_cast<XMP_Int64>(toDelete->getSize(true));
1061 mChildren.erase(mChildren.begin() + pos);
1062
1063 setChanged();
1064
1065 // to fix the sizes of this node and parents
1066 adjustSize(-sizeDeleted);
1067 }
1068 catch (...)
1069 {
1070 XMP_Throw ( "Index not valid.", kXMPErr_BadIndex );
1071 }
1072
1073 return toDelete;
1074 }
1075
1076 //-----------------------------------------------------------------------------
1077 //
1078 // replaceChildAt(...)
1079 //
1080 // Purpose: Remove child at the passed position and insert the new chunk
1081 //
1082 //-----------------------------------------------------------------------------
1083
replaceChildAt(XMP_Uns32 pos,Chunk * child)1084 Chunk* Chunk::replaceChildAt( XMP_Uns32 pos, Chunk* child )
1085 {
1086 Chunk* toDelete = NULL;
1087
1088 try
1089 {
1090 //
1091 // removed old chunk
1092 //
1093 toDelete = mChildren.at(pos);
1094 mChildren.erase(mChildren.begin() + pos);
1095
1096 //
1097 // insert new chunk
1098 //
1099 mChildren.insert(mChildren.begin() + pos, child);
1100 // make this the parent of the new node
1101 child->mParent = this;
1102 mChunkMode = CHUNK_NODE;
1103
1104 // set offset
1105 child->setOffset( toDelete->getOffset() );
1106
1107 setChanged();
1108
1109 // to fix the sizes of this node and parents
1110 adjustSize( child->getPadSize() - toDelete->getPadSize() );
1111 }
1112 catch (...)
1113 {
1114 XMP_Throw ( "Index not valid.", kXMPErr_BadIndex );
1115 }
1116
1117 return toDelete;
1118 }
1119
1120 //-----------------------------------------------------------------------------
1121 //
1122 // Chunk::firstChild(...)
1123 //
1124 // Purpose: iterators
1125 //
1126 //-----------------------------------------------------------------------------
1127
firstChild() const1128 Chunk::ConstChunkIterator Chunk::firstChild() const
1129 {
1130 return mChildren.begin();
1131 }
1132
1133
lastChild() const1134 Chunk::ConstChunkIterator Chunk::lastChild() const
1135 {
1136 return mChildren.end();
1137 }
1138
1139
1140 /******************* Private Methods ***************************/
1141
1142 //-----------------------------------------------------------------------------
1143 //
1144 // Chunk::setChanged(...)
1145 //
1146 // Purpose: Sets this node and all of its parents up to the tree root dirty
1147 //
1148 //-----------------------------------------------------------------------------
1149
setChanged()1150 void Chunk::setChanged()
1151 {
1152 mDirty = true;
1153
1154 if (mParent != NULL)
1155 {
1156 mParent->setChanged();
1157 }
1158 }
1159
1160
1161 //-----------------------------------------------------------------------------
1162 //
1163 // Chunk::adjustInternalBuffer(...)
1164 //
1165 // Purpose: Resizes the internal byte buffer to the given size if the new size
1166 // is bigger than the current one.
1167 // If the new size is smaller, the buffer is not adjusted
1168 //
1169 //-----------------------------------------------------------------------------
1170
adjustInternalBuffer(XMP_Uns64 newSize)1171 void Chunk::adjustInternalBuffer( XMP_Uns64 newSize )
1172 {
1173 // only adjust if the new size is bigger than the old one.
1174 // If it is smaller, leave the buffer alone
1175 if( newSize > mBufferSize )
1176 {
1177 XMP_Uns8 *tmp = new XMP_Uns8[static_cast<size_t>(newSize)]; // Might throw bad_alloc exception
1178
1179 // Do we have an old buffer?
1180 if( mData != NULL )
1181 {
1182 // Copy it to the new one and delete the old one
1183 memcpy( tmp, mData, static_cast<size_t>(mBufferSize) );
1184
1185 delete [] mData;
1186 }
1187 mData = tmp;
1188 mBufferSize = newSize;
1189 }
1190 }//adjustInternalBuffer
1191
1192
1193 //-----------------------------------------------------------------------------
1194 //
1195 // Chunk::adjustSize(...)
1196 //
1197 // Purpose: Adjusts the chunk size and the parents chunk sizes
1198 //
1199 //-----------------------------------------------------------------------------
1200
adjustSize(XMP_Int64 sizeChange)1201 void Chunk::adjustSize( XMP_Int64 sizeChange )
1202 {
1203 // Calculate leaf sizeChange
1204 if (mChunkMode == CHUNK_LEAF)
1205 {
1206 // Note: The leave nodes size is equal to the buffer size can have odd and even sizes.
1207 XMP_Uns64 sizeInclPad = mSize + (mSize % 2);
1208 sizeChange = mBufferSize - sizeInclPad;
1209 mSize = mBufferSize;
1210
1211 // if the difference is odd, the corrected even size has be incremented by 1
1212 sizeChange += std::abs(sizeChange % 2);
1213 }
1214 else // mChunkMode == CHUNK_NODE/CHUNK_UNKNOWN
1215 {
1216 // if the difference is odd, the corrected even size has be incremented by 1
1217 // (or decremented by 1 when < 0).
1218 sizeChange += sizeChange % 2;
1219
1220 // the chunk node gets the corrected (odd->even) size
1221 mSize += sizeChange;
1222 }
1223
1224
1225 if (mParent != NULL)
1226 {
1227 // adjusts the parents size with the corrected (odd->even) size difference of this node
1228 mParent->adjustSize(sizeChange);
1229 }
1230 }//adjustSize
1231