void PSIR_FileWriter::ParseFileResources ( LFA_FileRef fileRef, XMP_Uns32 length ) { bool ok; this->DeleteExistingInfo(); this->fileParsed = true; if ( length == 0 ) return; // Parse the image resource block. IOBuffer ioBuf; ioBuf.filePos = LFA_Seek ( fileRef, 0, SEEK_CUR ); XMP_Int64 psirOrigin = ioBuf.filePos; // Need this to determine the resource data offsets. XMP_Int64 fileEnd = ioBuf.filePos + length; std::string rsrcPName; while ( (ioBuf.filePos + (ioBuf.ptr - ioBuf.data)) < fileEnd ) { ok = CheckFileSpace ( fileRef, &ioBuf, 12 ); // The minimal image resource takes 12 bytes. if ( ! ok ) break; // Bad image resource. Throw instead? XMP_Int64 thisRsrcPos = ioBuf.filePos + (ioBuf.ptr - ioBuf.data); XMP_Uns32 type = GetUns32BE(ioBuf.ptr); XMP_Uns16 id = GetUns16BE(ioBuf.ptr+4); ioBuf.ptr += 6; // Advance to the resource name. XMP_Uns16 nameLen = ioBuf.ptr[0]; // ! The length for the Pascal string. XMP_Uns16 paddedLen = (nameLen + 2) & 0xFFFE; // ! Round up to an even total. Yes, +2! ok = CheckFileSpace ( fileRef, &ioBuf, paddedLen+4 ); // Get the name text and the data length. if ( ! ok ) break; // Bad image resource. Throw instead? if ( nameLen > 0 ) rsrcPName.assign ( (char*)(ioBuf.ptr), paddedLen ); // ! Include the length byte and pad. ioBuf.ptr += paddedLen; // Move to the data length. XMP_Uns32 dataLen = GetUns32BE(ioBuf.ptr); XMP_Uns32 dataTotal = ((dataLen + 1) & 0xFFFFFFFEUL); // Round up to an even total. ioBuf.ptr += 4; // Advance to the resource data. XMP_Int64 thisDataPos = ioBuf.filePos + (ioBuf.ptr - ioBuf.data); XMP_Int64 nextRsrcPos = thisDataPos + dataTotal; if ( type != k8BIM ) { XMP_Uns32 fullRsrcLen = (XMP_Uns32) (nextRsrcPos - thisRsrcPos); this->otherRsrcs.push_back ( OtherRsrcInfo ( (XMP_Uns32)thisRsrcPos, fullRsrcLen ) ); MoveToOffset ( fileRef, nextRsrcPos, &ioBuf ); continue; } InternalRsrcMap::value_type mapValue ( id, InternalRsrcInfo ( id, dataLen, kIsFileBased ) ); InternalRsrcMap::iterator newRsrc = this->imgRsrcs.insert ( this->imgRsrcs.end(), mapValue ); InternalRsrcInfo* rsrcPtr = &newRsrc->second; rsrcPtr->origOffset = (XMP_Uns32)thisDataPos; if ( nameLen > 0 ) { rsrcPtr->rsrcName = (XMP_Uns8*) malloc ( paddedLen ); if ( rsrcPtr->rsrcName == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); memcpy ( (void*)rsrcPtr->rsrcName, rsrcPName.c_str(), paddedLen ); // AUDIT: Safe, allocated enough bytes above. } if ( ! IsMetadataImgRsrc ( id ) ) { MoveToOffset ( fileRef, nextRsrcPos, &ioBuf ); continue; } rsrcPtr->dataPtr = malloc ( dataLen ); // ! Allocate after the IsMetadataImgRsrc check. if ( rsrcPtr->dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); if ( dataTotal <= kIOBufferSize ) { // The image resource data fits within the I/O buffer. ok = CheckFileSpace ( fileRef, &ioBuf, dataTotal ); if ( ! ok ) break; // Bad image resource. Throw instead? memcpy ( (void*)rsrcPtr->dataPtr, ioBuf.ptr, dataLen ); // AUDIT: Safe, malloc'ed dataLen bytes above. ioBuf.ptr += dataTotal; // ! Add the rounded length. } else { // The image resource data is bigger than the I/O buffer. LFA_Seek ( fileRef, (ioBuf.filePos + (ioBuf.ptr - ioBuf.data)), SEEK_SET ); LFA_Read ( fileRef, (void*)rsrcPtr->dataPtr, dataLen ); FillBuffer ( fileRef, nextRsrcPos, &ioBuf ); } } #if 0 { printf ( "\nPSIR_FileWriter::ParseFileResources, count = %d\n", this->imgRsrcs.size() ); InternalRsrcMap::iterator irPos = this->imgRsrcs.begin(); InternalRsrcMap::iterator irEnd = this->imgRsrcs.end(); for ( ; irPos != irEnd; ++irPos ) { InternalRsrcInfo& thisRsrc = irPos->second; printf ( " #%d, dataLen %d, origOffset %d (0x%X)%s\n", thisRsrc.id, thisRsrc.dataLen, thisRsrc.origOffset, thisRsrc.origOffset, (thisRsrc.changed ? ", changed" : "") ); } } #endif } // PSIR_FileWriter::ParseFileResources
void PSIR_FileWriter::ParseFileResources ( XMP_IO* fileRef, XMP_Uns32 length ) { // Parse the image resource block. 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! // PSIR layout: // - Uns32 type, usually '8BIM' // - Uns16 ID // - PString name // - Uns8 optional pad for even alignment // - Uns32 data size // - data // - Uns8 optional pad for even alignment static const size_t kMinPSIRSize = 12; // 4+2+1+1+4 this->DeleteExistingInfo(); this->fileParsed = true; if ( length == 0 ) return; XMP_Int64 psirOrigin = fileRef->Offset(); // Need this to determine the resource data offsets. XMP_Int64 fileEnd = psirOrigin + length; char nameBuffer [260]; // The name is a PString, at 1+255+1 including length and pad. while ( fileRef->Offset() < fileEnd ) { if ( ! XIO::CheckFileSpace ( fileRef, kMinPSIRSize ) ) break; // Bad image resource. XMP_Int64 thisRsrcPos = fileRef->Offset(); XMP_Uns32 type = XIO::ReadUns32_BE ( fileRef ); XMP_Uns16 id = XIO::ReadUns16_BE ( fileRef ); XMP_Uns8 nameLen = XIO::ReadUns8 ( fileRef ); // ! The length for the Pascal string. XMP_Uns16 paddedLen = (nameLen + 2) & 0xFFFE; // ! Round up to an even total. Yes, +2! if ( ! XIO::CheckFileSpace ( fileRef, paddedLen+4 ) ) break; // Bad image resource. nameBuffer[0] = nameLen; fileRef->ReadAll ( &nameBuffer[1], paddedLen-1 ); // Include the pad byte, present for zero nameLen. XMP_Uns32 dataLen = XIO::ReadUns32_BE ( fileRef ); XMP_Uns32 dataTotal = ((dataLen + 1) & 0xFFFFFFFEUL); // Round up to an even total. if ( ! XIO::CheckFileSpace ( fileRef, dataTotal ) ) break; // Bad image resource. XMP_Int64 thisDataPos = fileRef->Offset(); XMP_Int64 nextRsrcPos = thisDataPos + dataTotal; if ( type != k8BIM ) { XMP_Uns32 fullRsrcLen = (XMP_Uns32) (nextRsrcPos - thisRsrcPos); this->otherRsrcs.push_back ( OtherRsrcInfo ( (XMP_Uns32)thisRsrcPos, fullRsrcLen ) ); fileRef->Seek ( nextRsrcPos, kXMP_SeekFromStart ); continue; } InternalRsrcInfo newInfo ( id, dataLen, kIsFileBased ); InternalRsrcMap::iterator rsrcPos = this->imgRsrcs.find ( id ); if ( rsrcPos == this->imgRsrcs.end() ) { rsrcPos = this->imgRsrcs.insert ( rsrcPos, InternalRsrcMap::value_type ( id, newInfo ) ); } else if ( (rsrcPos->second.dataLen == 0) && (newInfo.dataLen != 0) ) { rsrcPos->second = newInfo; } else { fileRef->Seek ( nextRsrcPos, kXMP_SeekFromStart ); continue; } InternalRsrcInfo* rsrcPtr = &rsrcPos->second; rsrcPtr->origOffset = (XMP_Uns32)thisDataPos; if ( nameLen > 0 ) { rsrcPtr->rsrcName = (XMP_Uns8*) malloc ( paddedLen ); if ( rsrcPtr->rsrcName == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); memcpy ( (void*)rsrcPtr->rsrcName, nameBuffer, paddedLen ); // AUDIT: Safe, allocated enough bytes above. } if ( ! IsMetadataImgRsrc ( id ) ) { fileRef->Seek ( nextRsrcPos, kXMP_SeekFromStart ); continue; } rsrcPtr->dataPtr = malloc ( dataTotal ); // ! Allocate after the IsMetadataImgRsrc check. if ( rsrcPtr->dataPtr == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); fileRef->ReadAll ( (void*)rsrcPtr->dataPtr, dataTotal ); } #if 0 { printf ( "\nPSIR_FileWriter::ParseFileResources, count = %d\n", this->imgRsrcs.size() ); InternalRsrcMap::iterator irPos = this->imgRsrcs.begin(); InternalRsrcMap::iterator irEnd = this->imgRsrcs.end(); for ( ; irPos != irEnd; ++irPos ) { InternalRsrcInfo& thisRsrc = irPos->second; printf ( " #%d, dataLen %d, origOffset %d (0x%X)%s\n", thisRsrc.id, thisRsrc.dataLen, thisRsrc.origOffset, thisRsrc.origOffset, (thisRsrc.changed ? ", changed" : "") ); } } #endif } // PSIR_FileWriter::ParseFileResources