void FileInfo::CheckFormat ( LFA_FileRef fileRef ) { IOBuffer ioBuf; LFA_Seek ( fileRef, 0, SEEK_SET ); if ( CheckFileSpace ( fileRef, &ioBuf, SWF_SIGNATURE_LEN ) ) { if ( CheckBytes ( ioBuf.ptr, SWF_F_SIGNATURE_DATA, SWF_SIGNATURE_LEN ) ) { this->compressedFile = false; } else if ( CheckBytes ( ioBuf.ptr, SWF_C_SIGNATURE_DATA, SWF_SIGNATURE_LEN ) ) { this->compressedFile = true; } LFA_Seek ( fileRef, 4, SEEK_SET ); XMP_Uns8 buffer[4]; LFA_Read ( fileRef, buffer, 4 ); iSize = GetUns32LE ( buffer ); } LFA_Seek ( fileRef, 0, SEEK_SET ); } // FileInfo::CheckFormat
bool RewriteChunk ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long tagID, long parentID, const char * inData ) { UInt32 len; UInt64 pos; try { if ( FindChunk ( inOutRiffState, tagID, parentID, 0, NULL, &len, &pos ) ) { LFA_Seek ( inFileRef, pos, SEEK_SET ); LFA_Write ( inFileRef, inData, len ); } } catch ( ... ) { return false; } return true; }
long OpenRIFF ( LFA_FileRef inFileRef, RiffState & inOutRiffState ) { UInt64 pos = 0; long tag, subtype; UInt32 len; const XMP_Int64 fileLen = LFA_Measure ( inFileRef ); if ( fileLen < 8 ) return 0; LFA_Seek ( inFileRef, 0, SEEK_SET ); while ( ReadTag ( inFileRef, &tag, &len, &subtype, pos, fileLen ) ) { if ( tag != FOURCC_RIFF ) break; AddTag ( inOutRiffState, tag, len, pos, 0, 0, subtype ); if ( subtype != 0 ) SubRead ( inFileRef, inOutRiffState, subtype, len, pos ); } return (long) inOutRiffState.tags.size(); }
bool UpdateHeader ( LFA_FileRef fileRef ) { try { XMP_Int64 length64 = LFA_Measure ( fileRef ); if ( (length64 < 8) || (length64 > (XMP_Int64)0xFFFFFFFFULL) ) return false; XMP_Uns32 length32 = MakeUns32LE ( (XMP_Uns32)length64 ); LFA_Seek ( fileRef, 4, SEEK_SET ); LFA_Write ( fileRef, &length32, 4 ); return true; } catch ( ... ) {} return false; } // UpdateHeader
XMP_Uns32 PSIR_FileWriter::UpdateFileResources ( LFA_FileRef sourceRef, LFA_FileRef destRef, IOBuffer * ioBuf, XMP_AbortProc abortProc, void * abortArg ) { IgnoreParam(ioBuf); const XMP_Uns32 zero32 = 0; const bool checkAbort = (abortProc != 0); struct RsrcHeader { XMP_Uns32 type; XMP_Uns16 id; }; XMP_Assert ( (offsetof(RsrcHeader,type) == 0) && (offsetof(RsrcHeader,id) == 4) ); if ( this->memParsed ) XMP_Throw ( "Not file based", kXMPErr_EnforceFailure ); XMP_Int64 destLenOffset = LFA_Seek ( destRef, 0, SEEK_CUR ); XMP_Uns32 destLength = 0; LFA_Write ( destRef, &destLength, 4 ); // Write a placeholder for the new PSIR section length. #if 0 { printf ( "\nPSIR_FileWriter::UpdateFileResources, 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 // First write all of the '8BIM' resources from the map. Use the internal data if present, else // copy the data from the file. RsrcHeader outHeader; outHeader.type = MakeUns32BE ( k8BIM ); InternalRsrcMap::iterator rsrcPos = this->imgRsrcs.begin(); InternalRsrcMap::iterator rsrcEnd = this->imgRsrcs.end(); // printf ( "\nPSIR_FileWriter::UpdateFileResources - 8BIM resources\n" ); for ( ; rsrcPos != rsrcEnd; ++rsrcPos ) { InternalRsrcInfo& currRsrc = rsrcPos->second; outHeader.id = MakeUns16BE ( currRsrc.id ); LFA_Write ( destRef, &outHeader, 6 ); destLength += 6; if ( currRsrc.rsrcName == 0 ) { LFA_Write ( destRef, &zero32, 2 ); destLength += 2; } else { XMP_Assert ( currRsrc.rsrcName[0] != 0 ); XMP_Uns16 nameLen = currRsrc.rsrcName[0]; // ! Include room for +1. XMP_Uns16 paddedLen = (nameLen + 2) & 0xFFFE; // ! Round up to an even total. Yes, +2! LFA_Write ( destRef, currRsrc.rsrcName, paddedLen ); destLength += paddedLen; } XMP_Uns32 dataLen = MakeUns32BE ( currRsrc.dataLen ); LFA_Write ( destRef, &dataLen, 4 ); // printf ( " #%d, offset %d (0x%X), dataLen %d\n", currRsrc.id, destLength, destLength, currRsrc.dataLen ); if ( currRsrc.dataPtr != 0 ) { LFA_Write ( destRef, currRsrc.dataPtr, currRsrc.dataLen ); } else { LFA_Seek ( sourceRef, currRsrc.origOffset, SEEK_SET ); LFA_Copy ( sourceRef, destRef, currRsrc.dataLen ); } destLength += 4 + currRsrc.dataLen; if ( (currRsrc.dataLen & 1) != 0 ) { LFA_Write ( destRef, &zero32, 1 ); // ! Pad the data to an even length. ++destLength; } } // Now write all of the non-8BIM resources. Copy the entire resource chunk from the source file. // printf ( "\nPSIR_FileWriter::UpdateFileResources - other resources\n" ); for ( size_t i = 0; i < this->otherRsrcs.size(); ++i ) { // printf ( " offset %d (0x%X), length %d", // this->otherRsrcs[i].rsrcOffset, this->otherRsrcs[i].rsrcOffset, this->otherRsrcs[i].rsrcLength ); LFA_Seek ( sourceRef, this->otherRsrcs[i].rsrcOffset, SEEK_SET ); LFA_Copy ( sourceRef, destRef, this->otherRsrcs[i].rsrcLength ); destLength += this->otherRsrcs[i].rsrcLength; // Alignment padding is already included. } // Write the final PSIR section length, seek back to the end of the file, return the length. // printf ( "\nPSIR_FileWriter::UpdateFileResources - final length %d (0x%X)\n", destLength, destLength ); LFA_Seek ( destRef, destLenOffset, SEEK_SET ); XMP_Uns32 outLen = MakeUns32BE ( destLength ); LFA_Write ( destRef, &outLen, 4 ); LFA_Seek ( destRef, 0, SEEK_END ); // *** Not rebuilding the internal map - turns out we never want it, why pay for the I/O. // *** Should probably add an option for all of these cases, memory and file based. return destLength; } // PSIR_FileWriter::UpdateFileResources
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
// ================================================================================================= // MP3_MetaHandler::UpdateFile // =========================== void MP3_MetaHandler::UpdateFile ( bool doSafeUpdate ) { if ( doSafeUpdate ) XMP_Throw ( "UCF_MetaHandler::UpdateFile: Safe update not supported", kXMPErr_Unavailable ); LFA_FileRef file ( this->parent->fileRef ); // leave 2.3 resp. 2.4 header, since we want to let alone // and don't know enough about the encoding of unrelated frames... XMP_Assert( this->containsXMP ); tagIsDirty = false; mustShift = false; // write out native properties: // * update existing ones // * create new frames as needed // * delete frames if property is gone! // see what there is to do for us: // RECON LOOP START for (int r=0; reconProps[r].frameID != 0; r++) { std::string value; bool needDescriptor = false; bool need16LE = true; bool needEncodingByte = true; XMP_Uns32 frameID = GetUns32BE( reconProps[r].frameID ); // the would-be frame ID3v2Frame* frame = framesMap[ frameID ]; // the actual frame (if already existing) // get XMP property // * honour specific exceptions // * leave value empty() if it doesn't exist ==> frame must be delete/not created switch( frameID ) { case 0x54434D50: // TCMP if exists: part of compilation need16LE = false; if ( xmpObj.GetProperty( kXMP_NS_DM, "partOfCompilation", &value, 0 ) && ( 0 == stricmp( value.c_str(), "true" ) )) value = "1"; // set a TCMP frame of value 1 else value.erase(); // delete/prevent creation of frame break; case 0x54495432: // TIT2 -> title["x-default"] case 0x54434F50: // TCOP -> rights["x-default"] if (! xmpObj.GetLocalizedText( reconProps[r].ns, reconProps[r].prop, "", "x-default", 0, &value, 0 )) //jaja, side effect. value.erase(); // if not, erase string. break; case 0x54434F4E: // TCON -> genre { if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 )) { // nothing found? -> Erase string. (Leads to Unset below) value.erase(); break; } // genre: we need to get the number back, if possible XMP_Int16 iFound = -1; // flag as "not found" for ( int i=0; i < 127; ++i ) { if ( (value.size() == strlen(Genres[i])) && (stricmp( value.c_str(), Genres[i] ) == 0) ) //fixing stricmp buggy on PPC { iFound = i; // Found break; } } if ( iFound == -1 ) // not found known numeric genre? break; // stick with the literal value (also for v2.3, since this is common practice!) need16LE = false; // no unicode need for (##) char strGenre[64]; snprintf ( strGenre, sizeof(strGenre), "(%d)", iFound ); // AUDIT: Using sizeof(strGenre) is safe. value.assign(strGenre); } break; case 0x434F4D4D: // COMM case 0x55534C54: // USLT, both need descriptor. needDescriptor = true; if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 )) value.erase(); break; case 0x54594552: //TYER case 0x54444154: //TDAT case 0x54494D45: //TIME { if ( majorVersion <= 3 ) // TYER, TIME and TDAT depricated since v. 2.4 -> else use TDRC { XMP_DateTime dateTime; if (! xmpObj.GetProperty_Date( reconProps[r].ns, reconProps[r].prop, &dateTime, 0 )) { // nothing found? -> Erase string. (Leads to Unset below) value.erase(); break; } // TYER if ( frameID == 0x54594552 ) { XMP_Validate( dateTime.year <= 9999 && dateTime.year > 0 , "Year is out of range", kXMPErr_BadParam); // get only Year! SXMPUtils::ConvertFromInt( dateTime.year, "", &value ); break; } // TDAT else if ( frameID == 0x54444154 && dateTime.hasDate ) // date validation made by "GetProperty_Date" { std::string day, month; SXMPUtils::ConvertFromInt( dateTime.day, "", &day ); SXMPUtils::ConvertFromInt( dateTime.month, "", &month ); if ( dateTime.day < 10 ) value = "0"; value += day; if ( dateTime.month < 10 ) value += "0"; value += month; break; } // TIME else if ( frameID == 0x54494D45 && dateTime.hasTime ) // time validation made by "GetProperty_Date" ) { std::string hour, minute; SXMPUtils::ConvertFromInt( dateTime.hour, "", &hour ); SXMPUtils::ConvertFromInt( dateTime.minute, "", &minute ); if ( dateTime.hour < 10 ) value = "0"; value += hour; if ( dateTime.minute < 10 ) value += "0"; value += minute; break; } else { value.erase(); break; } } else // v.2.4 --> delete TYER,TIME or TDAT & write into TDRC { value.erase(); break; } } case 0x54445243: //TDRC (only v2.4) { // only export for id3 > v2.4 if ( majorVersion > 3 ) // (only v2.4) { if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 )) value.erase(); } break; } break; case 0x57434F50: //WCOP needEncodingByte = false; need16LE = false; if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 )) value.erase(); // if not, erase string break; case 0x5452434B: // TRCK case 0x54504F53: // TPOS need16LE = false; // no break, go on: default: if (! xmpObj.GetProperty( reconProps[r].ns, reconProps[r].prop, &value, 0 )) value.erase(); // if not, erase string break; } // [XMP exist] x [frame exist] => four cases: // 1/4) nothing before, nothing now if ( value.empty() && (frame==0)) continue; // nothing to do // all else means there will be rewrite work to do: tagIsDirty = true; // 2/4) value before, now gone: if ( value.empty() && (frame!=0)) { frame->active = false; //mark for non-use continue; } // 3/4) no old value, create new value if ( frame==0) { ID3v2Frame* newFrame=new ID3v2Frame( frameID ); newFrame->setFrameValue( value, needDescriptor, need16LE, false, needEncodingByte ); //always write as utf16-le incl. BOM framesVector.push_back( newFrame ); framesMap[ frameID ] = newFrame; continue; } // 4/4) change existing value else // resp. change frame { frame->setFrameValue( value, needDescriptor, need16LE, false, needEncodingByte ); } } // RECON LOOP END ///////////////////////////////////////////////////////////////////////////////// // (Re)Build XMP frame: ID3v2Frame* frame = framesMap[ XMP_FRAME_ID ]; if ( frame == 0) { ID3v2Frame* newFrame=new ID3v2Frame( XMP_FRAME_ID ); newFrame->setFrameValue( this->xmpPacket, false, false, true ); framesVector.push_back( newFrame ); framesMap[ XMP_FRAME_ID ] = newFrame; } else frame->setFrameValue( this->xmpPacket, false, false, true ); //////////////////////////////////////////////////////////////////////////////// // Decision making newFramesSize = 0; for ( XMP_Uns32 i=0; i < framesVector.size(); i++) { if (framesVector[i]->active) newFramesSize += (10 + framesVector[i]->contentSize ); } mustShift = ( newFramesSize > (oldTagSize - 10)) || //optimization: If more than 8K can be saved by rewriting the MP3, go do it: ((newFramesSize + 8*1024) < oldTagSize ); if (!mustShift) // fill what we got newTagSize = oldTagSize; else // if need to shift anyway, get some nice 2K padding newTagSize = newFramesSize + 2048 + 10; newPadding = newTagSize -10 - newFramesSize; // shifting needed? -> shift if ( mustShift ) { XMP_Int64 filesize = LFA_Measure( file ); if ( this->hasID3Tag ) LFA_Move( file, oldTagSize, file, newTagSize , filesize - oldTagSize ); //fix [2338569] else LFA_Move( file, 0, file, newTagSize, filesize ); // move entire file up. } // correct size stuff, write out header LFA_Rewind( file ); id3Header.write( file, newTagSize); // write out tags for ( XMP_Uns32 i=0; i < framesVector.size(); i++) { if ( framesVector[i]->active) framesVector[i]->write(file, majorVersion); } // write out padding: for ( XMP_Int64 i = newPadding; i > 0;) { const XMP_Uns64 zero = 0; if ( i >= 8 ) // worthwhile optimization { LFA_Write( file, &zero, 8 ); i -= 8; continue; } LFA_Write( file, &zero, 1 ); i--; } // check end of file for ID3v1 tag XMP_Int64 possibleTruncationPoint = LFA_Seek( file, -128, SEEK_END); bool alreadyHasID3v1 = (LFA_ReadInt32_BE( file ) & 0xFFFFFF00) == 0x54414700; // "TAG" if ( ! alreadyHasID3v1 ) // extend file LFA_Extend( file, LFA_Measure( file ) + 128 ); id3v1Tag.write( file, &this->xmpObj ); this->needsUpdate = false; //do last for safety reasons } // MP3_MetaHandler::UpdateFile
// ================================================================================================= // MP3_MetaHandler::CacheFileData // ============================== void MP3_MetaHandler::CacheFileData() { //*** abort procedures this->containsXMP = false; //assume no XMP for now LFA_FileRef file = this->parent->fileRef; XMP_PacketInfo &packetInfo = this->packetInfo; LFA_Rewind(file); hasID3Tag = id3Header.read( file ); majorVersion = id3Header.fields[ID3Header::o_version_major]; minorVersion = id3Header.fields[ID3Header::o_version_minor]; hasExtHeader = (0 != ( 0x40 & id3Header.fields[ID3Header::o_flags])); //'naturally' false if no ID3Tag hasFooter = ( 0 != ( 0x10 & id3Header.fields[ID3Header::o_flags])); //'naturally' false if no ID3Tag // stored size is w/o initial header (thus adding 10) // + but extended header (if existing) // + padding + frames after unsynchronisation (?) // (if no ID3 tag existing, constructed default correctly sets size to 10.) oldTagSize = 10 + synchToInt32(GetUns32BE( &id3Header.fields[ID3Header::o_size] )); if (hasExtHeader) { extHeaderSize = synchToInt32( LFA_ReadInt32_BE( file)); XMP_Uns8 extHeaderNumFlagBytes = LFA_ReadUns8( file ); // v2.3 doesn't include the size, while v2.4 does if ( majorVersion < 4 ) extHeaderSize += 4; XMP_Validate( extHeaderSize >= 6, "extHeader size too small", kXMPErr_BadFileFormat ); bool ok; LFA_Seek(file, extHeaderSize - 6, SEEK_CUR , &ok); XMP_Assert(ok); } else { extHeaderSize = 0; // := there is no such header. } this->framesVector.clear(); //mac precaution ID3v2Frame* curFrame = 0; // reusable //////////////////////////////////////////////////// // read frames while ( LFA_Tell(file) < oldTagSize ) { curFrame = new ID3v2Frame(); try { XMP_Int64 frameSize = curFrame->read( file, majorVersion ); if (frameSize == 0) // no more frames coming => proceed to padding { delete curFrame; // ..since not becoming part of vector for latter delete. break; // not a throw. There's nothing wrong with padding. } this->containsXMP = true; } catch( XMP_Error e) { delete curFrame; XMP_Throw( e.GetErrMsg(), e.GetID()); // rethrow } // these are both pointer assignments, no (copy) construction // (MemLeak Note: for all things pushed, memory cleanup is taken care of in destructor.) this->framesVector.push_back( curFrame ); //remember XMP-Frame, if it occurs: if ( CheckBytes( &curFrame->fields[ID3v2Frame::o_id], "PRIV", 4 )) if( curFrame->contentSize > 8 ) // to avoid adress violation on very small non-XMP PRIV frames if( CheckBytes( &curFrame->content[0], "XMP\0", 4 )) { // be sure that this is the first packet (all else would be illegal format) XMP_Validate( framesMap[ XMP_FRAME_ID] == 0, "two XMP packets in one file", kXMPErr_BadFileFormat ); //add this to map, needed on reconciliation framesMap[ XMP_FRAME_ID ] = curFrame; this->packetInfo.length = curFrame->contentSize - 4; // content minus "XMP\0" this->packetInfo.offset = ( LFA_Tell(file) - this->packetInfo.length ); this->xmpPacket.erase(); //safety this->xmpPacket.assign( &curFrame->content[4], curFrame->contentSize - 4 ); this->containsXMP = true; // do this last, after all possible failure } // No space for another frame? => assume into ID3v2.4 padding. if ( LFA_Tell(file) + 10 >= oldTagSize ) break; } //////////////////////////////////////////////////// // padding oldPadding = oldTagSize - LFA_Tell( file ); oldFramesSize = oldTagSize - 10 - oldPadding; XMP_Validate( oldPadding >= 0, "illegal oldTagSize or padding value", kXMPErr_BadFileFormat ); for ( XMP_Int64 i = oldPadding; i > 0;) { if ( i >= 8 ) // worthwhile optimization { if ( LFA_ReadInt64_BE(file) != 0 ) XMP_Throw ( "padding not nulled out.", kXMPErr_BadFileFormat ); i -= 8; continue; } if ( LFA_ReadUns8(file) != 0) XMP_Throw ( "padding(2) not nulled out.", kXMPErr_BadFileFormat ); i--; } //// read ID3v1 tag if ( ! this->containsXMP ) // all else has priority { this->containsXMP = id3v1Tag.read( file, &this->xmpObj ); } } // MP3_MetaHandler::CacheFileData
static bool ReadTag ( LFA_FileRef inFileRef, long * outTag, UInt32 * outLength, long * subtype, UInt64 & inOutPosition, UInt64 maxOffset ) { UInt32 realLength; long bytesRead; bytesRead = LFA_Read ( inFileRef, outTag, 4 ); if ( bytesRead != 4 ) return false; *outTag = GetUns32LE ( outTag ); bytesRead = LFA_Read ( inFileRef, outLength, 4 ); if ( bytesRead != 4 ) return false; *outLength = GetUns32LE ( outLength ); realLength = *outLength; realLength += (realLength & 1); // Round up to an even value. inOutPosition = GetFilePosition ( inFileRef ); // The file offset of the data portion. UInt64 maxLength = maxOffset - inOutPosition; if ( (inOutPosition > maxOffset) || ((UInt64)(*outLength) > maxLength) ) { bool ignoreLastPad = true; // Ignore cases where a final pad byte is missing. UInt64 fileLen = LFA_Measure ( inFileRef ); if ( inOutPosition > (maxOffset + 1) ) ignoreLastPad = false; if ( (UInt64)(*outLength) > (maxLength + 1) ) ignoreLastPad = false; if ( ! ignoreLastPad ) { // Workaround for bad files in the field that have a bad size in the outermost RIFF // chunk. Do a "runtime repair" of cases where the length is too long (beyond EOF). // This handles read-only usage, update usage is repaired (or not) in the handler. bool oversizeRIFF = (inOutPosition == 8) && // Is this the initial 'RIFF' chunk? (fileLen >= 8); // Is the file at least of the minimal size? if ( ! oversizeRIFF ) { XMP_Throw ( "RIFF tag exceeds maximum length", kXMPErr_BadValue ); } else { *outLength = (UInt32)(fileLen) - 8; realLength = *outLength; realLength += (realLength & 1); // Round up to an even value. } } } *subtype = 0; if ( (*outTag != FOURCC_LIST) && (*outTag != FOURCC_RIFF) ) { UInt64 tempPos = inOutPosition + realLength; if ( tempPos <= maxOffset ) { LFA_Seek ( inFileRef, tempPos, SEEK_SET ); } else if ( (tempPos == (maxOffset + 1)) && (maxOffset == (UInt64)LFA_Measure(inFileRef)) ) { LFA_Seek ( inFileRef, 0, SEEK_END ); // Hack to tolerate a missing final pad byte. } else { XMP_Throw ( "Bad RIFF offset", kXMPErr_BadValue ); } } else { bytesRead = LFA_Read ( inFileRef, subtype, 4 ); if ( bytesRead != 4 ) return false; *subtype = GetUns32LE ( subtype ); *outLength -= 4; realLength -= 4; // Special case: // Since the 'movi' chunk can contain billions of subchunks, skip over the 'movi' subchunk. // // The 'movi' subtype is added to the list as the TAG. // The subtype is returned empty so nobody will try to parse the subchunks. if ( *subtype == listtypeAVIMOVIE ) { inOutPosition = GetFilePosition ( inFileRef ); UInt64 tempPos = inOutPosition + realLength; if ( tempPos <= maxOffset ) { LFA_Seek ( inFileRef, tempPos, SEEK_SET ); } else if ( (tempPos == (maxOffset + 1)) && (maxOffset == (UInt64)LFA_Measure(inFileRef)) ) { LFA_Seek ( inFileRef, 0, SEEK_END ); // Hack to tolerate a missing final pad byte. } else { XMP_Throw ( "Bad RIFF offset", kXMPErr_BadValue ); } *outLength += 4; *outTag = *subtype; *subtype = 0; } inOutPosition = GetFilePosition ( inFileRef ); } return true; }
bool MakeChunk ( LFA_FileRef inFileRef, RiffState & inOutRiffState, long riffType, UInt32 len ) { long starttag; UInt32 taglen; UInt32 rifflen, avail; UInt64 pos; /* look for top level Premiere padding chunk */ starttag = 0; while ( FindChunk ( inOutRiffState, ckidPremierePadding, riffType, 0, &starttag, &taglen, &pos ) ) { pos -= 8; taglen += 8; long extra = taglen - len; if ( extra < 0 ) continue; RiffIterator iter = inOutRiffState.tags.begin(); iter += (starttag - 1); if ( extra == 0 ) { iter->len = 0; } else { atag pad; UInt64 padpos; /* need 8 bytes extra to be able to split it */ extra -= 8; if ( extra < 0 ) continue; try{ padpos = pos + len; LFA_Seek ( inFileRef, padpos, SEEK_SET ); pad.id = MakeUns32LE ( ckidPremierePadding ); pad.len = MakeUns32LE ( extra ); LFA_Write ( inFileRef, &pad, sizeof(pad) ); } catch ( ... ) { return false; } iter->pos = padpos + 8; iter->len = extra; } /* seek back to start of original padding chunk */ LFA_Seek ( inFileRef, pos, SEEK_SET ); return true; } /* can't take padding chunk, so append new chunk to end of file */ rifflen = inOutRiffState.rifflen + 8; avail = AVIMAXCHUNKSIZE - rifflen; LFA_Seek ( inFileRef, 0, SEEK_END ); pos = GetFilePosition ( inFileRef ); if ( (pos & 1) == 1 ) { // The file length is odd, need a pad byte. XMP_Uns8 pad = 0; LFA_Write ( inFileRef, &pad, 1 ); ++pos; } if ( avail < len ) { /* if needed, create new AVIX chunk */ ltag avix; avix.id = MakeUns32LE ( FOURCC_RIFF ); avix.len = MakeUns32LE ( 4 + len ); avix.subid = MakeUns32LE ( formtypeAVIX ); LFA_Write(inFileRef, &avix, sizeof(avix)); pos += 12; AddTag ( inOutRiffState, avix.id, len, pos, 0, 0, 0 ); } else { /* otherwise, rewrite length of last RIFF chunk in file */ pos = inOutRiffState.riffpos + 4; rifflen = inOutRiffState.rifflen + len; XMP_Uns32 fileLen = MakeUns32LE ( rifflen ); LFA_Seek ( inFileRef, pos, SEEK_SET ); LFA_Write ( inFileRef, &fileLen, 4 ); inOutRiffState.rifflen = rifflen; /* prepare to write data */ LFA_Seek ( inFileRef, 0, SEEK_END ); } return true; }
// plain convenience XMP_Int64 LFA_Rewind( LFA_FileRef file) { return LFA_Seek( file, 0 , SEEK_SET ); // _SET ! }
XMP_Int64 LFA_Tell ( LFA_FileRef file ) { return LFA_Seek( file, 0 , SEEK_CUR ); // _CUR ! }
int FileInfo::Def ( LFA_FileRef source, LFA_FileRef dest ) { int ret, flush; unsigned have; z_stream strm; unsigned char in[CHUNK]; unsigned char out[CHUNK]; // allocate deflate state strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; ret = deflateInit ( &strm, SWF_DEFAULT_COMPRESSION_LEVEL ); if ( ret != Z_OK ) return ret; // compress until end of file LFA_Seek ( source, SWF_COMPRESSION_BEGIN, SEEK_SET ); XMP_Uns64 outPos = SWF_COMPRESSION_BEGIN; try { do { strm.avail_in = LFA_Read ( source, in, CHUNK ); flush = ( (strm.avail_in < CHUNK) ? Z_FINISH : Z_NO_FLUSH ); strm.next_in = in; // run deflate() on input until output buffer not full, finish // compression if all of source has been read in do { strm.avail_out = CHUNK; strm.next_out = out; ret = deflate ( &strm, flush ); // no bad return value XMP_Assert ( ret != Z_STREAM_ERROR ); // state not clobbered have = CHUNK - strm.avail_out; LFA_Seek ( dest, outPos, SEEK_SET ); LFA_Write ( dest, out, have ); outPos += have; } while ( strm.avail_out == 0 ); XMP_Assert ( strm.avail_in == 0 ); // all input will be used // done when last data in file processed } while ( flush != Z_FINISH ); XMP_Assert ( ret == Z_STREAM_END ); // stream will be complete } catch ( ... ) { deflateEnd ( &strm ); return Z_ERRNO; } /* clean up and return */ deflateEnd ( &strm ); return Z_OK; } // FileInfo::Def
int FileInfo::Inf ( LFA_FileRef source, LFA_FileRef dest ) { int ret; unsigned have; z_stream strm; unsigned char in[CHUNK]; unsigned char out[CHUNK]; XMP_Uns64 allBytes = 0; // allocate inflate state strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = 0; strm.next_in = Z_NULL; ret = inflateInit ( &strm ); if ( ret != Z_OK ) return ret; // decompress until deflate stream ends or end of file LFA_Seek ( source, SWF_COMPRESSION_BEGIN, SEEK_SET ); XMP_Uns64 outPos = SWF_COMPRESSION_BEGIN; try { do { strm.avail_in = LFA_Read ( source, in, CHUNK ); if ( strm.avail_in == 0 ) { ret = Z_STREAM_END; break; } strm.next_in = in; // run inflate() on input until output buffer not full do { strm.avail_out = CHUNK; strm.next_out = out; ret = inflate ( &strm, Z_NO_FLUSH ); XMP_Assert ( ret != Z_STREAM_ERROR ); // state not clobbered switch ( ret ) { case Z_NEED_DICT: ret = Z_DATA_ERROR; // and fall through case Z_DATA_ERROR: case Z_MEM_ERROR: inflateEnd ( &strm ); return ret; } have = CHUNK - strm.avail_out; LFA_Seek ( dest, outPos, SEEK_SET ); LFA_Write ( dest, out, have ); outPos += have; } while ( strm.avail_out == 0 ); // done when inflate() says it's done } while ( ret != Z_STREAM_END ); } catch ( ... ) { inflateEnd ( &strm ); return Z_ERRNO; } // clean up and return inflateEnd ( &strm ); return ( (ret == Z_STREAM_END) ? Z_OK : Z_DATA_ERROR ); } // FileInfo::Inf