bool ID3v2Frame::advancePastCOMMDescriptor ( XMP_Int32& pos ) { if ( (this->contentSize - pos) <= 3 ) return false; // silent error, no room left behing language tag if ( ! CheckBytes ( &this->content[pos], "eng", 3 ) ) return false; // not an error, but leave all non-eng tags alone... pos += 3; // skip lang tag if ( pos >= this->contentSize ) return false; // silent error while ( pos < this->contentSize ) { if ( this->content[pos++] == 0x00 ) break; } if ( (pos < this->contentSize) && (this->content[pos] == 0x00) ) pos++; if ( (pos == 5) && (this->contentSize == 6) && (GetUns16BE(&this->content[4]) == 0x0031) ) { return false; } if ( pos > 4 ) { std::string descriptor = std::string ( &this->content[4], pos-1 ); if ( 0 == descriptor.substr(0,4).compare( "iTun" ) ) { // begins with engiTun ? return false; // leave alone, then } } return true; //o.k., descriptor skipped, time for the real thing. } // ID3v2Frame::advancePastCOMMDescriptor
XMP_Int64 ID3v2Frame::read ( XMP_IO* file, XMP_Uns8 majorVersion ) { XMP_Assert ( (2 <= majorVersion) && (majorVersion <= 4) ); this->release(); // ensures/allows reuse of 'curFrame' XMP_Int64 start = file->Offset(); if ( majorVersion > 2 ) { file->ReadAll ( this->fields, kV23_FrameHeaderSize ); } else { // Read the 6 byte v2.2 header into the 10 byte form. memset ( this->fields, 0, kV23_FrameHeaderSize ); // Clear all of the bytes. file->ReadAll ( &this->fields[o_id], 3 ); // Leave the low order byte as zero. file->ReadAll ( &this->fields[o_size+1], 3 ); // Read big endian UInt24. } this->id = GetUns32BE ( &this->fields[o_id] ); if ( this->id == 0 ) { file->Seek ( start, kXMP_SeekFromStart ); // Zero ID must mean nothing but padding. return 0; } this->flags = GetUns16BE ( &this->fields[o_flags] ); XMP_Validate ( (0 == (this->flags & 0xEE)), "invalid lower bits in frame flags", kXMPErr_BadFileFormat ); //*** flag handling, spec :429ff aka line 431ff (i.e. Frame should be discarded) // compression and all of that..., unsynchronisation this->contentSize = GetUns32BE ( &this->fields[o_size] ); if ( majorVersion == 4 ) this->contentSize = synchToInt32 ( this->contentSize ); XMP_Validate ( (this->contentSize >= 0), "negative frame size", kXMPErr_BadFileFormat ); XMP_Validate ( (this->contentSize < 20*1024*1024), "single frame exceeds 20MB", kXMPErr_BadFileFormat ); this->content = new char [ this->contentSize ]; file->ReadAll ( this->content, this->contentSize ); return file->Offset() - start; } // ID3v2Frame::read
bool ID3v2Frame::getFrameValue ( XMP_Uns8 majorVersion, XMP_Uns32 logicalID, std::string* utf8string ) { XMP_Assert ( (this->content != 0) && (this->contentSize >= 0) && (this->contentSize < 20*1024*1024) ); if ( this->contentSize == 0 ) { utf8string->erase(); return true; // ...it is "of interest", even if empty contents. } XMP_Int32 pos = 0; XMP_Uns8 encByte = 0; // WCOP does not have an encoding byte, for all others: use [0] as EncByte, advance pos if ( logicalID != 0x57434F50 ) { encByte = this->content[0]; pos++; } // mode specific forks, COMM or USLT bool commMode = ( (logicalID == 0x434F4D4D) || (logicalID == 0x55534C54) ); switch ( encByte ) { case 0: //ISO-8859-1, 0-terminated { if ( commMode && (! advancePastCOMMDescriptor ( pos )) ) return false; // not a frame of interest! char* localPtr = &this->content[pos]; size_t localLen = this->contentSize - pos; ReconcileUtils::Latin1ToUTF8 ( localPtr, localLen, utf8string ); break; } case 1: // Unicode, v2.4: UTF-16 (undetermined Endianess), with BOM, terminated 0x00 00 case 2: // UTF-16BE without BOM, terminated 0x00 00 { if ( commMode && (! advancePastCOMMDescriptor ( pos )) ) return false; // not a frame of interest! std::string tmp ( this->content, this->contentSize ); bool bigEndian = true; // assume for now (if no BOM follows) if ( GetUns16BE ( &this->content[pos] ) == 0xFEFF ) { pos += 2; bigEndian = true; } else if ( GetUns16BE ( &this->content[pos] ) == 0xFFFE ) { pos += 2; bigEndian = false; } FromUTF16 ( (UTF16Unit*)&this->content[pos], ((this->contentSize - pos)) / 2, utf8string, bigEndian ); break; } case 3: // UTF-8 unicode, terminated \0 { if ( commMode && (! advancePastCOMMDescriptor ( pos )) ) return false; // not a frame of interest! if ( (GetUns32BE ( &this->content[pos]) & 0xFFFFFF00 ) == 0xEFBBBF00 ) { pos += 3; // swallow any BOM, just in case } utf8string->assign ( &this->content[pos], (this->contentSize - pos) ); break; } default: XMP_Throw ( "unknown text encoding", kXMPErr_BadFileFormat ); //COULDDO assume latin-1 or utf-8 as best-effort break; } return true; } // ID3v2Frame::getFrameValue
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::ParseMemoryResources ( const void* data, XMP_Uns32 length, bool copyData /* = true */ ) { this->DeleteExistingInfo(); this->memParsed = true; if ( length == 0 ) return; // Allocate space for the full in-memory data and copy it. if ( ! copyData ) { this->memContent = (XMP_Uns8*) data; XMP_Assert ( ! this->ownedContent ); } else { if ( length > 100*1024*1024 ) XMP_Throw ( "Outrageous length for memory-based PSIR", kXMPErr_BadPSIR ); this->memContent = (XMP_Uns8*) malloc ( length ); if ( this->memContent == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); memcpy ( this->memContent, data, length ); // AUDIT: Safe, malloc'ed length bytes above. this->ownedContent = true; } this->memLength = length; // Capture the info for all of the resources. XMP_Uns8* psirPtr = this->memContent; XMP_Uns8* psirEnd = psirPtr + length; XMP_Uns8* psirLimit = psirEnd - kMinImgRsrcSize; while ( psirPtr <= psirLimit ) { XMP_Uns8* origin = psirPtr; // The beginning of this resource. XMP_Uns32 type = GetUns32BE(psirPtr); XMP_Uns16 id = GetUns16BE(psirPtr+4); psirPtr += 6; // Advance to the resource name. XMP_Uns8* namePtr = psirPtr; XMP_Uns16 nameLen = namePtr[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 dataOffset = (XMP_Uns32) ( psirPtr - this->memContent ); XMP_Uns8* nextRsrc = psirPtr + ((dataLen + 1) & 0xFFFFFFFEUL); // ! Round up to an even offset. if ( (dataLen > length) || (psirPtr > psirEnd-dataLen) ) break; // Bad image resource. Throw instead? if ( type == k8BIM ) { InternalRsrcMap::value_type mapValue ( id, InternalRsrcInfo ( id, dataLen, kIsMemoryBased ) ); InternalRsrcMap::iterator rsrcPos = this->imgRsrcs.insert ( this->imgRsrcs.end(), mapValue ); InternalRsrcInfo* rsrcPtr = &rsrcPos->second; rsrcPtr->dataPtr = psirPtr; rsrcPtr->origOffset = dataOffset; if ( nameLen != 0 ) rsrcPtr->rsrcName = namePtr; } else { XMP_Uns32 rsrcOffset = XMP_Uns32( origin - this->memContent ); XMP_Uns32 rsrcLength = XMP_Uns32( nextRsrc - origin ); // Includes trailing pad. XMP_Assert ( (rsrcLength & 1) == 0 ); this->otherRsrcs.push_back ( OtherRsrcInfo ( rsrcOffset, rsrcLength ) ); } psirPtr = nextRsrc; } } // PSIR_FileWriter::ParseMemoryResources
void PSIR_FileWriter::ParseMemoryResources ( const void* data, XMP_Uns32 length, bool copyData /* = true */ ) { this->DeleteExistingInfo(); this->memParsed = true; if ( length == 0 ) return; // Allocate space for the full in-memory data and copy it. if ( ! copyData ) { this->memContent = (XMP_Uns8*) data; XMP_Assert ( ! this->ownedContent ); } else { if ( length > 100*1024*1024 ) XMP_Throw ( "Outrageous length for memory-based PSIR", kXMPErr_BadPSIR ); this->memContent = (XMP_Uns8*) malloc ( length ); if ( this->memContent == 0 ) XMP_Throw ( "Out of memory", kXMPErr_NoMemory ); memcpy ( this->memContent, data, length ); // AUDIT: Safe, malloc'ed length bytes above. this->ownedContent = true; } this->memLength = 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->memContent; XMP_Uns8* psirEnd = psirPtr + length; XMP_Uns8* psirLimit = psirEnd - kMinImgRsrcSize; while ( psirPtr <= psirLimit ) { XMP_Uns8* origin = psirPtr; // The beginning of this resource. XMP_Uns32 type = GetUns32BE(psirPtr); XMP_Uns16 id = GetUns16BE(psirPtr+4); psirPtr += 6; // Advance to the resource name. XMP_Uns8* namePtr = psirPtr; XMP_Uns16 nameLen = namePtr[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 dataOffset = (XMP_Uns32) ( psirPtr - this->memContent ); XMP_Uns8* nextRsrc = psirPtr + ((dataLen + 1) & 0xFFFFFFFEUL); // ! Round up to an even offset. if ( (dataLen > length) || (psirPtr > psirEnd-dataLen) ) break; // Bad image resource. Throw instead? if ( type != k8BIM ) { XMP_Uns32 rsrcOffset = XMP_Uns32( origin - this->memContent ); XMP_Uns32 rsrcLength = XMP_Uns32( nextRsrc - origin ); // Includes trailing pad. XMP_Assert ( (rsrcLength & 1) == 0 ); this->otherRsrcs.push_back ( OtherRsrcInfo ( rsrcOffset, rsrcLength ) ); } else { InternalRsrcInfo newInfo ( id, dataLen, kIsMemoryBased ); newInfo.dataPtr = psirPtr; newInfo.origOffset = dataOffset; if ( nameLen != 0 ) newInfo.rsrcName = namePtr; InternalRsrcMap::iterator rsrcPos = this->imgRsrcs.find ( id ); if ( rsrcPos == this->imgRsrcs.end() ) { this->imgRsrcs.insert ( rsrcPos, InternalRsrcMap::value_type ( id, newInfo ) ); } else if ( (rsrcPos->second.dataLen == 0) && (newInfo.dataLen != 0) ) { rsrcPos->second = newInfo; } } psirPtr = nextRsrc; } } // PSIR_FileWriter::ParseMemoryResources