text: // =================================================================================================
  // Copyright Adobe
  // Copyright 2010 Adobe
  // All Rights Reserved
  //
  // NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms
  // of the Adobe license agreement accompanying it. 
  // =================================================================================================
  
  #include <string.h>
  
  #include "public/include/XMP_Environment.h"	// ! XMP_Environment.h must be the first included header.
  #include "public/include/XMP_Const.h"
  
  #include "XMPFiles/source/FormatSupport/WAVE/INFOMetadata.h"
  #include "source/Endian.h"
  
  using namespace IFF_RIFF;
  
  typedef std::map<XMP_Uns32, std::string>::const_iterator MapIterator;
  
  static const XMP_Uns32 kSizeChunkID		= 4;
  static const XMP_Uns32 kSizeChunkSize	= 4;
  static const XMP_Uns32 kSizeChunkType	= 4;
  static const XMP_Uns32 kChunkHeaderSize	= kSizeChunkID + kSizeChunkSize;
  
  static const XMP_Uns32 kType_INFO		= 0x494E464F;
  
  //-----------------------------------------------------------------------------
  // 
  // INFOMetadata::INFOMetadata(...)
  // 
  // Purpose: ctor/dtor
  // 
  //-----------------------------------------------------------------------------
  
  INFOMetadata::INFOMetadata()
  {
  }
  
  INFOMetadata::~INFOMetadata()
  {
  }
  
  //-----------------------------------------------------------------------------
  // 
  // INFOMetadata::parse(...)
  // 
  // Purpose: @see IMetadata::parse
  // 
  //-----------------------------------------------------------------------------
  
  void INFOMetadata::parse( const XMP_Uns8* input, XMP_Uns64 size )
  {
  	if( input != NULL && size >= kSizeChunkType )
  	{
  		const BigEndian& BE		= BigEndian::getInstance();
  		const LittleEndian& LE	= LittleEndian::getInstance();
  
  		XMP_Uns32 type = BE.getUns32( &input[0] );
  
  		XMP_Validate( type == kType_INFO, "Invalid LIST:INFO data", kXMPErr_InternalFailure );
  
  		XMP_Uns64 offset = kSizeChunkType;	// pointer into the buffer
  
  		while( offset < size )
  		{
  			//
  			// continue parsing only if the remaing buffer is greater than
  			// the chunk header size
  			//
  			if( size - offset >= kChunkHeaderSize )
  			{
  				//
  				// read: chunk id
  				//		 chunk size
  				//		 chunk data
  				XMP_Uns32 id		= BE.getUns32( &input[offset] );
  				XMP_Uns32 datasize	= LE.getUns32( &input[offset+kSizeChunkID] );
  
  				if( offset + kChunkHeaderSize + datasize <= size )
  				{
  					if( datasize > 0 )
  					{
  						//
  						// don't store empty values
  						//
  						std::string value( reinterpret_cast<const char*>( &input[offset+kChunkHeaderSize] ), datasize );
  
  						//
  						// set new value
  						//
  						this->setValue<std::string>( id, value );
  					}
  
  					//
  					// update pointer
  					//
  					offset = offset + datasize + kChunkHeaderSize;
  
  					if( datasize & 1 )
  					{
  						// pad byte
  						offset++;
  					}
  				}
  				else
  				{
  					//
  					// invalid chunk, clean up and throw exception
  					//
  					this->deleteAll();
  					XMP_Throw ( "Not a valid LIST:INFO chunk", kXMPErr_BadFileFormat );
  				}
  			}
  			else
  			{
  				//
  				// invalid chunk, clean up and throw exception
  				//
  				this->deleteAll();
  				XMP_Throw ( "Not a valid LIST:INFO chunk", kXMPErr_BadFileFormat );
  			}
  		}
  
  		this->resetChanges();
  	}
  	else
  	{
  		XMP_Throw ( "Not a valid LIST:INFO chunk", kXMPErr_BadFileFormat );
  	}
  }
  
  //-----------------------------------------------------------------------------
  // 
  // INFOMetadata::serialize(...)
  // 
  // Purpose: @see IMetadata::serialize
  // 
  //-----------------------------------------------------------------------------
  
  XMP_Uns64 INFOMetadata::serialize( XMP_Uns8** outBuffer )
  {
  	XMP_Uns64 size = 0;
  
  	if( outBuffer != NULL )
  	{
  		//
  		// calculate required buffer size
  		//
  		for( ValueMap::iterator iter=mValues.begin(); iter!=mValues.end(); iter++ )
  		{
  			TValueObject<std::string>* strObj = dynamic_cast<TValueObject<std::string>*>(iter->second);
  
  			XMP_Uns32 chunkSize = kChunkHeaderSize + (XMP_Uns32)strObj->getValue().length() + 1;	// 1 byte is added for NULL termination string
  
  			if( chunkSize & 1 )
  			{
  				// take account of pad byte
  				chunkSize++;
  			}
  
  			size += chunkSize;
  		}
  		
  		size += kSizeChunkType; // add size of type "INFO"
  
  		if( size > 0 )
  		{
  			XMP_Uns8* buffer = new XMP_Uns8[static_cast<size_t>(size)];		// output buffer
  
  			memset( buffer, 0, static_cast<size_t>(size) );
  
  			const BigEndian& BE		= BigEndian::getInstance();
  			const LittleEndian& LE	= LittleEndian::getInstance();
  
  			// Put type "INFO" in front of the buffer
  			XMP_Uns32 typeInfo = BE.getUns32(&kType_INFO);
  			memcpy( buffer, &typeInfo, kSizeChunkType );
  			XMP_Uns64 offset = kSizeChunkType;						// pointer into the buffer
  
  			//
  			// for each stored value
  			//
  			for( ValueMap::iterator iter=mValues.begin(); iter!=mValues.end(); iter++ )
  			{
  				//
  				// get: chunk data
  				//		chunk id
  				//		chunk size
  				//
  				XMP_Validate( iter->second != NULL, "ERROR inserting serialize. iter->second is NULL.", kXMPErr_InternalFailure );
  				XMP_Assert(dynamic_cast<TValueObject<std::string>*>(iter->second) == static_cast<TValueObject<std::string>*>(iter->second));
  				TValueObject<std::string>* strObj = dynamic_cast<TValueObject<std::string>*>(iter->second);
  				std::string value	= strObj->getValue();
  				XMP_Uns32 id		= iter->first;
  				XMP_Uns32 lSize		= (XMP_Uns32)value.length() + 1;		// Null terminated string
  
  				if( lSize & 1 && strObj->hasChanged() )
  				{
  					//
  					// if we modified the value of this entry
  					// then fill chunk data with zero bytes
  					// rather than use a pad byte, i.e.
  					// size of each LIST:INFO entry has
  					// an odd size
  					//
  					lSize++;
  				}
  
  				//
  				// chunk id and chunk size are stored in little endian format
  				//
  				id		= BE.getUns32( &id );
  				lSize	= LE.getUns32( &lSize );
  
  				//
  				// copy values into output buffer
  				//
  				memcpy( buffer+offset, &id, kSizeChunkID );
  				memcpy( buffer+offset+kSizeChunkID, &lSize, kSizeChunkSize );
  				//size has been changed in little endian format. Change it back to bigendina
  				lSize	= LE.getUns32( &lSize );
  				memcpy( buffer+offset+kChunkHeaderSize, value.c_str(), value.length() );
  
  				//
  				// update pointer
  				//
  				offset += kChunkHeaderSize;
  				offset += lSize;
  
  				if( lSize & 1 )
  				{
  					//
  					// take account of pad byte
  					offset++;
  				}
  			}
  
  			*outBuffer = buffer;
  		}
  	}
  	else
  	{
  		XMP_Throw ( "Invalid buffer", kXMPErr_InternalFailure );
  	}
  
  	return size;
  }
  
  //-----------------------------------------------------------------------------
  // 
  // INFOMetadata::isEmptyValue(...)
  // 
  // Purpose: Is the value of the passed ValueObject and its id "empty"?
  // 
  //-----------------------------------------------------------------------------
  
  bool INFOMetadata::isEmptyValue( XMP_Uns32 id, ValueObject& valueObj )
  {
  	TValueObject<std::string>* strObj = dynamic_cast<TValueObject<std::string>*>(&valueObj);
  
  	return ( strObj == NULL || ( strObj != NULL && strObj->getValue().empty() ) );
  }
  