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