// =================================================================================================
// Copyright Adobe
// Copyright 2006 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 "public/include/XMP_Environment.h"	// ! XMP_Environment.h must be the first included header.
#include "public/include/XMP_Const.h"

#include "XMPFiles/source/FormatSupport/PSIR_Support.hpp"
#include "source/EndianUtils.hpp"
#include "source/XIO.hpp"

#include <string.h>

// =================================================================================================
/// \file PSIR_MemoryReader.cpp
/// \brief Implementation of the memory-based read-only form of PSIR_Manager.
// =================================================================================================

// =================================================================================================
// PSIR_MemoryReader::GetImgRsrc
// =============================

bool PSIR_MemoryReader::GetImgRsrc ( XMP_Uns16 id, ImgRsrcInfo* info ) const
{
	ImgRsrcMap::const_iterator rsrcPos = this->imgRsrcs.find ( id );
	if ( rsrcPos == this->imgRsrcs.end() ) return false;
	
	if ( info != 0 ) *info = rsrcPos->second;
	return true;
	
}	// PSIR_MemoryReader::GetImgRsrc

// =================================================================================================
// PSIR_MemoryReader::ParseMemoryResources
// =======================================

void PSIR_MemoryReader::ParseMemoryResources ( const void* data, XMP_Uns32 length, bool copyData /* = true */ )
{
	// Get rid of any existing image resources.
	
	if ( this->ownedContent ) free ( this->psirContent );
	this->ownedContent = false;
	this->psirContent  = 0;
	this->psirLength   = 0;
	this->imgRsrcs.clear();
	
	if ( length == 0 ) return;
	
	// Allocate space for the full in-memory data and copy it.
	
	if ( ! copyData ) {
		this->psirContent = (XMP_Uns8*) data;
		XMP_Assert ( ! this->ownedContent );
	} else {
		if ( length > 100*1024*1024 ) XMP_Throw ( "Outrageous length for memory-based PSIR", kXMPErr_BadPSIR );
		this->psirContent = (XMP_Uns8*) malloc(length);
		if ( this->psirContent == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory );
		memcpy ( this->psirContent, data, length );	// AUDIT: Safe, malloc'ed length bytes above.
		this->ownedContent = true;
	}

	this->psirLength = length;
	
	// Capture the info for all of the resources. We're using a map keyed by ID, so only one
	// resource of each ID is recognized. Redundant resources are not legit, but have been seen in
	// the field. In particular, one case has been seen of a duplicate IIM block with one empty.
	// In general we keep the first seen copy to be compatible with Photoshop. A later non-empty
	// copy will be taken though if the current one is empty.
	
	// ! Don't use map[id] to lookup, that creates a default entry if none exists!
	
	XMP_Uns8* psirPtr   = this->psirContent;
	XMP_Uns8* psirEnd   = psirPtr + length;
	XMP_Uns8* psirLimit = psirEnd - kMinImgRsrcSize;
	
	while ( psirPtr <= psirLimit ) {
	
		XMP_Uns32 type = GetUns32BE(psirPtr);
		XMP_Uns16 id = GetUns16BE(psirPtr+4);
		psirPtr += 6;	// Advance to the resource name.

		XMP_Uns16 nameLen = psirPtr[0];			// ! The length for the Pascal string, w/ room for "+2".
		psirPtr += ((nameLen + 2) & 0xFFFE);	// ! Round up to an even offset. Yes, +2!
		
		if ( psirPtr > psirEnd-4 ) break;	// Bad image resource. Throw instead?

		XMP_Uns32 dataLen = GetUns32BE(psirPtr);
		psirPtr += 4;	// Advance to the resource data.
		XMP_Uns32 psirOffset = (XMP_Uns32) (psirPtr - this->psirContent);
		
		if ( (dataLen > length) || (psirPtr > psirEnd-dataLen) ) break;	// Bad image resource. Throw instead?
		
		if ( type == k8BIM ) {	// For read-only usage we ignore everything other than '8BIM' resources.
			ImgRsrcInfo newInfo ( id, dataLen, psirPtr, psirOffset );
			ImgRsrcMap::iterator rsrcPos = this->imgRsrcs.find ( id );
			if ( rsrcPos == this->imgRsrcs.end() ) {
				this->imgRsrcs.insert ( rsrcPos, ImgRsrcMap::value_type ( id, newInfo ) );
			} else if ( (rsrcPos->second.dataLen == 0) && (newInfo.dataLen != 0) ) {
				rsrcPos->second = newInfo;
			}
		}
		
		psirPtr += ((dataLen + 1) & 0xFFFFFFFEUL);	// ! Round up to an even offset.
	
	}

}	// PSIR_MemoryReader::ParseMemoryResources
